diff options
Diffstat (limited to 'drivers/iio/magnetometer/ak8975.c')
-rw-r--r-- | drivers/iio/magnetometer/ak8975.c | 168 |
1 files changed, 139 insertions, 29 deletions
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 48d127a45d90..dbf066129a04 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -36,6 +36,13 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/magnetometer/ak8975.h> + /* * Register definitions, as well as various shifts and masks to get at the * individual fields of the registers. @@ -370,6 +377,7 @@ struct ak8975_data { wait_queue_head_t data_ready_queue; unsigned long flags; u8 cntl_cache; + struct iio_mount_matrix orientation; struct regulator *vdd; }; @@ -633,22 +641,15 @@ static int wait_conversion_complete_interrupt(struct ak8975_data *data) return ret > 0 ? 0 : -ETIME; } -/* - * Emits the raw flux value for the x, y, or z axis. - */ -static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) +static int ak8975_start_read_axis(struct ak8975_data *data, + const struct i2c_client *client) { - struct ak8975_data *data = iio_priv(indio_dev); - struct i2c_client *client = data->client; - int ret; - - mutex_lock(&data->lock); - /* Set up the device for taking a sample. */ - ret = ak8975_set_mode(data, MODE_ONCE); + int ret = ak8975_set_mode(data, MODE_ONCE); + if (ret < 0) { dev_err(&client->dev, "Error in setting operating mode\n"); - goto exit; + return ret; } /* Wait for the conversion to complete. */ @@ -659,7 +660,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) else ret = wait_conversion_complete_polled(data); if (ret < 0) - goto exit; + return ret; /* This will be executed only for non-interrupt based waiting case */ if (ret & data->def->ctrl_masks[ST1_DRDY]) { @@ -667,32 +668,45 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) data->def->ctrl_regs[ST2]); if (ret < 0) { dev_err(&client->dev, "Error in reading ST2\n"); - goto exit; + return ret; } if (ret & (data->def->ctrl_masks[ST2_DERR] | data->def->ctrl_masks[ST2_HOFL])) { dev_err(&client->dev, "ST2 status error 0x%x\n", ret); - ret = -EINVAL; - goto exit; + return -EINVAL; } } - /* Read the flux value from the appropriate register - (the register is specified in the iio device attributes). */ - ret = i2c_smbus_read_word_data(client, data->def->data_regs[index]); - if (ret < 0) { - dev_err(&client->dev, "Read axis data fails\n"); + return 0; +} + +/* Retrieve raw flux value for one of the x, y, or z axis. */ +static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) +{ + struct ak8975_data *data = iio_priv(indio_dev); + const struct i2c_client *client = data->client; + const struct ak_def *def = data->def; + int ret; + + mutex_lock(&data->lock); + + ret = ak8975_start_read_axis(data, client); + if (ret) + goto exit; + + ret = i2c_smbus_read_word_data(client, def->data_regs[index]); + if (ret < 0) goto exit; - } mutex_unlock(&data->lock); /* Clamp to valid range. */ - *val = clamp_t(s16, ret, -data->def->range, data->def->range); + *val = clamp_t(s16, ret, -def->range, def->range); return IIO_VAL_INT; exit: mutex_unlock(&data->lock); + dev_err(&client->dev, "Error in reading axis\n"); return ret; } @@ -714,6 +728,18 @@ static int ak8975_read_raw(struct iio_dev *indio_dev, return -EINVAL; } +static const struct iio_mount_matrix * +ak8975_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + return &((struct ak8975_data *)iio_priv(indio_dev))->orientation; +} + +static const struct iio_chan_spec_ext_info ak8975_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8975_get_mount_matrix), + { }, +}; + #define AK8975_CHANNEL(axis, index) \ { \ .type = IIO_MAGN, \ @@ -722,12 +748,23 @@ static int ak8975_read_raw(struct iio_dev *indio_dev, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU \ + }, \ + .ext_info = ak8975_ext_info, \ } static const struct iio_chan_spec ak8975_channels[] = { AK8975_CHANNEL(X, 0), AK8975_CHANNEL(Y, 1), AK8975_CHANNEL(Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), }; +static const unsigned long ak8975_scan_masks[] = { 0x7, 0 }; + static const struct iio_info ak8975_info = { .read_raw = &ak8975_read_raw, .driver_module = THIS_MODULE, @@ -756,6 +793,56 @@ static const char *ak8975_match_acpi_device(struct device *dev, return dev_name(dev); } +static void ak8975_fill_buffer(struct iio_dev *indio_dev) +{ + struct ak8975_data *data = iio_priv(indio_dev); + const struct i2c_client *client = data->client; + const struct ak_def *def = data->def; + int ret; + s16 buff[8]; /* 3 x 16 bits axis values + 1 aligned 64 bits timestamp */ + + mutex_lock(&data->lock); + + ret = ak8975_start_read_axis(data, client); + if (ret) + goto unlock; + + /* + * For each axis, read the flux value from the appropriate register + * (the register is specified in the iio device attributes). + */ + ret = i2c_smbus_read_i2c_block_data_or_emulated(client, + def->data_regs[0], + 3 * sizeof(buff[0]), + (u8 *)buff); + if (ret < 0) + goto unlock; + + mutex_unlock(&data->lock); + + /* Clamp to valid range. */ + buff[0] = clamp_t(s16, le16_to_cpu(buff[0]), -def->range, def->range); + buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range); + buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range); + + iio_push_to_buffers_with_timestamp(indio_dev, buff, iio_get_time_ns()); + return; + +unlock: + mutex_unlock(&data->lock); + dev_err(&client->dev, "Error in reading axes block\n"); +} + +static irqreturn_t ak8975_handle_trigger(int irq, void *p) +{ + const struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + ak8975_fill_buffer(indio_dev); + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + static int ak8975_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -765,10 +852,12 @@ static int ak8975_probe(struct i2c_client *client, int err; const char *name = NULL; enum asahi_compass_chipset chipset; + const struct ak8975_platform_data *pdata = + dev_get_platdata(&client->dev); /* Grab and set up the supplied GPIO. */ - if (client->dev.platform_data) - eoc_gpio = *(int *)(client->dev.platform_data); + if (pdata) + eoc_gpio = pdata->eoc_gpio; else if (client->dev.of_node) eoc_gpio = of_get_gpio(client->dev.of_node, 0); else @@ -802,6 +891,15 @@ static int ak8975_probe(struct i2c_client *client, data->eoc_gpio = eoc_gpio; data->eoc_irq = 0; + if (!pdata) { + err = of_iio_read_mount_matrix(&client->dev, + "mount-matrix", + &data->orientation); + if (err) + return err; + } else + data->orientation = pdata->orientation; + /* id will be NULL when enumerated via ACPI */ if (id) { chipset = (enum asahi_compass_chipset)(id->driver_data); @@ -810,8 +908,7 @@ static int ak8975_probe(struct i2c_client *client, name = ak8975_match_acpi_device(&client->dev, &chipset); if (!name) return -ENODEV; - } - else + } else return -ENOSYS; if (chipset >= AK_MAX_TYPE) { @@ -845,15 +942,27 @@ static int ak8975_probe(struct i2c_client *client, indio_dev->channels = ak8975_channels; indio_dev->num_channels = ARRAY_SIZE(ak8975_channels); indio_dev->info = &ak8975_info; + indio_dev->available_scan_masks = ak8975_scan_masks; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = name; - err = iio_device_register(indio_dev); - if (err) + err = iio_triggered_buffer_setup(indio_dev, NULL, ak8975_handle_trigger, + NULL); + if (err) { + dev_err(&client->dev, "triggered buffer setup failed\n"); goto power_off; + } + + err = iio_device_register(indio_dev); + if (err) { + dev_err(&client->dev, "device register failed\n"); + goto cleanup_buffer; + } return 0; +cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); power_off: ak8975_power_off(client); return err; @@ -864,6 +973,7 @@ static int ak8975_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); ak8975_power_off(client); return 0; |