On 02/12/15 05:05, Matt Ranostay wrote: > Optimize device tranactions using i2c transfers versus multiple > possibly racey i2c_smbus_* function calls, and only one transaction > for distance measurement. Falls back to smbus method if i2c > functionality isn't available. > > Signed-off-by: Matt Ranostay <mranostay@xxxxxxxxx> This ended up more fiddly that I expected, but seems like you got it right to me. Applied to the togreg branch of iio.git - initially pushed out as testing for the autobuilders to play with it. Jonathan > --- > drivers/iio/proximity/pulsedlight-lidar-lite-v2.c | 95 +++++++++++++++++------ > 1 file changed, 70 insertions(+), 25 deletions(-) > > diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c > index 32f24f1..05db6ce 100644 > --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c > +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c > @@ -36,8 +36,10 @@ > #define LIDAR_REG_STATUS_INVALID BIT(3) > #define LIDAR_REG_STATUS_READY BIT(0) > > -#define LIDAR_REG_DATA_HBYTE 0x0f > -#define LIDAR_REG_DATA_LBYTE 0x10 > +#define LIDAR_REG_DATA_HBYTE 0x0f > +#define LIDAR_REG_DATA_LBYTE 0x10 > +#define LIDAR_REG_DATA_WORD_READ BIT(7) > + > #define LIDAR_REG_PWR_CONTROL 0x65 > > #define LIDAR_DRV_NAME "lidar" > @@ -46,6 +48,9 @@ struct lidar_data { > struct iio_dev *indio_dev; > struct i2c_client *client; > > + int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len); > + int i2c_enabled; > + > u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */ > }; > > @@ -64,7 +69,28 @@ static const struct iio_chan_spec lidar_channels[] = { > IIO_CHAN_SOFT_TIMESTAMP(1), > }; > > -static int lidar_read_byte(struct lidar_data *data, int reg) > +static int lidar_i2c_xfer(struct lidar_data *data, u8 reg, u8 *val, int len) > +{ > + struct i2c_client *client = data->client; > + struct i2c_msg msg[2]; > + int ret; > + > + msg[0].addr = client->addr; > + msg[0].flags = client->flags | I2C_M_STOP; > + msg[0].len = 1; > + msg[0].buf = (char *) ® > + > + msg[1].addr = client->addr; > + msg[1].flags = client->flags | I2C_M_RD; > + msg[1].len = len; > + msg[1].buf = (char *) val; > + > + ret = i2c_transfer(client->adapter, msg, 2); > + > + return (ret == 2) ? 0 : ret; > +} > + > +static int lidar_smbus_xfer(struct lidar_data *data, u8 reg, u8 *val, int len) > { > struct i2c_client *client = data->client; > int ret; > @@ -74,17 +100,35 @@ static int lidar_read_byte(struct lidar_data *data, int reg) > * so in turn i2c_smbus_read_byte_data cannot be used > */ > > - ret = i2c_smbus_write_byte(client, reg); > - if (ret < 0) { > - dev_err(&client->dev, "cannot write addr value"); > - return ret; > + while (len--) { > + ret = i2c_smbus_write_byte(client, reg++); > + if (ret < 0) { > + dev_err(&client->dev, "cannot write addr value"); > + return ret; > + } > + > + ret = i2c_smbus_read_byte(client); > + if (ret < 0) { > + dev_err(&client->dev, "cannot read data value"); > + return ret; > + } > + > + *(val++) = ret; > } > > - ret = i2c_smbus_read_byte(client); > + return 0; > +} > + > +static int lidar_read_byte(struct lidar_data *data, u8 reg) > +{ > + int ret; > + u8 val; > + > + ret = data->xfer(data, reg, &val, 1); > if (ret < 0) > - dev_err(&client->dev, "cannot read data value"); > + return ret; > > - return ret; > + return val; > } > > static inline int lidar_write_control(struct lidar_data *data, int val) > @@ -100,22 +144,14 @@ static inline int lidar_write_power(struct lidar_data *data, int val) > > static int lidar_read_measurement(struct lidar_data *data, u16 *reg) > { > - int ret; > - int val; > - > - ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE); > - if (ret < 0) > - return ret; > - val = ret << 8; > + int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE | > + (data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0), > + (u8 *) reg, 2); > > - ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE); > - if (ret < 0) > - return ret; > + if (!ret) > + *reg = be16_to_cpu(*reg); > > - val |= ret; > - *reg = val; > - > - return 0; > + return ret; > } > > static int lidar_get_measurement(struct lidar_data *data, u16 *reg) > @@ -237,6 +273,16 @@ static int lidar_probe(struct i2c_client *client, > indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > if (!indio_dev) > return -ENOMEM; > + data = iio_priv(indio_dev); > + > + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { > + data->xfer = lidar_i2c_xfer; > + data->i2c_enabled = 1; > + } else if (i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) > + data->xfer = lidar_smbus_xfer; > + else > + return -ENOTSUPP; > > indio_dev->info = &lidar_info; > indio_dev->name = LIDAR_DRV_NAME; > @@ -244,7 +290,6 @@ static int lidar_probe(struct i2c_client *client, > indio_dev->num_channels = ARRAY_SIZE(lidar_channels); > indio_dev->modes = INDIO_DIRECT_MODE; > > - data = iio_priv(indio_dev); > i2c_set_clientdata(client, indio_dev); > > data->client = client; > -- 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