First driver conversion. Side effect - now have a lot more use of spi_write_then_read. May benefit from a gather read of registers function in regmap. Signed-off-by: Jonathan Cameron <jic23@xxxxxxxxx> --- drivers/staging/iio/accel/Kconfig | 1 + drivers/staging/iio/accel/lis3l02dq.h | 9 +- drivers/staging/iio/accel/lis3l02dq_core.c | 221 ++++++++++++++++------------ drivers/staging/iio/accel/lis3l02dq_ring.c | 115 ++++----------- 4 files changed, 162 insertions(+), 184 deletions(-) diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig index 81a33b6..083680b 100644 --- a/drivers/staging/iio/accel/Kconfig +++ b/drivers/staging/iio/accel/Kconfig @@ -66,6 +66,7 @@ config LIS3L02DQ tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver" depends on SPI select IIO_TRIGGER if IIO_RING_BUFFER + select REGMAP_SPI depends on !IIO_RING_BUFFER || IIO_KFIFO_BUF || IIO_SW_RING help Say yes here to build SPI support for the ST microelectronics diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h index 18b23ac..07a2e03 100644 --- a/drivers/staging/iio/accel/lis3l02dq.h +++ b/drivers/staging/iio/accel/lis3l02dq.h @@ -146,22 +146,19 @@ Form of high byte dependent on justification set in ctrl reg */ #define LIS3L02DQ_MAX_TX 12 #define LIS3L02DQ_MAX_RX 12 +struct regmap; /** * struct lis3l02dq_state - device instance specific data * @us: actual spi_device * @trig: data ready trigger registered with iio - * @tx: transmit buffer - * @rx: receive buffer - * @buf_lock: mutex to protect tx and rx + * @regmap: register map **/ struct lis3l02dq_state { struct spi_device *us; struct iio_trigger *trig; struct mutex buf_lock; bool trigger_on; - - u8 tx[LIS3L02DQ_MAX_RX] ____cacheline_aligned; - u8 rx[LIS3L02DQ_MAX_RX] ____cacheline_aligned; + struct regmap *regmap; }; int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev, diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c index ee91cc7..55330c2 100644 --- a/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -22,6 +22,8 @@ #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/module.h> +#include <linux/regmap.h> +#include <linux/err.h> #include "../iio.h" #include "../sysfs.h" @@ -29,10 +31,6 @@ #include "lis3l02dq.h" -/* At the moment the spi framework doesn't allow global setting of cs_change. - * It's in the likely to be added comment at the top of spi.h. - * This means that use cannot be made of spi_write etc. - */ /* direct copy of the irq_default_primary_handler */ #ifndef CONFIG_IIO_RING_BUFFER static irqreturn_t lis3l02dq_noring(int irq, void *private) @@ -50,27 +48,17 @@ static irqreturn_t lis3l02dq_noring(int irq, void *private) int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev, u8 reg_address, u8 *val) { - struct lis3l02dq_state *st = iio_priv(indio_dev); - struct spi_message msg; int ret; - struct spi_transfer xfer = { - .tx_buf = st->tx, - .rx_buf = st->rx, - .bits_per_word = 8, - .len = 2, - }; - - mutex_lock(&st->buf_lock); - st->tx[0] = LIS3L02DQ_READ_REG(reg_address); - st->tx[1] = 0; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - ret = spi_sync(st->us, &msg); - *val = st->rx[1]; - mutex_unlock(&st->buf_lock); + struct lis3l02dq_state *st = iio_priv(indio_dev); + unsigned int value; - return ret; + ret = regmap_read(st->regmap, reg_address, &value); + + if (ret < 0) + return ret; + + *val = value; + return 0; } /** @@ -83,16 +71,9 @@ int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev, u8 reg_address, u8 val) { - int ret; struct lis3l02dq_state *st = iio_priv(indio_dev); - mutex_lock(&st->buf_lock); - st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address); - st->tx[1] = val; - ret = spi_write(st->us, st->tx, 2); - mutex_unlock(&st->buf_lock); - - return ret; + return regmap_write(st->regmap, reg_address, val); } /** @@ -107,31 +88,14 @@ static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev, s16 value) { int ret; - struct spi_message msg; - struct lis3l02dq_state *st = iio_priv(indio_dev); - struct spi_transfer xfers[] = { { - .tx_buf = st->tx, - .bits_per_word = 8, - .len = 2, - .cs_change = 1, - }, { - .tx_buf = st->tx + 2, - .bits_per_word = 8, - .len = 2, - }, - }; - - mutex_lock(&st->buf_lock); - st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address); - st->tx[1] = value & 0xFF; - st->tx[2] = LIS3L02DQ_WRITE_REG(lower_reg_address + 1); - st->tx[3] = (value >> 8) & 0xFF; - - spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); - ret = spi_sync(st->us, &msg); - mutex_unlock(&st->buf_lock); + ret = lis3l02dq_spi_write_reg_8(indio_dev, + lower_reg_address, + value & 0xFF); + if (ret) + return ret; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + lower_reg_address + 1, + (value >> 8) & 0xFF); return ret; } @@ -140,44 +104,23 @@ static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev, u8 lower_reg_address, int *val) { - struct lis3l02dq_state *st = iio_priv(indio_dev); - - struct spi_message msg; int ret; s16 tempval; - struct spi_transfer xfers[] = { { - .tx_buf = st->tx, - .rx_buf = st->rx, - .bits_per_word = 8, - .len = 2, - .cs_change = 1, - }, { - .tx_buf = st->tx + 2, - .rx_buf = st->rx + 2, - .bits_per_word = 8, - .len = 2, - }, - }; - - mutex_lock(&st->buf_lock); - st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address); - st->tx[1] = 0; - st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1); - st->tx[3] = 0; - - spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); - ret = spi_sync(st->us, &msg); - if (ret) { - dev_err(&st->us->dev, "problem when reading 16 bit register"); - goto error_ret; - } - tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8); + u8 value; + ret = lis3l02dq_spi_read_reg_8(indio_dev, + lower_reg_address, + &value); + if (ret < 0) + return ret; + tempval = value; + ret = lis3l02dq_spi_read_reg_8(indio_dev, + lower_reg_address + 1, + &value); + if (ret < 0) + return ret; + tempval |= ((u16)(value) << 8); *val = tempval; -error_ret: - mutex_unlock(&st->buf_lock); return ret; } @@ -661,6 +604,90 @@ static const struct iio_info lis3l02dq_info = { .attrs = &lis3l02dq_attribute_group, }; +static struct reg_default lis3l02dq_reg_defaults[] = { + { + .reg = LIS3L02DQ_REG_CTRL_1_ADDR, + .def = LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE | + LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE | + LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE, + }, { + .reg = LIS3L02DQ_REG_CTRL_2_ADDR, + .def = 0x00, + }, +}; + +static bool lis3l02dq_reg_writeable(struct device *dev, unsigned int reg) +{ + return (reg == LIS3L02DQ_REG_OFFSET_X_ADDR || + reg == LIS3L02DQ_REG_OFFSET_Y_ADDR || + reg == LIS3L02DQ_REG_OFFSET_Z_ADDR || + reg == LIS3L02DQ_REG_GAIN_X_ADDR || + reg == LIS3L02DQ_REG_GAIN_Y_ADDR || + reg == LIS3L02DQ_REG_CTRL_1_ADDR || + reg == LIS3L02DQ_REG_CTRL_2_ADDR || + reg == LIS3L02DQ_REG_STATUS_ADDR || + reg == LIS3L02DQ_REG_THS_L_ADDR || + reg == LIS3L02DQ_REG_THS_H_ADDR || + reg == LIS3L02DQ_REG_WAKE_UP_CFG_ADDR); +} + +static bool lis3l02dq_reg_readable(struct device *dev, unsigned int reg) +{ + return (reg == LIS3L02DQ_REG_OFFSET_X_ADDR || + reg == LIS3L02DQ_REG_OFFSET_Y_ADDR || + reg == LIS3L02DQ_REG_OFFSET_Z_ADDR || + reg == LIS3L02DQ_REG_GAIN_X_ADDR || + reg == LIS3L02DQ_REG_GAIN_Y_ADDR || + reg == LIS3L02DQ_REG_GAIN_Z_ADDR || + reg == LIS3L02DQ_REG_CTRL_1_ADDR || + reg == LIS3L02DQ_REG_CTRL_2_ADDR || + reg == LIS3L02DQ_REG_WAKE_UP_CFG_ADDR || + reg == LIS3L02DQ_REG_WAKE_UP_SRC_ADDR || + reg == LIS3L02DQ_REG_WAKE_UP_ACK_ADDR || + reg == LIS3L02DQ_REG_STATUS_ADDR || + reg == LIS3L02DQ_REG_OUT_X_L_ADDR || + reg == LIS3L02DQ_REG_OUT_X_H_ADDR || + reg == LIS3L02DQ_REG_OUT_Y_L_ADDR || + reg == LIS3L02DQ_REG_OUT_Y_H_ADDR || + reg == LIS3L02DQ_REG_OUT_Z_L_ADDR || + reg == LIS3L02DQ_REG_OUT_Z_H_ADDR); +} + +static bool lis3l02dq_reg_volatile(struct device *dev, unsigned int reg) +{ + return (reg == LIS3L02DQ_REG_WAKE_UP_ACK_ADDR || + reg == LIS3L02DQ_REG_OUT_X_L_ADDR || + reg == LIS3L02DQ_REG_OUT_X_H_ADDR || + reg == LIS3L02DQ_REG_OUT_Y_L_ADDR || + reg == LIS3L02DQ_REG_OUT_Y_H_ADDR || + reg == LIS3L02DQ_REG_OUT_Z_L_ADDR || + reg == LIS3L02DQ_REG_OUT_Z_H_ADDR); +} +/* Read the data registers might result in unexpected datardy signal + * so they have to be marked precious */ +static bool lis3l02dq_reg_precious(struct device *dev, unsigned int reg) +{ + return (reg == LIS3L02DQ_REG_WAKE_UP_ACK_ADDR || + reg == LIS3L02DQ_REG_OUT_X_L_ADDR || + reg == LIS3L02DQ_REG_OUT_X_H_ADDR || + reg == LIS3L02DQ_REG_OUT_Y_L_ADDR || + reg == LIS3L02DQ_REG_OUT_Y_H_ADDR || + reg == LIS3L02DQ_REG_OUT_Z_L_ADDR || + reg == LIS3L02DQ_REG_OUT_Z_H_ADDR); +} + +static struct regmap_config lis3l02dq_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_defaults = lis3l02dq_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(lis3l02dq_reg_defaults), + .writeable_reg = &lis3l02dq_reg_writeable, + .readable_reg = &lis3l02dq_reg_readable, + .precious_reg = &lis3l02dq_reg_precious, + .volatile_reg = &lis3l02dq_reg_volatile, + .max_register = 0x2F, +}; + static int __devinit lis3l02dq_probe(struct spi_device *spi) { int ret; @@ -673,11 +700,15 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi) goto error_ret; } st = iio_priv(indio_dev); + st->regmap = regmap_init_spi(spi, &lis3l02dq_regmap_config); + if (IS_ERR(st->regmap)) { + ret = PTR_ERR(st->regmap); + goto error_free_dev; + } /* this is only used tor removal purposes */ spi_set_drvdata(spi, indio_dev); st->us = spi; - mutex_init(&st->buf_lock); indio_dev->name = spi->dev.driver->name; indio_dev->dev.parent = &spi->dev; indio_dev->info = &lis3l02dq_info; @@ -688,7 +719,7 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi) ret = lis3l02dq_configure_ring(indio_dev); if (ret) - goto error_free_dev; + goto error_free_regmap; ret = iio_ring_buffer_register(indio_dev, lis3l02dq_channels, @@ -734,8 +765,10 @@ error_uninitialize_ring: iio_ring_buffer_unregister(indio_dev); error_unreg_ring_funcs: lis3l02dq_unconfigure_ring(indio_dev); +error_free_regmap: + regmap_exit(st->regmap); error_free_dev: - iio_free_device(indio_dev); + iio_free_device(indio_dev); error_ret: return ret; } @@ -787,7 +820,7 @@ static int lis3l02dq_remove(struct spi_device *spi) lis3l02dq_remove_trigger(indio_dev); iio_ring_buffer_unregister(indio_dev); lis3l02dq_unconfigure_ring(indio_dev); - + regmap_exit(st->regmap); iio_device_unregister(indio_dev); err_ret: diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index c816933..da7a434 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -4,6 +4,7 @@ #include <linux/kernel.h> #include <linux/spi/spi.h> #include <linux/slab.h> +#include <linux/regmap.h> #include "../iio.h" #include "../ring_sw.h" @@ -13,16 +14,6 @@ #include "lis3l02dq.h" /** - * combine_8_to_16() utility function to munge to u8s into u16 - **/ -static inline u16 combine_8_to_16(u8 lower, u8 upper) -{ - u16 _lower = lower; - u16 _upper = upper; - return _lower | (_upper << 8); -} - -/** * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig **/ irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private) @@ -69,13 +60,10 @@ error_free_data: return ret; } -static const u8 read_all_tx_array[] = { - LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0, - LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0, - LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0, - LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDR), 0, - LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDR), 0, - LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDR), 0, +static const u8 lis3l02dq_read_addresses[] = { + LIS3L02DQ_REG_OUT_X_L_ADDR, LIS3L02DQ_REG_OUT_X_H_ADDR, + LIS3L02DQ_REG_OUT_Y_L_ADDR, LIS3L02DQ_REG_OUT_Y_H_ADDR, + LIS3L02DQ_REG_OUT_Z_L_ADDR, LIS3L02DQ_REG_OUT_Z_H_ADDR, }; /** @@ -88,75 +76,29 @@ static int lis3l02dq_read_all(struct iio_dev *indio_dev, u8 *rx_array) { struct iio_ring_buffer *ring = indio_dev->ring; struct lis3l02dq_state *st = iio_priv(indio_dev); - struct spi_transfer *xfers; - struct spi_message msg; + int ret, i, j = 0; + unsigned int value; - xfers = kzalloc((ring->scan_count) * 2 - * sizeof(*xfers), GFP_KERNEL); - if (!xfers) - return -ENOMEM; - - mutex_lock(&st->buf_lock); - - for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) + for (i = 0; i < ARRAY_SIZE(lis3l02dq_read_addresses); i++) if (test_bit(i, ring->scan_mask)) { - /* lower byte */ - xfers[j].tx_buf = st->tx + 2*j; - st->tx[2*j] = read_all_tx_array[i*4]; - st->tx[2*j + 1] = 0; + ret = regmap_read(st->regmap, + lis3l02dq_read_addresses[i*2], + &value); + if (ret < 0) + return ret; if (rx_array) - xfers[j].rx_buf = rx_array + j*2; - xfers[j].bits_per_word = 8; - xfers[j].len = 2; - xfers[j].cs_change = 1; - j++; - - /* upper byte */ - xfers[j].tx_buf = st->tx + 2*j; - st->tx[2*j] = read_all_tx_array[i*4 + 2]; - st->tx[2*j + 1] = 0; + rx_array[j*2 + 0] = value; + ret = regmap_read(st->regmap, + lis3l02dq_read_addresses[i*2 + 1], + &value); + if (ret < 0) + return ret; if (rx_array) - xfers[j].rx_buf = rx_array + j*2; - xfers[j].bits_per_word = 8; - xfers[j].len = 2; - xfers[j].cs_change = 1; + rx_array[j*2 + 1] = value; j++; } - - /* After these are transmitted, the rx_buff should have - * values in alternate bytes - */ - spi_message_init(&msg); - for (j = 0; j < ring->scan_count * 2; j++) - spi_message_add_tail(&xfers[j], &msg); - - ret = spi_sync(st->us, &msg); - mutex_unlock(&st->buf_lock); - kfree(xfers); - - return ret; -} - -static int lis3l02dq_get_ring_element(struct iio_dev *indio_dev, - u8 *buf) -{ - int ret, i; - u8 *rx_array ; - s16 *data = (s16 *)buf; - - rx_array = kzalloc(4 * (indio_dev->ring->scan_count), GFP_KERNEL); - if (rx_array == NULL) - return -ENOMEM; - ret = lis3l02dq_read_all(indio_dev, rx_array); - if (ret < 0) - return ret; - for (i = 0; i < indio_dev->ring->scan_count; i++) - data[i] = combine_8_to_16(rx_array[i*4+1], - rx_array[i*4+3]); - kfree(rx_array); - - return i*sizeof(data[0]); + return 2*j; } static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) @@ -175,7 +117,7 @@ static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) } if (ring->scan_count) - len = lis3l02dq_get_ring_element(indio_dev, data); + len = lis3l02dq_read_all(indio_dev, data); /* Guaranteed to be aligned with 8 byte boundary */ if (ring->scan_timestamp) @@ -271,11 +213,16 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig, * by ensuring outstanding read events are cleared. */ ret = lis3l02dq_read_all(indio_dev, NULL); + if (ret < 0) + return ret; } - lis3l02dq_spi_read_reg_8(indio_dev, - LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, - &t); - return ret; + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, + &t); + if (ret < 0) + return ret; + + return 0; } /** -- 1.7.3.4 -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html