diff options
Diffstat (limited to 'drivers/iio/imu/st_lsm6ds3/st_lsm6ds3_spi.c')
| -rw-r--r-- | drivers/iio/imu/st_lsm6ds3/st_lsm6ds3_spi.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/drivers/iio/imu/st_lsm6ds3/st_lsm6ds3_spi.c b/drivers/iio/imu/st_lsm6ds3/st_lsm6ds3_spi.c new file mode 100644 index 00000000000..7212ae225f8 --- /dev/null +++ b/drivers/iio/imu/st_lsm6ds3/st_lsm6ds3_spi.c @@ -0,0 +1,180 @@ +/* + * STMicroelectronics lsm6ds3 spi driver + * + * Copyright 2014 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> + +#include "st_lsm6ds3.h" + +#define ST_SENSORS_SPI_READ 0x80 + +static int st_lsm6ds3_spi_read(struct lsm6ds3_data *cdata, + u8 reg_addr, int len, u8 *data, bool b_lock) +{ + int err; + + struct spi_transfer xfers[] = { + { + .tx_buf = cdata->tb.tx_buf, + .bits_per_word = 8, + .len = 1, + }, + { + .rx_buf = cdata->tb.rx_buf, + .bits_per_word = 8, + .len = len, + } + }; + + if (b_lock) + mutex_lock(&cdata->bank_registers_lock); + + mutex_lock(&cdata->tb.buf_lock); + cdata->tb.tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ; + + err = spi_sync_transfer(to_spi_device(cdata->dev), + xfers, ARRAY_SIZE(xfers)); + if (err) + goto acc_spi_read_error; + + memcpy(data, cdata->tb.rx_buf, len*sizeof(u8)); + mutex_unlock(&cdata->tb.buf_lock); + if (b_lock) + mutex_unlock(&cdata->bank_registers_lock); + + return len; + +acc_spi_read_error: + mutex_unlock(&cdata->tb.buf_lock); + if (b_lock) + mutex_unlock(&cdata->bank_registers_lock); + + return err; +} + +static int st_lsm6ds3_spi_write(struct lsm6ds3_data *cdata, + u8 reg_addr, int len, u8 *data, bool b_lock) +{ + int err; + + struct spi_transfer xfers = { + .tx_buf = cdata->tb.tx_buf, + .bits_per_word = 8, + .len = len + 1, + }; + + if (len >= ST_LSM6DS3_RX_MAX_LENGTH) + return -ENOMEM; + + if (b_lock) + mutex_lock(&cdata->bank_registers_lock); + + mutex_lock(&cdata->tb.buf_lock); + cdata->tb.tx_buf[0] = reg_addr; + + memcpy(&cdata->tb.tx_buf[1], data, len); + + err = spi_sync_transfer(to_spi_device(cdata->dev), &xfers, 1); + mutex_unlock(&cdata->tb.buf_lock); + if (b_lock) + mutex_unlock(&cdata->bank_registers_lock); + + return err; +} + +static const struct st_lsm6ds3_transfer_function st_lsm6ds3_tf_spi = { + .write = st_lsm6ds3_spi_write, + .read = st_lsm6ds3_spi_read, +}; + +static int st_lsm6ds3_spi_probe(struct spi_device *spi) +{ + int err; + struct lsm6ds3_data *cdata; + + cdata = kmalloc(sizeof(*cdata), GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + cdata->dev = &spi->dev; + cdata->name = spi->modalias; + spi_set_drvdata(spi, cdata); + + cdata->tf = &st_lsm6ds3_tf_spi; + + err = st_lsm6ds3_common_probe(cdata, spi->irq); + if (err < 0) + goto free_data; + + return 0; + +free_data: + kfree(cdata); + return err; +} + +static int st_lsm6ds3_spi_remove(struct spi_device *spi) +{ + struct lsm6ds3_data *cdata = spi_get_drvdata(spi); + + st_lsm6ds3_common_remove(cdata, spi->irq); + kfree(cdata); + + return 0; +} + +#ifdef CONFIG_PM +static int st_lsm6ds3_suspend(struct device *dev) +{ + struct lsm6ds3_data *cdata = spi_get_drvdata(to_spi_device(dev)); + + return st_lsm6ds3_common_suspend(cdata); +} + +static int st_lsm6ds3_resume(struct device *dev) +{ + struct lsm6ds3_data *cdata = spi_get_drvdata(to_spi_device(dev)); + + return st_lsm6ds3_common_resume(cdata); +} + +static const struct dev_pm_ops st_lsm6ds3_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(st_lsm6ds3_suspend, st_lsm6ds3_resume) +}; + +#define ST_LSM6DS3_PM_OPS (&st_lsm6ds3_pm_ops) +#else /* CONFIG_PM */ +#define ST_LSM6DS3_PM_OPS NULL +#endif /* CONFIG_PM */ + +static const struct spi_device_id st_lsm6ds3_id_table[] = { + { LSM6DS3_DEV_NAME }, + { }, +}; +MODULE_DEVICE_TABLE(spi, st_lsm6ds3_id_table); + +static struct spi_driver st_lsm6ds3_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st-lsm6ds3-spi", + .pm = ST_LSM6DS3_PM_OPS, + }, + .probe = st_lsm6ds3_spi_probe, + .remove = st_lsm6ds3_spi_remove, + .id_table = st_lsm6ds3_id_table, +}; +module_spi_driver(st_lsm6ds3_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics lsm6ds3 spi driver"); +MODULE_LICENSE("GPL v2"); |