/drivers/staging/iio/accel/kxsd9.c
C | 354 lines | 286 code | 43 blank | 25 comment | 14 complexity | 957fa180317575180c73b4609f94047b MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
1/* 2 * kxsd9.c simple support for the Kionix KXSD9 3D 3 * accelerometer. 4 * 5 * Copyright (c) 2008-2009 Jonathan Cameron <jic23@cam.ac.uk> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * The i2c interface is very similar, so shouldn't be a problem once 12 * I have a suitable wire made up. 13 * 14 * TODO: Support the motion detector 15 * Uses register address incrementing so could have a 16 * heavily optimized ring buffer access function. 17 */ 18 19#include <linux/device.h> 20#include <linux/kernel.h> 21#include <linux/spi/spi.h> 22#include <linux/sysfs.h> 23#include <linux/slab.h> 24 25#include "../iio.h" 26#include "../sysfs.h" 27#include "../adc/adc.h" 28#include "accel.h" 29 30#define KXSD9_REG_X 0x00 31#define KXSD9_REG_Y 0x02 32#define KXSD9_REG_Z 0x04 33#define KXSD9_REG_AUX 0x06 34#define KXSD9_REG_RESET 0x0a 35#define KXSD9_REG_CTRL_C 0x0c 36 37#define KXSD9_FS_8 0x00 38#define KXSD9_FS_6 0x01 39#define KXSD9_FS_4 0x02 40#define KXSD9_FS_2 0x03 41#define KXSD9_FS_MASK 0x03 42 43#define KXSD9_REG_CTRL_B 0x0d 44#define KXSD9_REG_CTRL_A 0x0e 45 46#define KXSD9_READ(a) (0x80 | (a)) 47#define KXSD9_WRITE(a) (a) 48 49#define KXSD9_SCALE_2G "0.011978" 50#define KXSD9_SCALE_4G "0.023927" 51#define KXSD9_SCALE_6G "0.035934" 52#define KXSD9_SCALE_8G "0.047853" 53 54#define KXSD9_STATE_RX_SIZE 2 55#define KXSD9_STATE_TX_SIZE 4 56/** 57 * struct kxsd9_state - device related storage 58 * @buf_lock: protect the rx and tx buffers. 59 * @us: spi device 60 * @rx: single rx buffer storage 61 * @tx: single tx buffer storage 62 **/ 63struct kxsd9_state { 64 struct mutex buf_lock; 65 struct spi_device *us; 66 u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned; 67 u8 tx[KXSD9_STATE_TX_SIZE]; 68}; 69 70/* This may want to move to mili g to allow for non integer ranges */ 71static ssize_t kxsd9_read_scale(struct device *dev, 72 struct device_attribute *attr, 73 char *buf) 74{ 75 int ret; 76 ssize_t len = 0; 77 struct iio_dev *indio_dev = dev_get_drvdata(dev); 78 struct kxsd9_state *st = iio_priv(indio_dev); 79 struct spi_transfer xfer = { 80 .bits_per_word = 8, 81 .len = 2, 82 .cs_change = 1, 83 .tx_buf = st->tx, 84 .rx_buf = st->rx, 85 }; 86 struct spi_message msg; 87 88 mutex_lock(&st->buf_lock); 89 st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C); 90 st->tx[1] = 0; 91 spi_message_init(&msg); 92 spi_message_add_tail(&xfer, &msg); 93 ret = spi_sync(st->us, &msg); 94 if (ret) 95 goto error_ret; 96 97 switch (st->rx[1] & KXSD9_FS_MASK) { 98 case KXSD9_FS_8: 99 len += sprintf(buf, "%s\n", KXSD9_SCALE_8G); 100 break; 101 case KXSD9_FS_6: 102 len += sprintf(buf, "%s\n", KXSD9_SCALE_6G); 103 break; 104 case KXSD9_FS_4: 105 len += sprintf(buf, "%s\n", KXSD9_SCALE_4G); 106 break; 107 case KXSD9_FS_2: 108 len += sprintf(buf, "%s\n", KXSD9_SCALE_2G); 109 break; 110 } 111 112error_ret: 113 mutex_unlock(&st->buf_lock); 114 115 return ret ? ret : len; 116} 117static ssize_t kxsd9_write_scale(struct device *dev, 118 struct device_attribute *attr, 119 const char *buf, 120 size_t len) 121{ 122 123 struct spi_message msg; 124 int ret; 125 struct iio_dev *indio_dev = dev_get_drvdata(dev); 126 struct kxsd9_state *st = iio_priv(indio_dev); 127 u8 val; 128 struct spi_transfer xfers[] = { 129 { 130 .bits_per_word = 8, 131 .len = 2, 132 .cs_change = 1, 133 .tx_buf = st->tx, 134 .rx_buf = st->rx, 135 }, { 136 .bits_per_word = 8, 137 .len = 2, 138 .cs_change = 1, 139 .tx_buf = st->tx, 140 }, 141 }; 142 143 if (!strncmp(buf, KXSD9_SCALE_8G, 144 strlen(buf) < strlen(KXSD9_SCALE_8G) 145 ? strlen(buf) : strlen(KXSD9_SCALE_8G))) 146 val = KXSD9_FS_8; 147 else if (!strncmp(buf, KXSD9_SCALE_6G, 148 strlen(buf) < strlen(KXSD9_SCALE_6G) 149 ? strlen(buf) : strlen(KXSD9_SCALE_6G))) 150 val = KXSD9_FS_6; 151 else if (!strncmp(buf, KXSD9_SCALE_4G, 152 strlen(buf) < strlen(KXSD9_SCALE_4G) 153 ? strlen(buf) : strlen(KXSD9_SCALE_4G))) 154 val = KXSD9_FS_4; 155 else if (!strncmp(buf, KXSD9_SCALE_2G, 156 strlen(buf) < strlen(KXSD9_SCALE_2G) 157 ? strlen(buf) : strlen(KXSD9_SCALE_2G))) 158 val = KXSD9_FS_2; 159 else 160 return -EINVAL; 161 162 mutex_lock(&st->buf_lock); 163 st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C); 164 st->tx[1] = 0; 165 spi_message_init(&msg); 166 spi_message_add_tail(&xfers[0], &msg); 167 ret = spi_sync(st->us, &msg); 168 if (ret) 169 goto error_ret; 170 st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C); 171 st->tx[1] = (st->rx[1] & ~KXSD9_FS_MASK) | val; 172 173 spi_message_init(&msg); 174 spi_message_add_tail(&xfers[1], &msg); 175 ret = spi_sync(st->us, &msg); 176error_ret: 177 mutex_unlock(&st->buf_lock); 178 return ret ? ret : len; 179} 180 181static ssize_t kxsd9_read_accel(struct device *dev, 182 struct device_attribute *attr, 183 char *buf) 184{ 185 struct spi_message msg; 186 int ret; 187 ssize_t len = 0; 188 u16 val; 189 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 190 struct iio_dev *indio_dev = dev_get_drvdata(dev); 191 struct kxsd9_state *st = iio_priv(indio_dev); 192 struct spi_transfer xfers[] = { 193 { 194 .bits_per_word = 8, 195 .len = 1, 196 .cs_change = 0, 197 .delay_usecs = 200, 198 .tx_buf = st->tx, 199 }, { 200 .bits_per_word = 8, 201 .len = 2, 202 .cs_change = 1, 203 .rx_buf = st->rx, 204 }, 205 }; 206 207 mutex_lock(&st->buf_lock); 208 st->tx[0] = KXSD9_READ(this_attr->address); 209 spi_message_init(&msg); 210 spi_message_add_tail(&xfers[0], &msg); 211 spi_message_add_tail(&xfers[1], &msg); 212 ret = spi_sync(st->us, &msg); 213 if (ret) 214 goto error_ret; 215 val = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0); 216 len = sprintf(buf, "%d\n", val); 217error_ret: 218 mutex_unlock(&st->buf_lock); 219 220 return ret ? ret : len; 221} 222 223static IIO_DEV_ATTR_ACCEL_X(kxsd9_read_accel, KXSD9_REG_X); 224static IIO_DEV_ATTR_ACCEL_Y(kxsd9_read_accel, KXSD9_REG_Y); 225static IIO_DEV_ATTR_ACCEL_Z(kxsd9_read_accel, KXSD9_REG_Z); 226static IIO_DEV_ATTR_IN_RAW(0, kxsd9_read_accel, KXSD9_REG_AUX); 227 228static IIO_DEVICE_ATTR(accel_scale, 229 S_IRUGO | S_IWUSR, 230 kxsd9_read_scale, 231 kxsd9_write_scale, 232 0); 233 234static IIO_CONST_ATTR(accel_scale_available, 235 KXSD9_SCALE_2G " " 236 KXSD9_SCALE_4G " " 237 KXSD9_SCALE_6G " " 238 KXSD9_SCALE_8G); 239 240static struct attribute *kxsd9_attributes[] = { 241 &iio_dev_attr_accel_x_raw.dev_attr.attr, 242 &iio_dev_attr_accel_y_raw.dev_attr.attr, 243 &iio_dev_attr_accel_z_raw.dev_attr.attr, 244 &iio_dev_attr_in0_raw.dev_attr.attr, 245 &iio_dev_attr_accel_scale.dev_attr.attr, 246 &iio_const_attr_accel_scale_available.dev_attr.attr, 247 NULL, 248}; 249 250static const struct attribute_group kxsd9_attribute_group = { 251 .attrs = kxsd9_attributes, 252}; 253 254static int __devinit kxsd9_power_up(struct kxsd9_state *st) 255{ 256 struct spi_transfer xfers[2] = { 257 { 258 .bits_per_word = 8, 259 .len = 2, 260 .cs_change = 1, 261 .tx_buf = st->tx, 262 }, { 263 .bits_per_word = 8, 264 .len = 2, 265 .cs_change = 1, 266 .tx_buf = st->tx + 2, 267 }, 268 }; 269 struct spi_message msg; 270 st->tx[0] = 0x0d; 271 st->tx[1] = 0x40; 272 st->tx[2] = 0x0c; 273 st->tx[3] = 0x9b; 274 275 spi_message_init(&msg); 276 spi_message_add_tail(&xfers[0], &msg); 277 spi_message_add_tail(&xfers[1], &msg); 278 279 return spi_sync(st->us, &msg); 280}; 281 282static const struct iio_info kxsd9_info = { 283 .attrs = &kxsd9_attribute_group, 284 .driver_module = THIS_MODULE, 285}; 286 287static int __devinit kxsd9_probe(struct spi_device *spi) 288{ 289 struct iio_dev *indio_dev; 290 struct kxsd9_state *st; 291 int ret = 0; 292 293 indio_dev = iio_allocate_device(sizeof(*st)); 294 if (indio_dev == NULL) { 295 ret = -ENOMEM; 296 goto error_ret; 297 } 298 st = iio_priv(indio_dev); 299 spi_set_drvdata(spi, indio_dev); 300 301 st->us = spi; 302 mutex_init(&st->buf_lock); 303 304 indio_dev->dev.parent = &spi->dev; 305 indio_dev->info = &kxsd9_info; 306 indio_dev->modes = INDIO_DIRECT_MODE; 307 308 ret = iio_device_register(indio_dev); 309 if (ret) 310 goto error_free_dev; 311 312 spi->mode = SPI_MODE_0; 313 spi_setup(spi); 314 kxsd9_power_up(st); 315 316 return 0; 317 318error_free_dev: 319 iio_free_device(indio_dev); 320error_ret: 321 return ret; 322} 323 324static int __devexit kxsd9_remove(struct spi_device *spi) 325{ 326 iio_device_unregister(spi_get_drvdata(spi)); 327 328 return 0; 329} 330 331static struct spi_driver kxsd9_driver = { 332 .driver = { 333 .name = "kxsd9", 334 .owner = THIS_MODULE, 335 }, 336 .probe = kxsd9_probe, 337 .remove = __devexit_p(kxsd9_remove), 338}; 339 340static __init int kxsd9_spi_init(void) 341{ 342 return spi_register_driver(&kxsd9_driver); 343} 344module_init(kxsd9_spi_init); 345 346static __exit void kxsd9_spi_exit(void) 347{ 348 spi_unregister_driver(&kxsd9_driver); 349} 350module_exit(kxsd9_spi_exit); 351 352MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); 353MODULE_DESCRIPTION("Kionix KXSD9 SPI driver"); 354MODULE_LICENSE("GPL v2");