diff options
Diffstat (limited to 'drivers')
55 files changed, 5187 insertions, 1035 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 9b9be8725e9d..9f67c10291bd 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -43,6 +43,9 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. + To compile this driver as a module, choose M here: the + module will be called hid-sensor-accel-3d. + config IIO_ST_ACCEL_3AXIS tristate "STMicroelectronics accelerometers 3-Axis Driver" depends on (I2C || SPI_MASTER) && SYSFS @@ -80,6 +83,9 @@ config KXSD9 Say yes here to build support for the Kionix KXSD9 accelerometer. Currently this only supports the device via an SPI interface. + To compile this driver as a module, choose M here: the module + will be called kxsd9. + config MMA8452 tristate "Freescale MMA8452Q Accelerometer Driver" depends on I2C @@ -105,4 +111,14 @@ config KXCJK1013 To compile this driver as a module, choose M here: the module will be called kxcjk-1013. +config MMA9551 + tristate "Freescale MMA9551L Intelligent Motion-Sensing Platform Driver" + depends on I2C + help + Say yes here to build support for the Freescale MMA9551L + Intelligent Motion-Sensing Platform Driver. + + To compile this driver as a module, choose M here: the module + will be called mma9551. + endmenu diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index a593996c6539..de5b9cb9670f 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o obj-$(CONFIG_MMA8452) += mma8452.o +obj-$(CONFIG_MMA9551) += mma9551.o obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o st_accel-y := st_accel_core.o diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index da2fe93739a2..567de269cc00 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -108,6 +108,7 @@ struct kxcjk1013_data { bool motion_trigger_on; int64_t timestamp; enum kx_chipset chipset; + bool is_smo8500_device; }; enum kxcjk1013_axis { @@ -377,6 +378,7 @@ static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data) static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) { +#ifdef CONFIG_PM int ret; if (on) @@ -388,8 +390,11 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: kxcjk1013_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(&data->client->dev); return ret; } +#endif return 0; } @@ -858,6 +863,8 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev, ret = kxcjk1013_setup_any_motion_interrupt(data, state); if (ret < 0) { + kxcjk1013_set_power_state(data, false); + data->ev_enable_state = 0; mutex_unlock(&data->mutex); return ret; } @@ -1008,6 +1015,7 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig, else ret = kxcjk1013_setup_new_data_interrupt(data, state); if (ret < 0) { + kxcjk1013_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -1131,12 +1139,16 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private) } static const char *kxcjk1013_match_acpi_device(struct device *dev, - enum kx_chipset *chipset) + enum kx_chipset *chipset, + bool *is_smo8500_device) { const struct acpi_device_id *id; + id = acpi_match_device(dev->driver->acpi_match_table, dev); if (!id) return NULL; + if (strcmp(id->id, "SMO8500") == 0) + *is_smo8500_device = true; *chipset = (enum kx_chipset)id->driver_data; return dev_name(dev); @@ -1151,6 +1163,8 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client, if (!client) return -EINVAL; + if (data->is_smo8500_device) + return -ENOTSUPP; dev = &client->dev; @@ -1200,7 +1214,8 @@ static int kxcjk1013_probe(struct i2c_client *client, name = id->name; } else if (ACPI_HANDLE(&client->dev)) { name = kxcjk1013_match_acpi_device(&client->dev, - &data->chipset); + &data->chipset, + &data->is_smo8500_device); } else return -ENODEV; @@ -1228,21 +1243,25 @@ static int kxcjk1013_probe(struct i2c_client *client, KXCJK1013_IRQ_NAME, indio_dev); if (ret) - return ret; + goto err_poweroff; data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, indio_dev->id); - if (!data->dready_trig) - return -ENOMEM; + if (!data->dready_trig) { + ret = -ENOMEM; + goto err_poweroff; + } data->motion_trig = devm_iio_trigger_alloc(&client->dev, "%s-any-motion-dev%d", indio_dev->name, indio_dev->id); - if (!data->motion_trig) - return -ENOMEM; + if (!data->motion_trig) { + ret = -ENOMEM; + goto err_poweroff; + } data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &kxcjk1013_trigger_ops; @@ -1251,7 +1270,7 @@ static int kxcjk1013_probe(struct i2c_client *client, iio_trigger_get(indio_dev->trig); ret = iio_trigger_register(data->dready_trig); if (ret) - return ret; + goto err_poweroff; data->motion_trig->dev.parent = &client->dev; data->motion_trig->ops = &kxcjk1013_trigger_ops; @@ -1300,6 +1319,8 @@ err_trigger_unregister: iio_trigger_unregister(data->dready_trig); if (data->motion_trig) iio_trigger_unregister(data->motion_trig); +err_poweroff: + kxcjk1013_set_mode(data, STANDBY); return ret; } @@ -1349,10 +1370,7 @@ static int kxcjk1013_resume(struct device *dev) int ret = 0; mutex_lock(&data->mutex); - /* Check, if the suspend occured while active */ - if (data->dready_trigger_on || data->motion_trigger_on || - data->ev_enable_state) - ret = kxcjk1013_set_mode(data, OPERATION); + ret = kxcjk1013_set_mode(data, OPERATION); mutex_unlock(&data->mutex); return ret; @@ -1364,8 +1382,14 @@ static int kxcjk1013_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct kxcjk1013_data *data = iio_priv(indio_dev); + int ret; - return kxcjk1013_set_mode(data, STANDBY); + ret = kxcjk1013_set_mode(data, STANDBY); + if (ret < 0) { + dev_err(&data->client->dev, "powering off device failed\n"); + return -EAGAIN; + } + return 0; } static int kxcjk1013_runtime_resume(struct device *dev) @@ -1399,6 +1423,7 @@ static const struct acpi_device_id kx_acpi_match[] = { {"KXCJ1013", KXCJK1013}, {"KXCJ1008", KXCJ91008}, {"KXTJ1009", KXTJ21009}, + {"SMO8500", KXCJ91008}, { }, }; MODULE_DEVICE_TABLE(acpi, kx_acpi_match); @@ -1407,6 +1432,7 @@ static const struct i2c_device_id kxcjk1013_id[] = { {"kxcjk1013", KXCJK1013}, {"kxcj91008", KXCJ91008}, {"kxtj21009", KXTJ21009}, + {"SMO8500", KXCJ91008}, {} }; diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 3c12d4966376..5b80657883bb 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -111,7 +111,7 @@ static const int mma8452_samp_freq[8][2] = { {6, 250000}, {1, 560000} }; -/* +/* * Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048 * The userspace interface uses m/s^2 and we declare micro units * So scale factor is given by: diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c new file mode 100644 index 000000000000..6563e267b8ae --- /dev/null +++ b/drivers/iio/accel/mma9551.c @@ -0,0 +1,956 @@ +/* + * Freescale MMA9551L Intelligent Motion-Sensing Platform driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +#define MMA9551_DRV_NAME "mma9551" +#define MMA9551_IRQ_NAME "mma9551_event" +#define MMA9551_GPIO_NAME "mma9551_int" +#define MMA9551_GPIO_COUNT 4 + +/* Applications IDs */ +#define MMA9551_APPID_VERSION 0x00 +#define MMA9551_APPID_GPIO 0x03 +#define MMA9551_APPID_AFE 0x06 +#define MMA9551_APPID_TILT 0x0B +#define MMA9551_APPID_SLEEP_WAKE 0x12 +#define MMA9551_APPID_RESET 0x17 +#define MMA9551_APPID_NONE 0xff + +/* Command masks for mailbox write command */ +#define MMA9551_CMD_READ_VERSION_INFO 0x00 +#define MMA9551_CMD_READ_CONFIG 0x10 +#define MMA9551_CMD_WRITE_CONFIG 0x20 +#define MMA9551_CMD_READ_STATUS 0x30 + +enum mma9551_gpio_pin { + mma9551_gpio6 = 0, + mma9551_gpio7, + mma9551_gpio8, + mma9551_gpio9, + mma9551_gpio_max = mma9551_gpio9, +}; + +/* Mailbox read command */ +#define MMA9551_RESPONSE_COCO BIT(7) + +/* Error-Status codes returned in mailbox read command */ +#define MMA9551_MCI_ERROR_NONE 0x00 +#define MMA9551_MCI_ERROR_PARAM 0x04 +#define MMA9551_MCI_INVALID_COUNT 0x19 +#define MMA9551_MCI_ERROR_COMMAND 0x1C +#define MMA9551_MCI_ERROR_INVALID_LENGTH 0x21 +#define MMA9551_MCI_ERROR_FIFO_BUSY 0x22 +#define MMA9551_MCI_ERROR_FIFO_ALLOCATED 0x23 +#define MMA9551_MCI_ERROR_FIFO_OVERSIZE 0x24 + +/* GPIO Application */ +#define MMA9551_GPIO_POL_MSB 0x08 +#define MMA9551_GPIO_POL_LSB 0x09 + +/* Sleep/Wake application */ +#define MMA9551_SLEEP_CFG 0x06 +#define MMA9551_SLEEP_CFG_SNCEN BIT(0) +#define MMA9551_SLEEP_CFG_SCHEN BIT(2) + +/* AFE application */ +#define MMA9551_AFE_X_ACCEL_REG 0x00 +#define MMA9551_AFE_Y_ACCEL_REG 0x02 +#define MMA9551_AFE_Z_ACCEL_REG 0x04 + +/* Tilt application (inclination in IIO terms). */ +#define MMA9551_TILT_XZ_ANG_REG 0x00 +#define MMA9551_TILT_YZ_ANG_REG 0x01 +#define MMA9551_TILT_XY_ANG_REG 0x02 +#define MMA9551_TILT_ANGFLG BIT(7) +#define MMA9551_TILT_QUAD_REG 0x03 +#define MMA9551_TILT_XY_QUAD_SHIFT 0 +#define MMA9551_TILT_YZ_QUAD_SHIFT 2 +#define MMA9551_TILT_XZ_QUAD_SHIFT 4 +#define MMA9551_TILT_CFG_REG 0x01 +#define MMA9551_TILT_ANG_THRESH_MASK GENMASK(3, 0) + +/* Tilt events are mapped to the first three GPIO pins. */ +enum mma9551_tilt_axis { + mma9551_x = 0, + mma9551_y, + mma9551_z, +}; + +/* + * A response is composed of: + * - control registers: MB0-3 + * - data registers: MB4-31 + * + * A request is composed of: + * - mbox to write to (always 0) + * - control registers: MB1-4 + * - data registers: MB5-31 + */ +#define MMA9551_MAILBOX_CTRL_REGS 4 +#define MMA9551_MAX_MAILBOX_DATA_REGS 28 +#define MMA9551_MAILBOX_REGS 32 + +#define MMA9551_I2C_READ_RETRIES 5 +#define MMA9551_I2C_READ_DELAY 50 /* us */ + +struct mma9551_mbox_request { + u8 start_mbox; /* Always 0. */ + u8 app_id; + /* + * See Section 5.3.1 of the MMA955xL Software Reference Manual. + * + * Bit 7: reserved, always 0 + * Bits 6-4: command + * Bits 3-0: upper bits of register offset + */ + u8 cmd_off; + u8 lower_off; + u8 nbytes; + u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1]; +} __packed; + +struct mma9551_mbox_response { + u8 app_id; + /* + * See Section 5.3.3 of the MMA955xL Software Reference Manual. + * + * Bit 7: COCO + * Bits 6-0: Error code. + */ + u8 coco_err; + u8 nbytes; + u8 req_bytes; + u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS]; +} __packed; + +struct mma9551_version_info { + __be32 device_id; + u8 rom_version[2]; + u8 fw_version[2]; + u8 hw_version[2]; + u8 fw_build[2]; +}; + +struct mma9551_data { + struct i2c_client *client; + struct mutex mutex; + int event_enabled[3]; + int irqs[MMA9551_GPIO_COUNT]; +}; + +static int mma9551_transfer(struct i2c_client *client, + u8 app_id, u8 command, u16 offset, + u8 *inbytes, int num_inbytes, + u8 *outbytes, int num_outbytes) +{ + struct mma9551_mbox_request req; + struct mma9551_mbox_response rsp; + struct i2c_msg in, out; + u8 req_len, err_code; + int ret, retries; + + if (offset >= 1 << 12) { + dev_err(&client->dev, "register offset too large\n"); + return -EINVAL; + } + + req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes; + req.start_mbox = 0; + req.app_id = app_id; + req.cmd_off = command | (offset >> 8); + req.lower_off = offset; + + if (command == MMA9551_CMD_WRITE_CONFIG) + req.nbytes = num_inbytes; + else + req.nbytes = num_outbytes; + if (num_inbytes) + memcpy(req.buf, inbytes, num_inbytes); + + out.addr = client->addr; + out.flags = 0; + out.len = req_len; + out.buf = (u8 *)&req; + + ret = i2c_transfer(client->adapter, &out, 1); + if (ret < 0) { + dev_err(&client->dev, "i2c write failed\n"); + return ret; + } + + retries = MMA9551_I2C_READ_RETRIES; + do { + udelay(MMA9551_I2C_READ_DELAY); + + in.addr = client->addr; + in.flags = I2C_M_RD; + in.len = sizeof(rsp); + in.buf = (u8 *)&rsp; + + ret = i2c_transfer(client->adapter, &in, 1); + if (ret < 0) { + dev_err(&client->dev, "i2c read failed\n"); + return ret; + } + + if (rsp.coco_err & MMA9551_RESPONSE_COCO) + break; + } while (--retries > 0); + + if (retries == 0) { + dev_err(&client->dev, + "timed out while waiting for command response\n"); + return -ETIMEDOUT; + } + + if (rsp.app_id != app_id) { + dev_err(&client->dev, + "app_id mismatch in response got %02x expected %02x\n", + rsp.app_id, app_id); + return -EINVAL; + } + + err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO; + if (err_code != MMA9551_MCI_ERROR_NONE) { + dev_err(&client->dev, "read returned error %x\n", err_code); + return -EINVAL; + } + + if (rsp.nbytes != rsp.req_bytes) { + dev_err(&client->dev, + "output length mismatch got %d expected %d\n", + rsp.nbytes, rsp.req_bytes); + return -EINVAL; + } + + if (num_outbytes) + memcpy(outbytes, rsp.buf, num_outbytes); + + return 0; +} + +static int mma9551_read_config_byte(struct i2c_client *client, u8 app_id, + u16 reg, u8 *val) +{ + return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG, + reg, NULL, 0, val, 1); +} + +static int mma9551_write_config_byte(struct i2c_client *client, u8 app_id, + u16 reg, u8 val) +{ + return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg, + &val, 1, NULL, 0); +} + +static int mma9551_read_status_byte(struct i2c_client *client, u8 app_id, + u16 reg, u8 *val) +{ + return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS, + reg, NULL, 0, val, 1); +} + +static int mma9551_read_status_word(struct i2c_client *client, u8 app_id, + u16 reg, u16 *val) +{ + int ret; + __be16 v; + + ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS, + reg, NULL, 0, (u8 *)&v, 2); + *val = be16_to_cpu(v); + + return ret; +} + +static int mma9551_update_config_bits(struct i2c_client *client, u8 app_id, + u16 reg, u8 mask, u8 val) +{ + int ret; + u8 tmp, orig; + + ret = mma9551_read_config_byte(client, app_id, reg, &orig); + if (ret < 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + if (tmp == orig) + return 0; + + return mma9551_write_config_byte(client, app_id, reg, tmp); +} + +/* + * The polarity parameter is described in section 6.2.2, page 66, of the + * Software Reference Manual. Basically, polarity=0 means the interrupt + * line has the same value as the selected bit, while polarity=1 means + * the line is inverted. + */ +static int mma9551_gpio_config(struct i2c_client *client, + enum mma9551_gpio_pin pin, + u8 app_id, u8 bitnum, int polarity) +{ + u8 reg, pol_mask, pol_val; + int ret; + + if (pin > mma9551_gpio_max) { + dev_err(&client->dev, "bad GPIO pin\n"); + return -EINVAL; + } + + /* + * Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and + * 0x03, and so on. + */ + reg = pin * 2; + + ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO, + reg, app_id); + if (ret < 0) { + dev_err(&client->dev, "error setting GPIO app_id\n"); + return ret; + } + + ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO, + reg + 1, bitnum); + if (ret < 0) { + dev_err(&client->dev, "error setting GPIO bit number\n"); + return ret; + } + + switch (pin) { + case mma9551_gpio6: + reg = MMA9551_GPIO_POL_LSB; + pol_mask = 1 << 6; + break; + case mma9551_gpio7: + reg = MMA9551_GPIO_POL_LSB; + pol_mask = 1 << 7; + break; + case mma9551_gpio8: + reg = MMA9551_GPIO_POL_MSB; + pol_mask = 1 << 0; + break; + case mma9551_gpio9: + reg = MMA9551_GPIO_POL_MSB; + pol_mask = 1 << 1; + break; + } + pol_val = polarity ? pol_mask : 0; + + ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg, + pol_mask, pol_val); + if (ret < 0) + dev_err(&client->dev, "error setting GPIO polarity\n"); + + return ret; +} + +static int mma9551_read_version(struct i2c_client *client) +{ + struct mma9551_version_info info; + int ret; + + ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00, + NULL, 0, (u8 *)&info, sizeof(info)); + if (ret < 0) + return ret; + + dev_info(&client->dev, "Device ID 0x%x, firmware version %02x.%02x\n", + be32_to_cpu(info.device_id), info.fw_version[0], + info.fw_version[1]); + + return 0; +} + +/* + * Use 'false' as the second parameter to cause the device to enter + * sleep. + */ +static int mma9551_set_device_state(struct i2c_client *client, + bool enable) +{ + return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE, + MMA9551_SLEEP_CFG, + MMA9551_SLEEP_CFG_SNCEN, + enable ? 0 : MMA9551_SLEEP_CFG_SNCEN); +} + +static int mma9551_read_incli_chan(struct i2c_client *client, + const struct iio_chan_spec *chan, + int *val) +{ + u8 quad_shift, angle, quadrant; + u16 reg_addr; + int ret; + + switch (chan->channel2) { + case IIO_MOD_X: + reg_addr = MMA9551_TILT_YZ_ANG_REG; + quad_shift = MMA9551_TILT_YZ_QUAD_SHIFT; + break; + case IIO_MOD_Y: + reg_addr = MMA9551_TILT_XZ_ANG_REG; + quad_shift = MMA9551_TILT_XZ_QUAD_SHIFT; + break; + case IIO_MOD_Z: + reg_addr = MMA9551_TILT_XY_ANG_REG; + quad_shift = MMA9551_TILT_XY_QUAD_SHIFT; + break; + default: + return -EINVAL; + } + + ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, + reg_addr, &angle); + if (ret < 0) + return ret; + + ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, + MMA9551_TILT_QUAD_REG, &quadrant); + if (ret < 0) + return ret; + + angle &= ~MMA9551_TILT_ANGFLG; + quadrant = (quadrant >> quad_shift) & 0x03; + + if (quadrant == 1 || quadrant == 3) + *val = 90 * (quadrant + 1) - angle; + else + *val = angle + 90 * quadrant; + + return IIO_VAL_INT; +} + +static int mma9551_read_accel_chan(struct i2c_client *client, + const struct iio_chan_spec *chan, + int *val, int *val2) +{ + u16 reg_addr; + s16 raw_accel; + int ret; + + switch (chan->channel2) { + case IIO_MOD_X: + reg_addr = MMA9551_AFE_X_ACCEL_REG; + break; + case IIO_MOD_Y: + reg_addr = MMA9551_AFE_Y_ACCEL_REG; + break; + case IIO_MOD_Z: + reg_addr = MMA9551_AFE_Z_ACCEL_REG; + break; + default: + return -EINVAL; + } + + ret = mma9551_read_status_word(client, MMA9551_APPID_AFE, + reg_addr, &raw_accel); + if (ret < 0) + return ret; + + *val = raw_accel; + + return IIO_VAL_INT; +} + +static int mma9551_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mma9551_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_INCLI: + mutex_lock(&data->mutex); + ret = mma9551_read_incli_chan(data->client, chan, val); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_ACCEL: + mutex_lock(&data->mutex); + ret = mma9551_read_accel_chan(data->client, + chan, val, val2); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + *val = 0; + *val2 = 2440; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int mma9551_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct mma9551_data *data = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_INCLI: + /* IIO counts axes from 1, because IIO_NO_MOD is 0. */ + return data->event_enabled[chan->channel2 - 1]; + default: + return -EINVAL; + } +} + +static int mma9551_config_incli_event(struct iio_dev *indio_dev, + enum iio_modifier axis, + int state) +{ + struct mma9551_data *data = iio_priv(indio_dev); + enum mma9551_tilt_axis mma_axis; + int ret; + + /* IIO counts axes from 1, because IIO_NO_MOD is 0. */ + mma_axis = axis - 1; + + if (data->event_enabled[mma_axis] == state) + return 0; + + if (state == 0) { + ret = mma9551_gpio_config(data->client, + (enum mma9551_gpio_pin)mma_axis, + MMA9551_APPID_NONE, 0, 0); + if (ret < 0) + return ret; + } else { + int bitnum; + + /* Bit 7 of each angle register holds the angle flag. */ + switch (axis) { + case IIO_MOD_X: + bitnum = 7 + 8 * MMA9551_TILT_YZ_ANG_REG; + break; + case IIO_MOD_Y: + bitnum = 7 + 8 * MMA9551_TILT_XZ_ANG_REG; + break; + case IIO_MOD_Z: + bitnum = 7 + 8 * MMA9551_TILT_XY_ANG_REG; + break; + default: + return -EINVAL; + } + + ret = mma9551_gpio_config(data->client, + (enum mma9551_gpio_pin)mma_axis, + MMA9551_APPID_TILT, bitnum, 0); + if (ret < 0) + return ret; + } + + data->event_enabled[mma_axis] = state; + + return ret; +} + +static int mma9551_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct mma9551_data *data = iio_priv(indio_dev); + int ret; + + switch (chan->type) { + case IIO_INCLI: + mutex_lock(&data->mutex); + ret = mma9551_config_incli_event(indio_dev, + chan->channel2, state); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } +} + +static int mma9551_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct mma9551_data *data = iio_priv(indio_dev); + int ret; + + switch (chan->type) { + case IIO_INCLI: + if (val2 != 0 || val < 1 || val > 10) + return -EINVAL; + mutex_lock(&data->mutex); + ret = mma9551_update_config_bits(data->client, + MMA9551_APPID_TILT, + MMA9551_TILT_CFG_REG, + MMA9551_TILT_ANG_THRESH_MASK, + val); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } +} + +static int mma9551_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct mma9551_data *data = iio_priv(indio_dev); + int ret; + u8 tmp; + + switch (chan->type) { + case IIO_INCLI: + mutex_lock(&data->mutex); + ret = mma9551_read_config_byte(data->client, + MMA9551_APPID_TILT, + MMA9551_TILT_CFG_REG, &tmp); + mutex_unlock(&data->mutex); + if (ret < 0) + return ret; + *val = tmp & MMA9551_TILT_ANG_THRESH_MASK; + *val2 = 0; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_event_spec mma9551_incli_event = { + .type = IIO_EV_TYPE_ROC, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), +}; + +#define MMA9551_ACCEL_CHANNEL(axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +#define MMA9551_INCLI_CHANNEL(axis) { \ + .type = IIO_INCLI, \ + .modified = 1, \ + .channel2 = axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .event_spec = &mma9551_incli_event, \ + .num_event_specs = 1, \ +} + +static const struct iio_chan_spec mma9551_channels[] = { + MMA9551_ACCEL_CHANNEL(IIO_MOD_X), + MMA9551_ACCEL_CHANNEL(IIO_MOD_Y), + MMA9551_ACCEL_CHANNEL(IIO_MOD_Z), + + MMA9551_INCLI_CHANNEL(IIO_MOD_X), + MMA9551_INCLI_CHANNEL(IIO_MOD_Y), + MMA9551_INCLI_CHANNEL(IIO_MOD_Z), +}; + +static const struct iio_info mma9551_info = { + .driver_module = THIS_MODULE, + .read_raw = mma9551_read_raw, + .read_event_config = mma9551_read_event_config, + .write_event_config = mma9551_write_event_config, + .read_event_value = mma9551_read_event_value, + .write_event_value = mma9551_write_event_value, +}; + +static irqreturn_t mma9551_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct mma9551_data *data = iio_priv(indio_dev); + int i, ret, mma_axis = -1; + u16 reg; + u8 val; + + mutex_lock(&data->mutex); + + for (i = 0; i < 3; i++) + if (irq == data->irqs[i]) { + mma_axis = i; + break; + } + + if (mma_axis == -1) { + /* IRQ was triggered on 4th line, which we don't use. */ + dev_warn(&data->client->dev, + "irq triggered on unused line %d\n", data->irqs[3]); + goto out; + } + + switch (mma_axis) { + case mma9551_x: + reg = MMA9551_TILT_YZ_ANG_REG; + break; + case mma9551_y: + reg = MMA9551_TILT_XZ_ANG_REG; + break; + case mma9551_z: + reg = MMA9551_TILT_XY_ANG_REG; + break; + } + + /* + * Read the angle even though we don't use it, otherwise we + * won't get any further interrupts. + */ + ret = mma9551_read_status_byte(data->client, MMA9551_APPID_TILT, + reg, &val); + if (ret < 0) { + dev_err(&data->client->dev, + "error %d reading tilt register in IRQ\n", ret); + goto out; + } + + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_INCLI, 0, (mma_axis + 1), + IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING), + iio_get_time_ns()); + +out: + mutex_unlock(&data->mutex); + + return IRQ_HANDLED; +} + +static int mma9551_init(struct mma9551_data *data) +{ + int ret; + + ret = mma9551_read_version(data->client); + if (ret) + return ret; + + /* Power on chip and enable doze mode. */ + return mma9551_update_config_bits(data->client, + MMA9551_APPID_SLEEP_WAKE, + MMA9551_SLEEP_CFG, + MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN, + MMA9551_SLEEP_CFG_SCHEN); +} + +static int mma9551_gpio_probe(struct iio_dev *indio_dev) +{ + struct gpio_desc *gpio; + int i, ret; + struct mma9551_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + + for (i = 0; i < MMA9551_GPIO_COUNT; i++) { + gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i); + if (IS_ERR(gpio)) { + dev_err(dev, "acpi gpio get index failed\n"); + return PTR_ERR(gpio); + } + + ret = gpiod_direction_input(gpio); + if (ret) + return ret; + + data->irqs[i] = gpiod_to_irq(gpio); + ret = devm_request_threaded_irq(dev, data->irqs[i], + NULL, mma9551_event_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + MMA9551_IRQ_NAME, indio_dev); + if (ret < 0) { + dev_err(dev, "request irq %d failed\n", data->irqs[i]); + return ret; + } + + dev_dbg(dev, "gpio resource, no:%d irq:%d\n", + desc_to_gpio(gpio), data->irqs[i]); + } + + return 0; +} + +static const char *mma9551_match_acpi_device(struct device *dev) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return NULL; + + return dev_name(dev); +} + +static int mma9551_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mma9551_data *data; + struct iio_dev *indio_dev; + const char *name = NULL; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + if (id) + name = id->name; + else if (ACPI_HANDLE(&client->dev)) + name = mma9551_match_acpi_device(&client->dev); + + ret = mma9551_init(data); + if (ret < 0) + return ret; + + mutex_init(&data->mutex); + + indio_dev->dev.parent = &client->dev; + indio_dev->channels = mma9551_channels; + indio_dev->num_channels = ARRAY_SIZE(mma9551_channels); + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mma9551_info; + + ret = mma9551_gpio_probe(indio_dev); + if (ret < 0) + goto out_poweroff; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "unable to register iio device\n"); + goto out_poweroff; + } + + return 0; + +out_poweroff: + mma9551_set_device_state(client, false); + + return ret; +} + +static int mma9551_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct mma9551_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + mutex_lock(&data->mutex); + mma9551_set_device_state(data->client, false); + mutex_unlock(&data->mutex); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mma9551_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct mma9551_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + mma9551_set_device_state(data->client, false); + mutex_unlock(&data->mutex); + + return 0; +} + +static int mma9551_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct mma9551_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + mma9551_set_device_state(data->client, true); + mutex_unlock(&data->mutex); + + return 0; +} +#else +#define mma9551_suspend NULL +#define mma9551_resume NULL +#endif + +static const struct dev_pm_ops mma9551_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) +}; + +static const struct acpi_device_id mma9551_acpi_match[] = { + {"MMA9551", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, mma9551_acpi_match); + +static const struct i2c_device_id mma9551_id[] = { + {"mma9551", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mma9551_id); + +static struct i2c_driver mma9551_driver = { + .driver = { + .name = MMA9551_DRV_NAME, + .acpi_match_table = ACPI_PTR(mma9551_acpi_match), + .pm = &mma9551_pm_ops, + }, + .probe = mma9551_probe, + .remove = mma9551_remove, + .id_table = mma9551_id, +}; + +module_i2c_driver(mma9551_driver); + +MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); +MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MMA9551L motion-sensing platform driver"); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index b730864731e8..5eea299518a3 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -250,7 +250,7 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev, struct iio_buffer *buffer; int ret; - buffer = iio_kfifo_allocate(indio_dev); + buffer = iio_kfifo_allocate(); if (!buffer) return -ENOMEM; @@ -264,16 +264,8 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev, indio_dev->setup_ops = setup_ops; indio_dev->modes |= INDIO_BUFFER_HARDWARE; - ret = iio_buffer_register(indio_dev, - indio_dev->channels, - indio_dev->num_channels); - if (ret) - goto error_free_irq; - return 0; -error_free_irq: - free_irq(irq, indio_dev); error_kfifo_free: iio_kfifo_free(indio_dev->buffer); return ret; @@ -285,7 +277,6 @@ static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev) free_irq(adc_dev->mfd_tscadc->irq, indio_dev); iio_kfifo_free(indio_dev->buffer); - iio_buffer_unregister(indio_dev); } diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index ba6f6a91dfff..c0d364ebaea8 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -31,7 +31,7 @@ struct ad8366_state { }; static int ad8366_write(struct iio_dev *indio_dev, - unsigned char ch_a, char unsigned ch_b) + unsigned char ch_a, unsigned char ch_b) { struct ad8366_state *st = iio_priv(indio_dev); int ret; @@ -166,7 +166,7 @@ static int ad8366_probe(struct spi_device *spi) if (ret) goto error_disable_reg; - ad8366_write(indio_dev, 0 , 0); + ad8366_write(indio_dev, 0, 0); return 0; diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c index 78a6a1ab3ece..5b377373f48d 100644 --- a/drivers/iio/common/st_sensors/st_sensors_spi.c +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -54,7 +54,7 @@ static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb, if (err) goto acc_spi_read_error; - memcpy(data, tb->rx_buf, len*sizeof(u8)); + memcpy(data, tb->rx_buf, len); mutex_unlock(&tb->buf_lock); return len; diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index 7c5245d9f99c..50ed8d1ca45a 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -445,7 +445,7 @@ static int ad9523_store_eeprom(struct iio_dev *indio_dev) tmp = 4; do { - msleep(16); + msleep(20); ret = ad9523_read(indio_dev, AD9523_EEPROM_DATA_XFER_STATUS); if (ret < 0) diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 63a25d9e1204..10a0dfc3b01f 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -387,10 +387,8 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) int ret; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "could not allocate memory for platform data\n"); + if (!pdata) return NULL; - } strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1); @@ -613,9 +611,8 @@ static int adf4350_remove(struct spi_device *spi) if (st->clk) clk_disable_unprepare(st->clk); - if (!IS_ERR(reg)) { + if (!IS_ERR(reg)) regulator_disable(reg); - } return 0; } diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 5f0ea77fe717..359883525ab7 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -48,6 +48,8 @@ unsigned int iio_buffer_poll(struct file *filp, ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, size_t n, loff_t *f_ps); +int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev); +void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev); #define iio_buffer_poll_addr (&iio_buffer_poll) #define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer) @@ -60,6 +62,13 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); #define iio_buffer_poll_addr NULL #define iio_buffer_read_first_n_outer_addr NULL +static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {} + static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {} static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {} diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 2b0e45133e9d..5e610f7de5aa 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -25,6 +25,17 @@ config ADIS16480 Say yes here to build support for Analog Devices ADIS16375, ADIS16480, ADIS16485, ADIS16488 inertial sensors. +config KMX61 + tristate "Kionix KMX61 6-axis accelerometer and magnetometer" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build a driver for Kionix KMX61 6-axis + accelerometer and magnetometer. + To compile this driver as module, choose M here: the module will + be called kmx61. + source "drivers/iio/imu/inv_mpu6050/Kconfig" endmenu diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 114d2c17cbe2..e1e6e3d70e26 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -14,3 +14,5 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o obj-y += inv_mpu6050/ + +obj-$(CONFIG_KMX61) += kmx61.o diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 2d0608ba88d7..48fbc0bc7e2a 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -7,6 +7,7 @@ config INV_MPU6050_IIO depends on I2C && SYSFS select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select I2C_MUX help This driver supports the Invensense MPU6050 devices. This driver can also support MPU6500 in MPU6050 compatibility mode diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index b75519deac1a..f73e60b7a796 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -23,6 +23,8 @@ #include <linux/kfifo.h> #include <linux/spinlock.h> #include <linux/iio/iio.h> +#include <linux/i2c-mux.h> +#include <linux/acpi.h> #include "inv_mpu_iio.h" /* @@ -52,6 +54,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = { .int_enable = INV_MPU6050_REG_INT_ENABLE, .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, + .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, }; static const struct inv_mpu6050_chip_config chip_config_6050 = { @@ -77,6 +80,83 @@ int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d) return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d); } +/* + * The i2c read/write needs to happen in unlocked mode. As the parent + * adapter is common. If we use locked versions, it will fail as + * the mux adapter will lock the parent i2c adapter, while calling + * select/deselect functions. + */ +static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st, + u8 reg, u8 d) +{ + int ret; + u8 buf[2]; + struct i2c_msg msg[1] = { + { + .addr = st->client->addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + buf[0] = reg; + buf[1] = d; + ret = __i2c_transfer(st->client->adapter, msg, 1); + if (ret != 1) + return ret; + + return 0; +} + +static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv, + u32 chan_id) +{ + struct iio_dev *indio_dev = mux_priv; + struct inv_mpu6050_state *st = iio_priv(indio_dev); + int ret = 0; + + /* Use the same mutex which was used everywhere to protect power-op */ + mutex_lock(&indio_dev->mlock); + if (!st->powerup_count) { + ret = inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1, + 0); + if (ret) + goto write_error; + + msleep(INV_MPU6050_REG_UP_TIME); + } + if (!ret) { + st->powerup_count++; + ret = inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg, + st->client->irq | + INV_MPU6050_BIT_BYPASS_EN); + } +write_error: + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap, + void *mux_priv, u32 chan_id) +{ + struct iio_dev *indio_dev = mux_priv; + struct inv_mpu6050_state *st = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + /* It doesn't really mattter, if any of the calls fails */ + inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg, + st->client->irq); + st->powerup_count--; + if (!st->powerup_count) + inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1, + INV_MPU6050_BIT_SLEEP); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) { u8 d, mgmt_1; @@ -133,13 +213,22 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) { - int result; + int result = 0; + + if (power_on) { + /* Already under indio-dev->mlock mutex */ + if (!st->powerup_count) + result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, + 0); + if (!result) + st->powerup_count++; + } else { + st->powerup_count--; + if (!st->powerup_count) + result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, + INV_MPU6050_BIT_SLEEP); + } - if (power_on) - result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, 0); - else - result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, - INV_MPU6050_BIT_SLEEP); if (result) return result; @@ -673,6 +762,7 @@ static int inv_mpu_probe(struct i2c_client *client, st = iio_priv(indio_dev); st->client = client; + st->powerup_count = 0; pdata = dev_get_platdata(&client->dev); if (pdata) st->plat_data = *pdata; @@ -720,8 +810,21 @@ static int inv_mpu_probe(struct i2c_client *client, goto out_remove_trigger; } + st->mux_adapter = i2c_add_mux_adapter(client->adapter, + &client->dev, + indio_dev, + 0, 0, 0, + inv_mpu6050_select_bypass, + inv_mpu6050_deselect_bypass); + if (!st->mux_adapter) { + result = -ENODEV; + goto out_unreg_device; + } + return 0; +out_unreg_device: + iio_device_unregister(indio_dev); out_remove_trigger: inv_mpu6050_remove_trigger(st); out_unreg_ring: @@ -734,6 +837,7 @@ static int inv_mpu_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct inv_mpu6050_state *st = iio_priv(indio_dev); + i2c_del_mux_adapter(st->mux_adapter); iio_device_unregister(indio_dev); inv_mpu6050_remove_trigger(st); iio_triggered_buffer_cleanup(indio_dev); @@ -772,6 +876,13 @@ static const struct i2c_device_id inv_mpu_id[] = { MODULE_DEVICE_TABLE(i2c, inv_mpu_id); +static const struct acpi_device_id inv_acpi_match[] = { + {"INVN6500", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(acpi, inv_acpi_match); + static struct i2c_driver inv_mpu_driver = { .probe = inv_mpu_probe, .remove = inv_mpu_remove, @@ -780,6 +891,7 @@ static struct i2c_driver inv_mpu_driver = { .owner = THIS_MODULE, .name = "inv-mpu6050", .pm = INV_MPU6050_PMOPS, + .acpi_match_table = ACPI_PTR(inv_acpi_match), }, }; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index e7799315d4dc..aa837de57079 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -54,6 +54,7 @@ struct inv_mpu6050_reg_map { u8 int_enable; u8 pwr_mgmt_1; u8 pwr_mgmt_2; + u8 int_pin_cfg; }; /*device enum */ @@ -119,6 +120,8 @@ struct inv_mpu6050_state { enum inv_devices chip_type; spinlock_t time_stamp_lock; struct i2c_client *client; + struct i2c_adapter *mux_adapter; + unsigned int powerup_count; struct inv_mpu6050_platform_data plat_data; DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); }; @@ -179,6 +182,9 @@ struct inv_mpu6050_state { /* 6 + 6 round up and plus 8 */ #define INV_MPU6050_OUTPUT_DATA_SIZE 24 +#define INV_MPU6050_REG_INT_PIN_CFG 0x37 +#define INV_MPU6050_BIT_BYPASS_EN 0x2 + /* init parameters */ #define INV_MPU6050_INIT_FIFO_RATE 50 #define INV_MPU6050_TIME_STAMP_TOR 5 diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c new file mode 100644 index 000000000000..5cc3692acf37 --- /dev/null +++ b/drivers/iio/imu/kmx61.c @@ -0,0 +1,1595 @@ +/* + * KMX61 - Kionix 6-axis Accelerometer/Magnetometer + * + * Copyright (c) 2014, Intel Corporation. + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * IIO driver for KMX61 (7-bit I2C slave address 0x0E or 0x0F). + * + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/trigger.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#define KMX61_DRV_NAME "kmx61" +#define KMX61_GPIO_NAME "kmx61_int" +#define KMX61_IRQ_NAME "kmx61_event" + +#define KMX61_REG_WHO_AM_I 0x00 +#define KMX61_REG_INS1 0x01 +#define KMX61_REG_INS2 0x02 + +/* + * three 16-bit accelerometer output registers for X/Y/Z axis + * we use only XOUT_L as a base register, all other addresses + * can be obtained by applying an offset and are provided here + * only for clarity. + */ +#define KMX61_ACC_XOUT_L 0x0A +#define KMX61_ACC_XOUT_H 0x0B +#define KMX61_ACC_YOUT_L 0x0C +#define KMX61_ACC_YOUT_H 0x0D +#define KMX61_ACC_ZOUT_L 0x0E +#define KMX61_ACC_ZOUT_H 0x0F + +/* + * one 16-bit temperature output register + */ +#define KMX61_TEMP_L 0x10 +#define KMX61_TEMP_H 0x11 + +/* + * three 16-bit magnetometer output registers for X/Y/Z axis + */ +#define KMX61_MAG_XOUT_L 0x12 +#define KMX61_MAG_XOUT_H 0x13 +#define KMX61_MAG_YOUT_L 0x14 +#define KMX61_MAG_YOUT_H 0x15 +#define KMX61_MAG_ZOUT_L 0x16 +#define KMX61_MAG_ZOUT_H 0x17 + +#define KMX61_REG_INL 0x28 +#define KMX61_REG_STBY 0x29 +#define KMX61_REG_CTRL1 0x2A +#define KMX61_REG_CTRL2 0x2B +#define KMX61_REG_ODCNTL 0x2C +#define KMX61_REG_INC1 0x2D + +#define KMX61_REG_WUF_THRESH 0x3D +#define KMX61_REG_WUF_TIMER 0x3E + +#define KMX61_ACC_STBY_BIT BIT(0) +#define KMX61_MAG_STBY_BIT BIT(1) +#define KMX61_ACT_STBY_BIT BIT(7) + +#define KMX61_ALL_STBY (KMX61_ACC_STBY_BIT | KMX61_MAG_STBY_BIT) + +#define KMX61_REG_INS1_BIT_WUFS BIT(1) + +#define KMX61_REG_INS2_BIT_ZP BIT(0) +#define KMX61_REG_INS2_BIT_ZN BIT(1) +#define KMX61_REG_INS2_BIT_YP BIT(2) +#define KMX61_REG_INS2_BIT_YN BIT(3) +#define KMX61_REG_INS2_BIT_XP BIT(4) +#define KMX61_REG_INS2_BIT_XN BIT(5) + +#define KMX61_REG_CTRL1_GSEL_MASK 0x03 + +#define KMX61_REG_CTRL1_BIT_RES BIT(4) +#define KMX61_REG_CTRL1_BIT_DRDYE BIT(5) +#define KMX61_REG_CTRL1_BIT_WUFE BIT(6) +#define KMX61_REG_CTRL1_BIT_BTSE BIT(7) + +#define KMX61_REG_INC1_BIT_WUFS BIT(0) +#define KMX61_REG_INC1_BIT_DRDYM BIT(1) +#define KMX61_REG_INC1_BIT_DRDYA BIT(2) +#define KMX61_REG_INC1_BIT_IEN BIT(5) + +#define KMX61_ACC_ODR_SHIFT 0 +#define KMX61_MAG_ODR_SHIFT 4 +#define KMX61_ACC_ODR_MASK 0x0F +#define KMX61_MAG_ODR_MASK 0xF0 + +#define KMX61_OWUF_MASK 0x7 + +#define KMX61_DEFAULT_WAKE_THRESH 1 +#define KMX61_DEFAULT_WAKE_DURATION 1 + +#define KMX61_SLEEP_DELAY_MS 2000 + +#define KMX61_CHIP_ID 0x12 + +/* KMX61 devices */ +#define KMX61_ACC 0x01 +#define KMX61_MAG 0x02 + +struct kmx61_data { + struct i2c_client *client; + + /* serialize access to non-atomic ops, e.g set_mode */ + struct mutex lock; + + /* standby state */ + bool acc_stby; + bool mag_stby; + + /* power state */ + bool acc_ps; + bool mag_ps; + + /* config bits */ + u8 range; + u8 odr_bits; + u8 wake_thresh; + u8 wake_duration; + + /* accelerometer specific data */ + struct iio_dev *acc_indio_dev; + struct iio_trigger *acc_dready_trig; + struct iio_trigger *motion_trig; + bool acc_dready_trig_on; + bool motion_trig_on; + bool ev_enable_state; + + /* magnetometer specific data */ + struct iio_dev *mag_indio_dev; + struct iio_trigger *mag_dready_trig; + bool mag_dready_trig_on; +}; + +enum kmx61_range { + KMX61_RANGE_2G, + KMX61_RANGE_4G, + KMX61_RANGE_8G, +}; + +enum kmx61_axis { + KMX61_AXIS_X, + KMX61_AXIS_Y, + KMX61_AXIS_Z, +}; + +static const u16 kmx61_uscale_table[] = {9582, 19163, 38326}; + +static const struct { + int val; + int val2; + u8 odr_bits; +} kmx61_samp_freq_table[] = { {12, 500000, 0x00}, + {25, 0, 0x01}, + {50, 0, 0x02}, + {100, 0, 0x03}, + {200, 0, 0x04}, + {400, 0, 0x05}, + {800, 0, 0x06}, + {1600, 0, 0x07}, + {0, 781000, 0x08}, + {1, 563000, 0x09}, + {3, 125000, 0x0A}, + {6, 250000, 0x0B} }; + +static const struct { + int val; + int val2; + int odr_bits; +} kmx61_wake_up_odr_table[] = { {0, 781000, 0x00}, + {1, 563000, 0x01}, + {3, 125000, 0x02}, + {6, 250000, 0x03}, + {12, 500000, 0x04}, + {25, 0, 0x05}, + {50, 0, 0x06}, + {100, 0, 0x06}, + {200, 0, 0x06}, + {400, 0, 0x06}, + {800, 0, 0x06}, + {1600, 0, 0x06} }; + +static IIO_CONST_ATTR(accel_scale_available, "0.009582 0.019163 0.038326"); +static IIO_CONST_ATTR(magn_scale_available, "0.001465"); +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "0.781000 1.563000 3.125000 6.250000 12.500000 25 50 100 200 400 800"); + +static struct attribute *kmx61_acc_attributes[] = { + &iio_const_attr_accel_scale_available.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static struct attribute *kmx61_mag_attributes[] = { + &iio_const_attr_magn_scale_available.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group kmx61_acc_attribute_group = { + .attrs = kmx61_acc_attributes, +}; + +static const struct attribute_group kmx61_mag_attribute_group = { + .attrs = kmx61_mag_attributes, +}; + +static const struct iio_event_spec kmx61_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD), +}; + +#define KMX61_ACC_CHAN(_axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## _axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .address = KMX61_ACC, \ + .scan_index = KMX61_AXIS_ ## _axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_LE, \ + }, \ + .event_spec = &kmx61_event, \ + .num_event_specs = 1 \ +} + +#define KMX61_MAG_CHAN(_axis) { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## _axis, \ + .address = KMX61_MAG, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = KMX61_AXIS_ ## _axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 14, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_LE, \ + }, \ +} + +static const struct iio_chan_spec kmx61_acc_channels[] = { + KMX61_ACC_CHAN(X), + KMX61_ACC_CHAN(Y), + KMX61_ACC_CHAN(Z), +}; + +static const struct iio_chan_spec kmx61_mag_channels[] = { + KMX61_MAG_CHAN(X), + KMX61_MAG_CHAN(Y), + KMX61_MAG_CHAN(Z), +}; + +static void kmx61_set_data(struct iio_dev *indio_dev, struct kmx61_data *data) +{ + struct kmx61_data **priv = iio_priv(indio_dev); + + *priv = data; +} + +static struct kmx61_data *kmx61_get_data(struct iio_dev *indio_dev) +{ + return *(struct kmx61_data **)iio_priv(indio_dev); +} + +static int kmx61_convert_freq_to_bit(int val, int val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) + if (val == kmx61_samp_freq_table[i].val && + val2 == kmx61_samp_freq_table[i].val2) + return kmx61_samp_freq_table[i].odr_bits; + return -EINVAL; +} + +static int kmx61_convert_bit_to_freq(u8 odr_bits, int *val, int *val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) + if (odr_bits == kmx61_samp_freq_table[i].odr_bits) { + *val = kmx61_samp_freq_table[i].val; + *val2 = kmx61_samp_freq_table[i].val2; + return 0; + } + return -EINVAL; +} + + +static int kmx61_convert_wake_up_odr_to_bit(int val, int val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kmx61_wake_up_odr_table); ++i) + if (kmx61_wake_up_odr_table[i].val == val && + kmx61_wake_up_odr_table[i].val2 == val2) + return kmx61_wake_up_odr_table[i].odr_bits; + return -EINVAL; +} + +/** + * kmx61_set_mode() - set KMX61 device operating mode + * @data - kmx61 device private data pointer + * @mode - bitmask, indicating operating mode for @device + * @device - bitmask, indicating device for which @mode needs to be set + * @update - update stby bits stored in device's private @data + * + * For each sensor (accelerometer/magnetometer) there are two operating modes + * STANDBY and OPERATION. Neither accel nor magn can be disabled independently + * if they are both enabled. Internal sensors state is saved in acc_stby and + * mag_stby members of driver's private @data. + */ +static int kmx61_set_mode(struct kmx61_data *data, u8 mode, u8 device, + bool update) +{ + int ret; + int acc_stby = -1, mag_stby = -1; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_STBY); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_stby\n"); + return ret; + } + if (device & KMX61_ACC) { + if (mode & KMX61_ACC_STBY_BIT) { + ret |= KMX61_ACC_STBY_BIT; + acc_stby = 1; + } else { + ret &= ~KMX61_ACC_STBY_BIT; + acc_stby = 0; + } + } + + if (device & KMX61_MAG) { + if (mode & KMX61_MAG_STBY_BIT) { + ret |= KMX61_MAG_STBY_BIT; + mag_stby = 1; + } else { + ret &= ~KMX61_MAG_STBY_BIT; + mag_stby = 0; + } + } + + if (mode & KMX61_ACT_STBY_BIT) + ret |= KMX61_ACT_STBY_BIT; + + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_STBY, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_stby\n"); + return ret; + } + + if (acc_stby != -1 && update) + data->acc_stby = acc_stby; + if (mag_stby != -1 && update) + data->mag_stby = mag_stby; + + return 0; +} + +static int kmx61_get_mode(struct kmx61_data *data, u8 *mode, u8 device) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_STBY); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_stby\n"); + return ret; + } + *mode = 0; + + if (device & KMX61_ACC) { + if (ret & KMX61_ACC_STBY_BIT) + *mode |= KMX61_ACC_STBY_BIT; + else + *mode &= ~KMX61_ACC_STBY_BIT; + } + + if (device & KMX61_MAG) { + if (ret & KMX61_MAG_STBY_BIT) + *mode |= KMX61_MAG_STBY_BIT; + else + *mode &= ~KMX61_MAG_STBY_BIT; + } + + return 0; +} + +static int kmx61_set_wake_up_odr(struct kmx61_data *data, int val, int val2) +{ + int ret, odr_bits; + + odr_bits = kmx61_convert_wake_up_odr_to_bit(val, val2); + if (odr_bits < 0) + return odr_bits; + + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL2, + odr_bits); + if (ret < 0) + dev_err(&data->client->dev, "Error writing reg_ctrl2\n"); + return ret; +} + +static int kmx61_set_odr(struct kmx61_data *data, int val, int val2, u8 device) +{ + int ret; + u8 mode; + int lodr_bits, odr_bits; + + ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG); + if (ret < 0) + return ret; + + lodr_bits = kmx61_convert_freq_to_bit(val, val2); + if (lodr_bits < 0) + return lodr_bits; + + /* To change ODR, accel and magn must be in STDBY */ + ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, + true); + if (ret < 0) + return ret; + + odr_bits = 0; + if (device & KMX61_ACC) + odr_bits |= lodr_bits << KMX61_ACC_ODR_SHIFT; + if (device & KMX61_MAG) + odr_bits |= lodr_bits << KMX61_MAG_ODR_SHIFT; + + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_ODCNTL, + odr_bits); + if (ret < 0) + return ret; + + data->odr_bits = odr_bits; + + if (device & KMX61_ACC) { + ret = kmx61_set_wake_up_odr(data, val, val2); + if (ret) + return ret; + } + + return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true); +} + +static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2, + u8 device) +{ int i; + u8 lodr_bits; + + if (device & KMX61_ACC) + lodr_bits = (data->odr_bits >> KMX61_ACC_ODR_SHIFT) & + KMX61_ACC_ODR_MASK; + else if (device & KMX61_MAG) + lodr_bits = (data->odr_bits >> KMX61_MAG_ODR_SHIFT) & + KMX61_MAG_ODR_MASK; + else + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) + if (lodr_bits == kmx61_samp_freq_table[i].odr_bits) { + *val = kmx61_samp_freq_table[i].val; + *val2 = kmx61_samp_freq_table[i].val2; + return 0; + } + return -EINVAL; +} + +static int kmx61_set_range(struct kmx61_data *data, u8 range) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + return ret; + } + + ret &= ~KMX61_REG_CTRL1_GSEL_MASK; + ret |= range & KMX61_REG_CTRL1_GSEL_MASK; + + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); + return ret; + } + + data->range = range; + + return 0; +} + +static int kmx61_set_scale(struct kmx61_data *data, u16 uscale) +{ + int ret, i; + u8 mode; + + for (i = 0; i < ARRAY_SIZE(kmx61_uscale_table); i++) { + if (kmx61_uscale_table[i] == uscale) { + ret = kmx61_get_mode(data, &mode, + KMX61_ACC | KMX61_MAG); + if (ret < 0) + return ret; + + ret = kmx61_set_mode(data, KMX61_ALL_STBY, + KMX61_ACC | KMX61_MAG, true); + if (ret < 0) + return ret; + + ret = kmx61_set_range(data, i); + if (ret < 0) + return ret; + + return kmx61_set_mode(data, mode, + KMX61_ACC | KMX61_MAG, true); + } + } + return -EINVAL; +} + +static int kmx61_chip_init(struct kmx61_data *data) +{ + int ret, val, val2; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_WHO_AM_I); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading who_am_i\n"); + return ret; + } + + if (ret != KMX61_CHIP_ID) { + dev_err(&data->client->dev, + "Wrong chip id, got %x expected %x\n", + ret, KMX61_CHIP_ID); + return -EINVAL; + } + + /* set accel 12bit, 4g range */ + ret = kmx61_set_range(data, KMX61_RANGE_4G); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_ODCNTL); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_odcntl\n"); + return ret; + } + data->odr_bits = ret; + + /* set output data rate for wake up (motion detection) function */ + ret = kmx61_convert_bit_to_freq(data->odr_bits, &val, &val2); + if (ret < 0) + return ret; + + ret = kmx61_set_wake_up_odr(data, val, val2); + if (ret < 0) + return ret; + + /* set acc/magn to OPERATION mode */ + ret = kmx61_set_mode(data, 0, KMX61_ACC | KMX61_MAG, true); + if (ret < 0) + return ret; + + data->wake_thresh = KMX61_DEFAULT_WAKE_THRESH; + data->wake_duration = KMX61_DEFAULT_WAKE_DURATION; + + return 0; +} + +static int kmx61_setup_new_data_interrupt(struct kmx61_data *data, + bool status, u8 device) +{ + u8 mode; + int ret; + + ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG); + if (ret < 0) + return ret; + + ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INC1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + return ret; + } + + if (status) { + ret |= KMX61_REG_INC1_BIT_IEN; + if (device & KMX61_ACC) + ret |= KMX61_REG_INC1_BIT_DRDYA; + if (device & KMX61_MAG) + ret |= KMX61_REG_INC1_BIT_DRDYM; + } else { + ret &= ~KMX61_REG_INC1_BIT_IEN; + if (device & KMX61_ACC) + ret &= ~KMX61_REG_INC1_BIT_DRDYA; + if (device & KMX61_MAG) + ret &= ~KMX61_REG_INC1_BIT_DRDYM; + } + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_INC1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); + return ret; + } + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + return ret; + } + + if (status) + ret |= KMX61_REG_CTRL1_BIT_DRDYE; + else + ret &= ~KMX61_REG_CTRL1_BIT_DRDYE; + + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); + return ret; + } + + return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true); +} + +static int kmx61_chip_update_thresholds(struct kmx61_data *data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, + KMX61_REG_WUF_TIMER, + data->wake_duration); + if (ret < 0) { + dev_err(&data->client->dev, "Errow writing reg_wuf_timer\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(data->client, + KMX61_REG_WUF_THRESH, + data->wake_thresh); + if (ret < 0) + dev_err(&data->client->dev, "Error writing reg_wuf_thresh\n"); + + return ret; +} + +static int kmx61_setup_any_motion_interrupt(struct kmx61_data *data, + bool status) +{ + u8 mode; + int ret; + + ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG); + if (ret < 0) + return ret; + + ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); + if (ret < 0) + return ret; + + ret = kmx61_chip_update_thresholds(data); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INC1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_inc1\n"); + return ret; + } + if (status) + ret |= (KMX61_REG_INC1_BIT_IEN | KMX61_REG_INC1_BIT_WUFS); + else + ret &= ~(KMX61_REG_INC1_BIT_IEN | KMX61_REG_INC1_BIT_WUFS); + + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_INC1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_inc1\n"); + return ret; + } + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + return ret; + } + + if (status) + ret |= KMX61_REG_CTRL1_BIT_WUFE | KMX61_REG_CTRL1_BIT_BTSE; + else + ret &= ~(KMX61_REG_CTRL1_BIT_WUFE | KMX61_REG_CTRL1_BIT_BTSE); + + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); + return ret; + } + mode |= KMX61_ACT_STBY_BIT; + return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true); +} + +/** + * kmx61_set_power_state() - set power state for kmx61 @device + * @data - kmx61 device private pointer + * @on - power state to be set for @device + * @device - bitmask indicating device for which @on state needs to be set + * + * Notice that when ACC power state needs to be set to ON and MAG is in + * OPERATION then we know that kmx61_runtime_resume was already called + * so we must set ACC OPERATION mode here. The same happens when MAG power + * state needs to be set to ON and ACC is in OPERATION. + */ +static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device) +{ +#ifdef CONFIG_PM + int ret; + + if (device & KMX61_ACC) { + if (on && !data->acc_ps && !data->mag_stby) { + ret = kmx61_set_mode(data, 0, KMX61_ACC, true); + if (ret < 0) + return ret; + } + data->acc_ps = on; + } + if (device & KMX61_MAG) { + if (on && !data->mag_ps && !data->acc_stby) { + ret = kmx61_set_mode(data, 0, KMX61_MAG, true); + if (ret < 0) + return ret; + } + data->mag_ps = on; + } + + if (on) { + ret = pm_runtime_get_sync(&data->client->dev); + } else { + pm_runtime_mark_last_busy(&data->client->dev); + ret = pm_runtime_put_autosuspend(&data->client->dev); + } + if (ret < 0) { + dev_err(&data->client->dev, + "Failed: kmx61_set_power_state for %d, ret %d\n", + on, ret); + if (on) + pm_runtime_put_noidle(&data->client->dev); + + return ret; + } +#endif + return 0; +} + +static int kmx61_read_measurement(struct kmx61_data *data, u8 base, u8 offset) +{ + int ret; + u8 reg = base + offset * 2; + + ret = i2c_smbus_read_word_data(data->client, reg); + if (ret < 0) + dev_err(&data->client->dev, "failed to read reg at %x\n", reg); + + return ret; +} + +static int kmx61_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + u8 base_reg; + struct kmx61_data *data = kmx61_get_data(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_ACCEL: + base_reg = KMX61_ACC_XOUT_L; + break; + case IIO_MAGN: + base_reg = KMX61_MAG_XOUT_L; + break; + default: + return -EINVAL; + } + mutex_lock(&data->lock); + + ret = kmx61_set_power_state(data, true, chan->address); + if (ret) { + mutex_unlock(&data->lock); + return ret; + } + + ret = kmx61_read_measurement(data, base_reg, chan->scan_index); + if (ret < 0) { + kmx61_set_power_state(data, false, chan->address); + mutex_unlock(&data->lock); + return ret; + } + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + ret = kmx61_set_power_state(data, false, chan->address); + + mutex_unlock(&data->lock); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + *val = 0; + *val2 = kmx61_uscale_table[data->range]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_MAGN: + /* 14 bits res, 1465 microGauss per magn count */ + *val = 0; + *val2 = 1465; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN) + return -EINVAL; + + mutex_lock(&data->lock); + ret = kmx61_get_odr(data, val, val2, chan->address); + mutex_unlock(&data->lock); + if (ret) + return -EINVAL; + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static int kmx61_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + int ret; + struct kmx61_data *data = kmx61_get_data(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN) + return -EINVAL; + + mutex_lock(&data->lock); + ret = kmx61_set_odr(data, val, val2, chan->address); + mutex_unlock(&data->lock); + return ret; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + if (val != 0) + return -EINVAL; + mutex_lock(&data->lock); + ret = kmx61_set_scale(data, val2); + mutex_unlock(&data->lock); + return ret; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int kmx61_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct kmx61_data *data = kmx61_get_data(indio_dev); + + *val2 = 0; + switch (info) { + case IIO_EV_INFO_VALUE: + *val = data->wake_thresh; + return IIO_VAL_INT; + case IIO_EV_INFO_PERIOD: + *val = data->wake_duration; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int kmx61_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct kmx61_data *data = kmx61_get_data(indio_dev); + + if (data->ev_enable_state) + return -EBUSY; + + switch (info) { + case IIO_EV_INFO_VALUE: + data->wake_thresh = val; + return IIO_VAL_INT; + case IIO_EV_INFO_PERIOD: + data->wake_duration = val; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int kmx61_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct kmx61_data *data = kmx61_get_data(indio_dev); + + return data->ev_enable_state; +} + +static int kmx61_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct kmx61_data *data = kmx61_get_data(indio_dev); + int ret = 0; + + if (state && data->ev_enable_state) + return 0; + + mutex_lock(&data->lock); + + if (!state && data->motion_trig_on) { + data->ev_enable_state = false; + goto err_unlock; + } + + ret = kmx61_set_power_state(data, state, KMX61_ACC); + if (ret < 0) + goto err_unlock; + + ret = kmx61_setup_any_motion_interrupt(data, state); + if (ret < 0) { + kmx61_set_power_state(data, false, KMX61_ACC); + goto err_unlock; + } + + data->ev_enable_state = state; + +err_unlock: + mutex_unlock(&data->lock); + + return ret; +} + +static int kmx61_acc_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct kmx61_data *data = kmx61_get_data(indio_dev); + + if (data->acc_dready_trig != trig && data->motion_trig != trig) + return -EINVAL; + + return 0; +} + +static int kmx61_mag_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct kmx61_data *data = kmx61_get_data(indio_dev); + + if (data->mag_dready_trig != trig) + return -EINVAL; + + return 0; +} + +static const struct iio_info kmx61_acc_info = { + .driver_module = THIS_MODULE, + .read_raw = kmx61_read_raw, + .write_raw = kmx61_write_raw, + .attrs = &kmx61_acc_attribute_group, + .read_event_value = kmx61_read_event, + .write_event_value = kmx61_write_event, + .read_event_config = kmx61_read_event_config, + .write_event_config = kmx61_write_event_config, + .validate_trigger = kmx61_acc_validate_trigger, +}; + +static const struct iio_info kmx61_mag_info = { + .driver_module = THIS_MODULE, + .read_raw = kmx61_read_raw, + .write_raw = kmx61_write_raw, + .attrs = &kmx61_mag_attribute_group, + .validate_trigger = kmx61_mag_validate_trigger, +}; + + +static int kmx61_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + int ret = 0; + u8 device; + + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct kmx61_data *data = kmx61_get_data(indio_dev); + + mutex_lock(&data->lock); + + if (!state && data->ev_enable_state && data->motion_trig_on) { + data->motion_trig_on = false; + goto err_unlock; + } + + if (data->acc_dready_trig == trig || data->motion_trig == trig) + device = KMX61_ACC; + else + device = KMX61_MAG; + + ret = kmx61_set_power_state(data, state, device); + if (ret < 0) + goto err_unlock; + + if (data->acc_dready_trig == trig || data->mag_dready_trig == trig) + ret = kmx61_setup_new_data_interrupt(data, state, device); + else + ret = kmx61_setup_any_motion_interrupt(data, state); + if (ret < 0) { + kmx61_set_power_state(data, false, device); + goto err_unlock; + } + + if (data->acc_dready_trig == trig) + data->acc_dready_trig_on = state; + else if (data->mag_dready_trig == trig) + data->mag_dready_trig_on = state; + else + data->motion_trig_on = state; +err_unlock: + mutex_unlock(&data->lock); + + return ret; +} + +static int kmx61_trig_try_reenable(struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct kmx61_data *data = kmx61_get_data(indio_dev); + int ret; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INL); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_inl\n"); + return ret; + } + + return 0; +} + +static const struct iio_trigger_ops kmx61_trigger_ops = { + .set_trigger_state = kmx61_data_rdy_trigger_set_state, + .try_reenable = kmx61_trig_try_reenable, + .owner = THIS_MODULE, +}; + +static irqreturn_t kmx61_event_handler(int irq, void *private) +{ + struct kmx61_data *data = private; + struct iio_dev *indio_dev = data->acc_indio_dev; + int ret; + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INS1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ins1\n"); + goto ack_intr; + } + + if (ret & KMX61_REG_INS1_BIT_WUFS) { + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INS2); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ins2\n"); + goto ack_intr; + } + + if (ret & KMX61_REG_INS2_BIT_XN) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + 0); + + if (ret & KMX61_REG_INS2_BIT_XP) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + 0); + + if (ret & KMX61_REG_INS2_BIT_YN) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + 0); + + if (ret & KMX61_REG_INS2_BIT_YP) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + 0); + + if (ret & KMX61_REG_INS2_BIT_ZN) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + 0); + + if (ret & KMX61_REG_INS2_BIT_ZP) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + 0); + } + +ack_intr: + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1); + if (ret < 0) + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + + ret |= KMX61_REG_CTRL1_BIT_RES; + ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret); + if (ret < 0) + dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); + + ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INL); + if (ret < 0) + dev_err(&data->client->dev, "Error reading reg_inl\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t kmx61_data_rdy_trig_poll(int irq, void *private) +{ + struct kmx61_data *data = private; + + if (data->acc_dready_trig_on) + iio_trigger_poll(data->acc_dready_trig); + if (data->mag_dready_trig_on) + iio_trigger_poll(data->mag_dready_trig); + + if (data->motion_trig_on) + iio_trigger_poll(data->motion_trig); + + if (data->ev_enable_state) + return IRQ_WAKE_THREAD; + return IRQ_HANDLED; +} + +static irqreturn_t kmx61_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct kmx61_data *data = kmx61_get_data(indio_dev); + int bit, ret, i = 0; + u8 base; + s16 buffer[8]; + + if (indio_dev == data->acc_indio_dev) + base = KMX61_ACC_XOUT_L; + else + base = KMX61_MAG_XOUT_L; + + mutex_lock(&data->lock); + for_each_set_bit(bit, indio_dev->buffer->scan_mask, + indio_dev->masklength) { + ret = kmx61_read_measurement(data, base, bit); + if (ret < 0) { + mutex_unlock(&data->lock); + goto err; + } + buffer[i++] = ret; + } + mutex_unlock(&data->lock); + + iio_push_to_buffers(indio_dev, buffer); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const char *kmx61_match_acpi_device(struct device *dev) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return NULL; + return dev_name(dev); +} + +static int kmx61_gpio_probe(struct i2c_client *client, struct kmx61_data *data) +{ + struct device *dev; + struct gpio_desc *gpio; + int ret; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* data ready gpio interrupt pin */ + gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0); + if (IS_ERR(gpio)) { + dev_err(dev, "acpi gpio get index failed\n"); + return PTR_ERR(gpio); + } + + ret = gpiod_direction_input(gpio); + if (ret) + return ret; + + ret = gpiod_to_irq(gpio); + + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + return ret; +} + +static struct iio_dev *kmx61_indiodev_setup(struct kmx61_data *data, + const struct iio_info *info, + const struct iio_chan_spec *chan, + int num_channels, + const char *name) +{ + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&data->client->dev, sizeof(data)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + + kmx61_set_data(indio_dev, data); + + indio_dev->dev.parent = &data->client->dev; + indio_dev->channels = chan; + indio_dev->num_channels = num_channels; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = info; + + return indio_dev; +} + +static struct iio_trigger *kmx61_trigger_setup(struct kmx61_data *data, + struct iio_dev *indio_dev, + const char *tag) +{ + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&data->client->dev, + "%s-%s-dev%d", + indio_dev->name, + tag, + indio_dev->id); + if (!trig) + return ERR_PTR(-ENOMEM); + + trig->dev.parent = &data->client->dev; + trig->ops = &kmx61_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + + ret = iio_trigger_register(trig); + if (ret) + return ERR_PTR(ret); + + return trig; +} + +static int kmx61_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct kmx61_data *data; + const char *name = NULL; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + + mutex_init(&data->lock); + + if (id) + name = id->name; + else if (ACPI_HANDLE(&client->dev)) + name = kmx61_match_acpi_device(&client->dev); + else + return -ENODEV; + + data->acc_indio_dev = + kmx61_indiodev_setup(data, &kmx61_acc_info, + kmx61_acc_channels, + ARRAY_SIZE(kmx61_acc_channels), + name); + if (IS_ERR(data->acc_indio_dev)) + return PTR_ERR(data->acc_indio_dev); + + data->mag_indio_dev = + kmx61_indiodev_setup(data, &kmx61_mag_info, + kmx61_mag_channels, + ARRAY_SIZE(kmx61_mag_channels), + name); + if (IS_ERR(data->mag_indio_dev)) + return PTR_ERR(data->mag_indio_dev); + + ret = kmx61_chip_init(data); + if (ret < 0) + return ret; + + if (client->irq < 0) + client->irq = kmx61_gpio_probe(client, data); + + if (client->irq >= 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + kmx61_data_rdy_trig_poll, + kmx61_event_handler, + IRQF_TRIGGER_RISING, + KMX61_IRQ_NAME, + data); + if (ret) + goto err_chip_uninit; + + data->acc_dready_trig = + kmx61_trigger_setup(data, data->acc_indio_dev, + "dready"); + if (IS_ERR(data->acc_dready_trig)) { + ret = PTR_ERR(data->acc_dready_trig); + goto err_chip_uninit; + } + + data->mag_dready_trig = + kmx61_trigger_setup(data, data->mag_indio_dev, + "dready"); + if (IS_ERR(data->mag_dready_trig)) { + ret = PTR_ERR(data->mag_dready_trig); + goto err_trigger_unregister_acc_dready; + } + + data->motion_trig = + kmx61_trigger_setup(data, data->acc_indio_dev, + "any-motion"); + if (IS_ERR(data->motion_trig)) { + ret = PTR_ERR(data->motion_trig); + goto err_trigger_unregister_mag_dready; + } + + ret = iio_triggered_buffer_setup(data->acc_indio_dev, + &iio_pollfunc_store_time, + kmx61_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&data->client->dev, + "Failed to setup acc triggered buffer\n"); + goto err_trigger_unregister_motion; + } + + ret = iio_triggered_buffer_setup(data->mag_indio_dev, + &iio_pollfunc_store_time, + kmx61_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&data->client->dev, + "Failed to setup mag triggered buffer\n"); + goto err_buffer_cleanup_acc; + } + } + + ret = iio_device_register(data->acc_indio_dev); + if (ret < 0) { + dev_err(&client->dev, "Failed to register acc iio device\n"); + goto err_buffer_cleanup_mag; + } + + ret = iio_device_register(data->mag_indio_dev); + if (ret < 0) { + dev_err(&client->dev, "Failed to register mag iio device\n"); + goto err_iio_unregister_acc; + } + + ret = pm_runtime_set_active(&client->dev); + if (ret < 0) + goto err_iio_unregister_mag; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + return 0; + +err_iio_unregister_mag: + iio_device_unregister(data->mag_indio_dev); +err_iio_unregister_acc: + iio_device_unregister(data->acc_indio_dev); +err_buffer_cleanup_mag: + if (client->irq >= 0) + iio_triggered_buffer_cleanup(data->mag_indio_dev); +err_buffer_cleanup_acc: + if (client->irq >= 0) + iio_triggered_buffer_cleanup(data->acc_indio_dev); +err_trigger_unregister_motion: + iio_trigger_unregister(data->motion_trig); +err_trigger_unregister_mag_dready: + iio_trigger_unregister(data->mag_dready_trig); +err_trigger_unregister_acc_dready: + iio_trigger_unregister(data->acc_dready_trig); +err_chip_uninit: + kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); + return ret; +} + +static int kmx61_remove(struct i2c_client *client) +{ + struct kmx61_data *data = i2c_get_clientdata(client); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + iio_device_unregister(data->acc_indio_dev); + iio_device_unregister(data->mag_indio_dev); + + if (client->irq >= 0) { + iio_triggered_buffer_cleanup(data->acc_indio_dev); + iio_triggered_buffer_cleanup(data->mag_indio_dev); + iio_trigger_unregister(data->acc_dready_trig); + iio_trigger_unregister(data->mag_dready_trig); + iio_trigger_unregister(data->motion_trig); + } + + mutex_lock(&data->lock); + kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); + mutex_unlock(&data->lock); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int kmx61_suspend(struct device *dev) +{ + int ret; + struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + mutex_lock(&data->lock); + ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, + false); + mutex_unlock(&data->lock); + + return ret; +} + +static int kmx61_resume(struct device *dev) +{ + u8 stby = 0; + struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + if (data->acc_stby) + stby |= KMX61_ACC_STBY_BIT; + if (data->mag_stby) + stby |= KMX61_MAG_STBY_BIT; + + return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true); +} +#endif + +#ifdef CONFIG_PM +static int kmx61_runtime_suspend(struct device *dev) +{ + struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); + int ret; + + mutex_lock(&data->lock); + ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); + mutex_unlock(&data->lock); + + return ret; +} + +static int kmx61_runtime_resume(struct device *dev) +{ + struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); + u8 stby = 0; + + if (!data->acc_ps) + stby |= KMX61_ACC_STBY_BIT; + if (!data->mag_ps) + stby |= KMX61_MAG_STBY_BIT; + + return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true); +} +#endif + +static const struct dev_pm_ops kmx61_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(kmx61_suspend, kmx61_resume) + SET_RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL) +}; + +static const struct acpi_device_id kmx61_acpi_match[] = { + {"KMX61021", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, kmx61_acpi_match); + +static const struct i2c_device_id kmx61_id[] = { + {"kmx611021", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, kmx61_id); + +static struct i2c_driver kmx61_driver = { + .driver = { + .name = KMX61_DRV_NAME, + .acpi_match_table = ACPI_PTR(kmx61_acpi_match), + .pm = &kmx61_pm_ops, + }, + .probe = kmx61_probe, + .remove = kmx61_remove, + .id_table = kmx61_id, +}; + +module_i2c_driver(kmx61_driver); + +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); +MODULE_DESCRIPTION("KMX61 accelerometer/magnetometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index f971f79103ec..403b72878b0b 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -178,6 +178,80 @@ static ssize_t iio_scan_el_show(struct device *dev, return sprintf(buf, "%d\n", ret); } +/* Note NULL used as error indicator as it doesn't make sense. */ +static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks, + unsigned int masklength, + const unsigned long *mask) +{ + if (bitmap_empty(mask, masklength)) + return NULL; + while (*av_masks) { + if (bitmap_subset(mask, av_masks, masklength)) + return av_masks; + av_masks += BITS_TO_LONGS(masklength); + } + return NULL; +} + +static bool iio_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + if (!indio_dev->setup_ops->validate_scan_mask) + return true; + + return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask); +} + +/** + * iio_scan_mask_set() - set particular bit in the scan mask + * @indio_dev: the iio device + * @buffer: the buffer whose scan mask we are interested in + * @bit: the bit to be set. + * + * Note that at this point we have no way of knowing what other + * buffers might request, hence this code only verifies that the + * individual buffers request is plausible. + */ +static int iio_scan_mask_set(struct iio_dev *indio_dev, + struct iio_buffer *buffer, int bit) +{ + const unsigned long *mask; + unsigned long *trialmask; + + trialmask = kmalloc(sizeof(*trialmask)* + BITS_TO_LONGS(indio_dev->masklength), + GFP_KERNEL); + + if (trialmask == NULL) + return -ENOMEM; + if (!indio_dev->masklength) { + WARN_ON("Trying to set scanmask prior to registering buffer\n"); + goto err_invalid_mask; + } + bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength); + set_bit(bit, trialmask); + + if (!iio_validate_scan_mask(indio_dev, trialmask)) + goto err_invalid_mask; + + if (indio_dev->available_scan_masks) { + mask = iio_scan_mask_match(indio_dev->available_scan_masks, + indio_dev->masklength, + trialmask); + if (!mask) + goto err_invalid_mask; + } + bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength); + + kfree(trialmask); + + return 0; + +err_invalid_mask: + kfree(trialmask); + return -EINVAL; +} + static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit) { clear_bit(bit, buffer->scan_mask); @@ -309,115 +383,19 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, return ret; } -static const char * const iio_scan_elements_group_name = "scan_elements"; - -int iio_buffer_register(struct iio_dev *indio_dev, - const struct iio_chan_spec *channels, - int num_channels) -{ - struct iio_dev_attr *p; - struct attribute **attr; - struct iio_buffer *buffer = indio_dev->buffer; - int ret, i, attrn, attrcount, attrcount_orig = 0; - - if (buffer->attrs) - indio_dev->groups[indio_dev->groupcounter++] = buffer->attrs; - - if (buffer->scan_el_attrs != NULL) { - attr = buffer->scan_el_attrs->attrs; - while (*attr++ != NULL) - attrcount_orig++; - } - attrcount = attrcount_orig; - INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list); - if (channels) { - /* new magic */ - for (i = 0; i < num_channels; i++) { - if (channels[i].scan_index < 0) - continue; - - /* Establish necessary mask length */ - if (channels[i].scan_index > - (int)indio_dev->masklength - 1) - indio_dev->masklength - = channels[i].scan_index + 1; - - ret = iio_buffer_add_channel_sysfs(indio_dev, - &channels[i]); - if (ret < 0) - goto error_cleanup_dynamic; - attrcount += ret; - if (channels[i].type == IIO_TIMESTAMP) - indio_dev->scan_index_timestamp = - channels[i].scan_index; - } - if (indio_dev->masklength && buffer->scan_mask == NULL) { - buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), - sizeof(*buffer->scan_mask), - GFP_KERNEL); - if (buffer->scan_mask == NULL) { - ret = -ENOMEM; - goto error_cleanup_dynamic; - } - } - } - - buffer->scan_el_group.name = iio_scan_elements_group_name; - - buffer->scan_el_group.attrs = kcalloc(attrcount + 1, - sizeof(buffer->scan_el_group.attrs[0]), - GFP_KERNEL); - if (buffer->scan_el_group.attrs == NULL) { - ret = -ENOMEM; - goto error_free_scan_mask; - } - if (buffer->scan_el_attrs) - memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs, - sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig); - attrn = attrcount_orig; - - list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) - buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr; - indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group; - - return 0; - -error_free_scan_mask: - kfree(buffer->scan_mask); -error_cleanup_dynamic: - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); - - return ret; -} -EXPORT_SYMBOL(iio_buffer_register); - -void iio_buffer_unregister(struct iio_dev *indio_dev) -{ - kfree(indio_dev->buffer->scan_mask); - kfree(indio_dev->buffer->scan_el_group.attrs); - iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list); -} -EXPORT_SYMBOL(iio_buffer_unregister); - -ssize_t iio_buffer_read_length(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t iio_buffer_read_length(struct device *dev, + struct device_attribute *attr, + char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_buffer *buffer = indio_dev->buffer; - if (buffer->access->get_length) - return sprintf(buf, "%d\n", - buffer->access->get_length(buffer)); - - return 0; + return sprintf(buf, "%d\n", buffer->length); } -EXPORT_SYMBOL(iio_buffer_read_length); -ssize_t iio_buffer_write_length(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) +static ssize_t iio_buffer_write_length(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_buffer *buffer = indio_dev->buffer; @@ -428,47 +406,28 @@ ssize_t iio_buffer_write_length(struct device *dev, if (ret) return ret; - if (buffer->access->get_length) - if (val == buffer->access->get_length(buffer)) - return len; + if (val == buffer->length) + return len; mutex_lock(&indio_dev->mlock); if (iio_buffer_is_active(indio_dev->buffer)) { ret = -EBUSY; } else { - if (buffer->access->set_length) - buffer->access->set_length(buffer, val); + buffer->access->set_length(buffer, val); ret = 0; } mutex_unlock(&indio_dev->mlock); return ret ? ret : len; } -EXPORT_SYMBOL(iio_buffer_write_length); -ssize_t iio_buffer_show_enable(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t iio_buffer_show_enable(struct device *dev, + struct device_attribute *attr, + char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer)); } -EXPORT_SYMBOL(iio_buffer_show_enable); - -/* Note NULL used as error indicator as it doesn't make sense. */ -static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks, - unsigned int masklength, - const unsigned long *mask) -{ - if (bitmap_empty(mask, masklength)) - return NULL; - while (*av_masks) { - if (bitmap_subset(mask, av_masks, masklength)) - return av_masks; - av_masks += BITS_TO_LONGS(masklength); - } - return NULL; -} static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const unsigned long *mask, bool timestamp) @@ -755,10 +714,10 @@ out_unlock: } EXPORT_SYMBOL_GPL(iio_update_buffers); -ssize_t iio_buffer_store_enable(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) +static ssize_t iio_buffer_store_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) { int ret; bool requested_state; @@ -790,83 +749,146 @@ done: mutex_unlock(&indio_dev->mlock); return (ret < 0) ? ret : len; } -EXPORT_SYMBOL(iio_buffer_store_enable); -/** - * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected - * @indio_dev: the iio device - * @mask: scan mask to be checked - * - * Return true if exactly one bit is set in the scan mask, false otherwise. It - * can be used for devices where only one channel can be active for sampling at - * a time. - */ -bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, - const unsigned long *mask) -{ - return bitmap_weight(mask, indio_dev->masklength) == 1; -} -EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot); - -static bool iio_validate_scan_mask(struct iio_dev *indio_dev, - const unsigned long *mask) -{ - if (!indio_dev->setup_ops->validate_scan_mask) - return true; +static const char * const iio_scan_elements_group_name = "scan_elements"; - return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask); -} +static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length, + iio_buffer_write_length); +static struct device_attribute dev_attr_length_ro = __ATTR(length, + S_IRUGO, iio_buffer_read_length, NULL); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, + iio_buffer_show_enable, iio_buffer_store_enable); -/** - * iio_scan_mask_set() - set particular bit in the scan mask - * @indio_dev: the iio device - * @buffer: the buffer whose scan mask we are interested in - * @bit: the bit to be set. - * - * Note that at this point we have no way of knowing what other - * buffers might request, hence this code only verifies that the - * individual buffers request is plausible. - */ -int iio_scan_mask_set(struct iio_dev *indio_dev, - struct iio_buffer *buffer, int bit) +int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { - const unsigned long *mask; - unsigned long *trialmask; + struct iio_dev_attr *p; + struct attribute **attr; + struct iio_buffer *buffer = indio_dev->buffer; + int ret, i, attrn, attrcount, attrcount_orig = 0; + const struct iio_chan_spec *channels; - trialmask = kmalloc(sizeof(*trialmask)* - BITS_TO_LONGS(indio_dev->masklength), - GFP_KERNEL); + if (!buffer) + return 0; - if (trialmask == NULL) + attrcount = 0; + if (buffer->attrs) { + while (buffer->attrs[attrcount] != NULL) + attrcount++; + } + + buffer->buffer_group.name = "buffer"; + buffer->buffer_group.attrs = kcalloc(attrcount + 3, + sizeof(*buffer->buffer_group.attrs), GFP_KERNEL); + if (!buffer->buffer_group.attrs) return -ENOMEM; - if (!indio_dev->masklength) { - WARN_ON("Trying to set scanmask prior to registering buffer\n"); - goto err_invalid_mask; + + if (buffer->access->set_length) + buffer->buffer_group.attrs[0] = &dev_attr_length.attr; + else + buffer->buffer_group.attrs[0] = &dev_attr_length_ro.attr; + buffer->buffer_group.attrs[1] = &dev_attr_enable.attr; + if (buffer->attrs) + memcpy(&buffer->buffer_group.attrs[2], buffer->attrs, + sizeof(*&buffer->buffer_group.attrs) * attrcount); + buffer->buffer_group.attrs[attrcount+2] = NULL; + + indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group; + + if (buffer->scan_el_attrs != NULL) { + attr = buffer->scan_el_attrs->attrs; + while (*attr++ != NULL) + attrcount_orig++; } - bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength); - set_bit(bit, trialmask); + attrcount = attrcount_orig; + INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list); + channels = indio_dev->channels; + if (channels) { + /* new magic */ + for (i = 0; i < indio_dev->num_channels; i++) { + if (channels[i].scan_index < 0) + continue; - if (!iio_validate_scan_mask(indio_dev, trialmask)) - goto err_invalid_mask; + /* Establish necessary mask length */ + if (channels[i].scan_index > + (int)indio_dev->masklength - 1) + indio_dev->masklength + = channels[i].scan_index + 1; - if (indio_dev->available_scan_masks) { - mask = iio_scan_mask_match(indio_dev->available_scan_masks, - indio_dev->masklength, - trialmask); - if (!mask) - goto err_invalid_mask; + ret = iio_buffer_add_channel_sysfs(indio_dev, + &channels[i]); + if (ret < 0) + goto error_cleanup_dynamic; + attrcount += ret; + if (channels[i].type == IIO_TIMESTAMP) + indio_dev->scan_index_timestamp = + channels[i].scan_index; + } + if (indio_dev->masklength && buffer->scan_mask == NULL) { + buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), + sizeof(*buffer->scan_mask), + GFP_KERNEL); + if (buffer->scan_mask == NULL) { + ret = -ENOMEM; + goto error_cleanup_dynamic; + } + } } - bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength); - kfree(trialmask); + buffer->scan_el_group.name = iio_scan_elements_group_name; + + buffer->scan_el_group.attrs = kcalloc(attrcount + 1, + sizeof(buffer->scan_el_group.attrs[0]), + GFP_KERNEL); + if (buffer->scan_el_group.attrs == NULL) { + ret = -ENOMEM; + goto error_free_scan_mask; + } + if (buffer->scan_el_attrs) + memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs, + sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig); + attrn = attrcount_orig; + + list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) + buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr; + indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group; return 0; -err_invalid_mask: - kfree(trialmask); - return -EINVAL; +error_free_scan_mask: + kfree(buffer->scan_mask); +error_cleanup_dynamic: + iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); + kfree(indio_dev->buffer->buffer_group.attrs); + + return ret; +} + +void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) +{ + if (!indio_dev->buffer) + return; + + kfree(indio_dev->buffer->scan_mask); + kfree(indio_dev->buffer->buffer_group.attrs); + kfree(indio_dev->buffer->scan_el_group.attrs); + iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list); +} + +/** + * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected + * @indio_dev: the iio device + * @mask: scan mask to be checked + * + * Return true if exactly one bit is set in the scan mask, false otherwise. It + * can be used for devices where only one channel can be active for sampling at + * a time. + */ +bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + return bitmap_weight(mask, indio_dev->masklength) == 1; } -EXPORT_SYMBOL_GPL(iio_scan_mask_set); +EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot); int iio_scan_mask_query(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index af3e76d652ba..69feb912515a 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -70,6 +70,8 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_CCT] = "cct", [IIO_PRESSURE] = "pressure", [IIO_HUMIDITYRELATIVE] = "humidityrelative", + [IIO_ACTIVITY] = "activity", + [IIO_STEPS] = "steps", }; static const char * const iio_modifier_names[] = { @@ -91,6 +93,10 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_NORTH_TRUE] = "from_north_true", [IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp", [IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp", + [IIO_MOD_RUNNING] = "running", + [IIO_MOD_JOGGING] = "jogging", + [IIO_MOD_WALKING] = "walking", + [IIO_MOD_STILL] = "still", }; /* relies on pairs of these shared then separate */ @@ -113,6 +119,8 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain", [IIO_CHAN_INFO_HYSTERESIS] = "hysteresis", [IIO_CHAN_INFO_INT_TIME] = "integration_time", + [IIO_CHAN_INFO_ENABLE] = "en", + [IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight", }; /** @@ -1035,7 +1043,6 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv) if (!ptr) return NULL; - /* use raw alloc_dr for kmalloc caller tracing */ iio_dev = iio_device_alloc(sizeof_priv); if (iio_dev) { *ptr = iio_dev; @@ -1127,6 +1134,29 @@ static const struct file_operations iio_buffer_fileops = { .compat_ioctl = iio_ioctl, }; +static int iio_check_unique_scan_index(struct iio_dev *indio_dev) +{ + int i, j; + const struct iio_chan_spec *channels = indio_dev->channels; + + if (!(indio_dev->modes & INDIO_ALL_BUFFER_MODES)) + return 0; + + for (i = 0; i < indio_dev->num_channels - 1; i++) { + if (channels[i].scan_index < 0) + continue; + for (j = i + 1; j < indio_dev->num_channels; j++) + if (channels[i].scan_index == channels[j].scan_index) { + dev_err(&indio_dev->dev, + "Duplicate scan index %d\n", + channels[i].scan_index); + return -EINVAL; + } + } + + return 0; +} + static const struct iio_buffer_setup_ops noop_ring_setup_ops; /** @@ -1141,6 +1171,10 @@ int iio_device_register(struct iio_dev *indio_dev) if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; + ret = iio_check_unique_scan_index(indio_dev); + if (ret < 0) + return ret; + /* configure elements for the chrdev */ indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); @@ -1150,11 +1184,19 @@ int iio_device_register(struct iio_dev *indio_dev) "Failed to register debugfs interfaces\n"); return ret; } + + ret = iio_buffer_alloc_sysfs_and_mask(indio_dev); + if (ret) { + dev_err(indio_dev->dev.parent, + "Failed to create buffer sysfs interfaces\n"); + goto error_unreg_debugfs; + } + ret = iio_device_register_sysfs(indio_dev); if (ret) { dev_err(indio_dev->dev.parent, "Failed to register sysfs interfaces\n"); - goto error_unreg_debugfs; + goto error_buffer_free_sysfs; } ret = iio_device_register_eventset(indio_dev); if (ret) { @@ -1187,6 +1229,8 @@ error_unreg_eventset: iio_device_unregister_eventset(indio_dev); error_free_sysfs: iio_device_unregister_sysfs(indio_dev); +error_buffer_free_sysfs: + iio_buffer_free_sysfs_and_mask(indio_dev); error_unreg_debugfs: iio_device_unregister_debugfs(indio_dev); return ret; @@ -1215,6 +1259,8 @@ void iio_device_unregister(struct iio_dev *indio_dev) iio_buffer_wakeup_poll(indio_dev); mutex_unlock(&indio_dev->info_exist_lock); + + iio_buffer_free_sysfs_and_mask(indio_dev); } EXPORT_SYMBOL(iio_device_unregister); diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 0c1e37e3120a..3f5cee0295c5 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -197,6 +197,7 @@ static const char * const iio_ev_type_text[] = { [IIO_EV_TYPE_ROC] = "roc", [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive", [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", + [IIO_EV_TYPE_INSTANCE] = "instance", }; static const char * const iio_ev_dir_text[] = { @@ -327,9 +328,15 @@ static int iio_device_add_event(struct iio_dev *indio_dev, for_each_set_bit(i, mask, sizeof(*mask)*8) { if (i >= ARRAY_SIZE(iio_ev_info_text)) return -EINVAL; - postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_ev_type_text[type], iio_ev_dir_text[dir], - iio_ev_info_text[i]); + if (dir != IIO_EV_DIR_NONE) + postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", + iio_ev_type_text[type], + iio_ev_dir_text[dir], + iio_ev_info_text[i]); + else + postfix = kasprintf(GFP_KERNEL, "%s_%s", + iio_ev_type_text[type], + iio_ev_info_text[i]); if (postfix == NULL) return -ENOMEM; diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c index d6f54930b34a..15a5341b5e7b 100644 --- a/drivers/iio/industrialio-triggered-buffer.c +++ b/drivers/iio/industrialio-triggered-buffer.c @@ -32,7 +32,7 @@ static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { * * This function combines some common tasks which will normally be performed * when setting up a triggered buffer. It will allocate the buffer and the - * pollfunc, as well as register the buffer with the IIO core. + * pollfunc. * * Before calling this function the indio_dev structure should already be * completely initialized, but not yet registered. In practice this means that @@ -49,7 +49,7 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev, struct iio_buffer *buffer; int ret; - buffer = iio_kfifo_allocate(indio_dev); + buffer = iio_kfifo_allocate(); if (!buffer) { ret = -ENOMEM; goto error_ret; @@ -78,16 +78,8 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev, /* Flag that polled ring buffering is possible */ indio_dev->modes |= INDIO_BUFFER_TRIGGERED; - ret = iio_buffer_register(indio_dev, - indio_dev->channels, - indio_dev->num_channels); - if (ret) - goto error_dealloc_pollfunc; - return 0; -error_dealloc_pollfunc: - iio_dealloc_pollfunc(indio_dev->pollfunc); error_kfifo_free: iio_kfifo_free(indio_dev->buffer); error_ret: @@ -101,7 +93,6 @@ EXPORT_SYMBOL(iio_triggered_buffer_setup); */ void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev) { - iio_buffer_unregister(indio_dev); iio_dealloc_pollfunc(indio_dev->pollfunc); iio_kfifo_free(indio_dev->buffer); } diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 90c8cb727cc7..c8bad3cf891d 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -116,8 +116,11 @@ static int __of_iio_simple_xlate(struct iio_dev *indio_dev, if (!iiospec->args_count) return 0; - if (iiospec->args[0] >= indio_dev->num_channels) + if (iiospec->args[0] >= indio_dev->num_channels) { + dev_err(&indio_dev->dev, "invalid channel index %u\n", + iiospec->args[0]); return -EINVAL; + } return iiospec->args[0]; } @@ -634,3 +637,28 @@ err_unlock: return ret; } EXPORT_SYMBOL_GPL(iio_get_channel_type); + +static int iio_channel_write(struct iio_channel *chan, int val, int val2, + enum iio_chan_info_enum info) +{ + return chan->indio_dev->info->write_raw(chan->indio_dev, + chan->channel, val, val2, info); +} + +int iio_write_channel_raw(struct iio_channel *chan, int val) +{ + int ret; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (chan->indio_dev->info == NULL) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_RAW); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_write_channel_raw); diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index 7134e8ada09a..b2beea01c49b 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -47,30 +47,6 @@ static int iio_request_update_kfifo(struct iio_buffer *r) return ret; } -static int iio_get_length_kfifo(struct iio_buffer *r) -{ - return r->length; -} - -static IIO_BUFFER_ENABLE_ATTR; -static IIO_BUFFER_LENGTH_ATTR; - -static struct attribute *iio_kfifo_attributes[] = { - &dev_attr_length.attr, - &dev_attr_enable.attr, - NULL, -}; - -static struct attribute_group iio_kfifo_attribute_group = { - .attrs = iio_kfifo_attributes, - .name = "buffer", -}; - -static int iio_get_bytes_per_datum_kfifo(struct iio_buffer *r) -{ - return r->bytes_per_datum; -} - static int iio_mark_update_needed_kfifo(struct iio_buffer *r) { struct iio_kfifo *kf = iio_to_kfifo(r); @@ -159,26 +135,25 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = { .read_first_n = &iio_read_first_n_kfifo, .data_available = iio_kfifo_buf_data_available, .request_update = &iio_request_update_kfifo, - .get_bytes_per_datum = &iio_get_bytes_per_datum_kfifo, .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, - .get_length = &iio_get_length_kfifo, .set_length = &iio_set_length_kfifo, .release = &iio_kfifo_buffer_release, }; -struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev) +struct iio_buffer *iio_kfifo_allocate(void) { struct iio_kfifo *kf; - kf = kzalloc(sizeof *kf, GFP_KERNEL); + kf = kzalloc(sizeof(*kf), GFP_KERNEL); if (!kf) return NULL; + kf->update_needed = true; iio_buffer_init(&kf->buffer); - kf->buffer.attrs = &iio_kfifo_attribute_group; kf->buffer.access = &kfifo_access_funcs; kf->buffer.length = 2; mutex_init(&kf->user_lock); + return &kf->buffer; } EXPORT_SYMBOL(iio_kfifo_allocate); @@ -189,4 +164,58 @@ void iio_kfifo_free(struct iio_buffer *r) } EXPORT_SYMBOL(iio_kfifo_free); +static void devm_iio_kfifo_release(struct device *dev, void *res) +{ + iio_kfifo_free(*(struct iio_buffer **)res); +} + +static int devm_iio_kfifo_match(struct device *dev, void *res, void *data) +{ + struct iio_buffer **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +/** + * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate() + * @dev: Device to allocate kfifo buffer for + * + * RETURNS: + * Pointer to allocated iio_buffer on success, NULL on failure. + */ +struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) +{ + struct iio_buffer **ptr, *r; + + ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + r = iio_kfifo_allocate(); + if (r) { + *ptr = r; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return r; +} +EXPORT_SYMBOL(devm_iio_kfifo_allocate); + +/** + * devm_iio_fifo_free - Resource-managed iio_kfifo_free() + * @dev: Device the buffer belongs to + * @r: The buffer associated with the device + */ +void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r) +{ + WARN_ON(devres_release(dev, devm_iio_kfifo_release, + devm_iio_kfifo_match, r)); +} +EXPORT_SYMBOL(devm_iio_kfifo_free); + MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 5bea821adcae..5a3237b2aaa5 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -95,6 +95,9 @@ config HID_SENSOR_ALS Say yes here to build support for the HID SENSOR Ambient light sensor. + To compile this driver as a module, choose M here: the + module will be called hid-sensor-als. + config HID_SENSOR_PROX depends on HID_SENSOR_HUB select IIO_BUFFER @@ -109,6 +112,16 @@ config HID_SENSOR_PROX To compile this driver as a module, choose M here: the module will be called hid-sensor-prox. +config JSA1212 + tristate "JSA1212 ALS and proximity sensor driver" + depends on I2C + help + Say Y here if you want to build a IIO driver for JSA1212 + proximity & ALS sensor device. + + To compile this driver as a module, choose M here: + the module will be called jsa1212. + config SENSORS_LM3533 tristate "LM3533 ambient light sensor" depends on MFD_LM3533 diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 47877a36cc12..74656c19a899 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o obj-$(CONFIG_ISL29125) += isl29125.o +obj-$(CONFIG_JSA1212) += jsa1212.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o obj-$(CONFIG_LTR501) += ltr501.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c new file mode 100644 index 000000000000..29de7e7d9562 --- /dev/null +++ b/drivers/iio/light/jsa1212.c @@ -0,0 +1,471 @@ +/* + * JSA1212 Ambient Light & Proximity Sensor Driver + * + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * JSA1212 I2C slave address: 0x44(ADDR tied to GND), 0x45(ADDR tied to VDD) + * + * TODO: Interrupt support, thresholds, range support. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/acpi.h> +#include <linux/regmap.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* JSA1212 reg address */ +#define JSA1212_CONF_REG 0x01 +#define JSA1212_INT_REG 0x02 +#define JSA1212_PXS_LT_REG 0x03 +#define JSA1212_PXS_HT_REG 0x04 +#define JSA1212_ALS_TH1_REG 0x05 +#define JSA1212_ALS_TH2_REG 0x06 +#define JSA1212_ALS_TH3_REG 0x07 +#define JSA1212_PXS_DATA_REG 0x08 +#define JSA1212_ALS_DT1_REG 0x09 +#define JSA1212_ALS_DT2_REG 0x0A +#define JSA1212_ALS_RNG_REG 0x0B +#define JSA1212_MAX_REG 0x0C + +/* JSA1212 reg masks */ +#define JSA1212_CONF_MASK 0xFF +#define JSA1212_INT_MASK 0xFF +#define JSA1212_PXS_LT_MASK 0xFF +#define JSA1212_PXS_HT_MASK 0xFF +#define JSA1212_ALS_TH1_MASK 0xFF +#define JSA1212_ALS_TH2_LT_MASK 0x0F +#define JSA1212_ALS_TH2_HT_MASK 0xF0 +#define JSA1212_ALS_TH3_MASK 0xFF +#define JSA1212_PXS_DATA_MASK 0xFF +#define JSA1212_ALS_DATA_MASK 0x0FFF +#define JSA1212_ALS_DT1_MASK 0xFF +#define JSA1212_ALS_DT2_MASK 0x0F +#define JSA1212_ALS_RNG_MASK 0x07 + +/* JSA1212 CONF REG bits */ +#define JSA1212_CONF_PXS_MASK 0x80 +#define JSA1212_CONF_PXS_ENABLE 0x80 +#define JSA1212_CONF_PXS_DISABLE 0x00 +#define JSA1212_CONF_ALS_MASK 0x04 +#define JSA1212_CONF_ALS_ENABLE 0x04 +#define JSA1212_CONF_ALS_DISABLE 0x00 +#define JSA1212_CONF_IRDR_MASK 0x08 +/* Proxmity sensing IRDR current sink settings */ +#define JSA1212_CONF_IRDR_200MA 0x08 +#define JSA1212_CONF_IRDR_100MA 0x00 +#define JSA1212_CONF_PXS_SLP_MASK 0x70 +#define JSA1212_CONF_PXS_SLP_0MS 0x70 +#define JSA1212_CONF_PXS_SLP_12MS 0x60 +#define JSA1212_CONF_PXS_SLP_50MS 0x50 +#define JSA1212_CONF_PXS_SLP_75MS 0x40 +#define JSA1212_CONF_PXS_SLP_100MS 0x30 +#define JSA1212_CONF_PXS_SLP_200MS 0x20 +#define JSA1212_CONF_PXS_SLP_400MS 0x10 +#define JSA1212_CONF_PXS_SLP_800MS 0x00 + +/* JSA1212 INT REG bits */ +#define JSA1212_INT_CTRL_MASK 0x01 +#define JSA1212_INT_CTRL_EITHER 0x00 +#define JSA1212_INT_CTRL_BOTH 0x01 +#define JSA1212_INT_ALS_PRST_MASK 0x06 +#define JSA1212_INT_ALS_PRST_1CONV 0x00 +#define JSA1212_INT_ALS_PRST_4CONV 0x02 +#define JSA1212_INT_ALS_PRST_8CONV 0x04 +#define JSA1212_INT_ALS_PRST_16CONV 0x06 +#define JSA1212_INT_ALS_FLAG_MASK 0x08 +#define JSA1212_INT_ALS_FLAG_CLR 0x00 +#define JSA1212_INT_PXS_PRST_MASK 0x60 +#define JSA1212_INT_PXS_PRST_1CONV 0x00 +#define JSA1212_INT_PXS_PRST_4CONV 0x20 +#define JSA1212_INT_PXS_PRST_8CONV 0x40 +#define JSA1212_INT_PXS_PRST_16CONV 0x60 +#define JSA1212_INT_PXS_FLAG_MASK 0x80 +#define JSA1212_INT_PXS_FLAG_CLR 0x00 + +/* JSA1212 ALS RNG REG bits */ +#define JSA1212_ALS_RNG_0_2048 0x00 +#define JSA1212_ALS_RNG_0_1024 0x01 +#define JSA1212_ALS_RNG_0_512 0x02 +#define JSA1212_ALS_RNG_0_256 0x03 +#define JSA1212_ALS_RNG_0_128 0x04 + +/* JSA1212 INT threshold range */ +#define JSA1212_ALS_TH_MIN 0x0000 +#define JSA1212_ALS_TH_MAX 0x0FFF +#define JSA1212_PXS_TH_MIN 0x00 +#define JSA1212_PXS_TH_MAX 0xFF + +#define JSA1212_ALS_DELAY_MS 200 +#define JSA1212_PXS_DELAY_MS 100 + +#define JSA1212_DRIVER_NAME "jsa1212" +#define JSA1212_REGMAP_NAME "jsa1212_regmap" + +enum jsa1212_op_mode { + JSA1212_OPMODE_ALS_EN, + JSA1212_OPMODE_PXS_EN, +}; + +struct jsa1212_data { + struct i2c_client *client; + struct mutex lock; + u8 als_rng_idx; + bool als_en; /* ALS enable status */ + bool pxs_en; /* proximity enable status */ + struct regmap *regmap; +}; + +/* ALS range idx to val mapping */ +static const int jsa1212_als_range_val[] = {2048, 1024, 512, 256, 128, + 128, 128, 128}; + +/* Enables or disables ALS function based on status */ +static int jsa1212_als_enable(struct jsa1212_data *data, u8 status) +{ + int ret; + + ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG, + JSA1212_CONF_ALS_MASK, + status); + if (ret < 0) + return ret; + + data->als_en = !!status; + + return 0; +} + +/* Enables or disables PXS function based on status */ +static int jsa1212_pxs_enable(struct jsa1212_data *data, u8 status) +{ + int ret; + + ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG, + JSA1212_CONF_PXS_MASK, + status); + if (ret < 0) + return ret; + + data->pxs_en = !!status; + + return 0; +} + +static int jsa1212_read_als_data(struct jsa1212_data *data, + unsigned int *val) +{ + int ret; + __le16 als_data; + + ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE); + if (ret < 0) + return ret; + + /* Delay for data output */ + msleep(JSA1212_ALS_DELAY_MS); + + /* Read 12 bit data */ + ret = regmap_bulk_read(data->regmap, JSA1212_ALS_DT1_REG, &als_data, 2); + if (ret < 0) { + dev_err(&data->client->dev, "als data read err\n"); + goto als_data_read_err; + } + + *val = le16_to_cpu(als_data); + +als_data_read_err: + return jsa1212_als_enable(data, JSA1212_CONF_ALS_DISABLE); +} + +static int jsa1212_read_pxs_data(struct jsa1212_data *data, + unsigned int *val) +{ + int ret; + unsigned int pxs_data; + + ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE); + if (ret < 0) + return ret; + + /* Delay for data output */ + msleep(JSA1212_PXS_DELAY_MS); + + /* Read out all data */ + ret = regmap_read(data->regmap, JSA1212_PXS_DATA_REG, &pxs_data); + if (ret < 0) { + dev_err(&data->client->dev, "pxs data read err\n"); + goto pxs_data_read_err; + } + + *val = pxs_data & JSA1212_PXS_DATA_MASK; + +pxs_data_read_err: + return jsa1212_pxs_enable(data, JSA1212_CONF_PXS_DISABLE); +} + +static int jsa1212_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct jsa1212_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + switch (chan->type) { + case IIO_LIGHT: + ret = jsa1212_read_als_data(data, val); + break; + case IIO_PROXIMITY: + ret = jsa1212_read_pxs_data(data, val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + return ret < 0 ? ret : IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_LIGHT: + *val = jsa1212_als_range_val[data->als_rng_idx]; + *val2 = BIT(12); /* Max 12 bit value */ + return IIO_VAL_FRACTIONAL; + default: + break; + } + break; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_chan_spec jsa1212_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + } +}; + +static const struct iio_info jsa1212_info = { + .driver_module = THIS_MODULE, + .read_raw = &jsa1212_read_raw, +}; + +static int jsa1212_chip_init(struct jsa1212_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, JSA1212_CONF_REG, + (JSA1212_CONF_PXS_SLP_50MS | + JSA1212_CONF_IRDR_200MA)); + if (ret < 0) + return ret; + + ret = regmap_write(data->regmap, JSA1212_INT_REG, + JSA1212_INT_ALS_PRST_4CONV); + if (ret < 0) + return ret; + + data->als_rng_idx = JSA1212_ALS_RNG_0_2048; + + return 0; +} + +static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case JSA1212_PXS_DATA_REG: + case JSA1212_ALS_DT1_REG: + case JSA1212_ALS_DT2_REG: + case JSA1212_INT_REG: + return true; + default: + return false; + } +} + +static struct regmap_config jsa1212_regmap_config = { + .name = JSA1212_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 8, + .max_register = JSA1212_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = jsa1212_is_volatile_reg, +}; + +static int jsa1212_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct jsa1212_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &jsa1212_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Regmap initialization failed.\n"); + return PTR_ERR(regmap); + } + + data = iio_priv(indio_dev); + + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->regmap = regmap; + + mutex_init(&data->lock); + + ret = jsa1212_chip_init(data); + if (ret < 0) + return ret; + + indio_dev->dev.parent = &client->dev; + indio_dev->channels = jsa1212_channels; + indio_dev->num_channels = ARRAY_SIZE(jsa1212_channels); + indio_dev->name = JSA1212_DRIVER_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->info = &jsa1212_info; + + ret = iio_device_register(indio_dev); + if (ret < 0) + dev_err(&client->dev, "%s: register device failed\n", __func__); + + return ret; +} + + /* power off the device */ +static int jsa1212_power_off(struct jsa1212_data *data) +{ + int ret; + + mutex_lock(&data->lock); + + ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG, + JSA1212_CONF_ALS_MASK | + JSA1212_CONF_PXS_MASK, + JSA1212_CONF_ALS_DISABLE | + JSA1212_CONF_PXS_DISABLE); + + if (ret < 0) + dev_err(&data->client->dev, "power off cmd failed\n"); + + mutex_unlock(&data->lock); + + return ret; +} + +static int jsa1212_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct jsa1212_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + return jsa1212_power_off(data); +} + +#ifdef CONFIG_PM_SLEEP +static int jsa1212_suspend(struct device *dev) +{ + struct jsa1212_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return jsa1212_power_off(data); +} + +static int jsa1212_resume(struct device *dev) +{ + int ret = 0; + struct jsa1212_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + mutex_lock(&data->lock); + + if (data->als_en) { + ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE); + if (ret < 0) { + dev_err(dev, "als resume failed\n"); + goto unlock_and_ret; + } + } + + if (data->pxs_en) { + ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE); + if (ret < 0) + dev_err(dev, "pxs resume failed\n"); + } + +unlock_and_ret: + mutex_unlock(&data->lock); + return ret; +} + +static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume); + +#define JSA1212_PM_OPS (&jsa1212_pm_ops) +#else +#define JSA1212_PM_OPS NULL +#endif + +static const struct acpi_device_id jsa1212_acpi_match[] = { + {"JSA1212", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match); + +static const struct i2c_device_id jsa1212_id[] = { + { JSA1212_DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, jsa1212_id); + +static struct i2c_driver jsa1212_driver = { + .driver = { + .name = JSA1212_DRIVER_NAME, + .pm = JSA1212_PM_OPS, + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(jsa1212_acpi_match), + }, + .probe = jsa1212_probe, + .remove = jsa1212_remove, + .id_table = jsa1212_id, +}; +module_i2c_driver(jsa1212_driver); + +MODULE_AUTHOR("Sathya Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com>"); +MODULE_DESCRIPTION("JSA1212 proximity/ambient light sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index ae3c71bdd6c6..076bc46fad03 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -657,7 +657,7 @@ static ALS_HYSTERESIS_ATTR_RO(3); #define ILLUMINANCE_ATTR_RO(_name) \ DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL) #define ILLUMINANCE_ATTR_RW(_name) \ - DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \ + DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \ show_##_name, store_##_name) /* * ALS Zone threshold-event enable diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index b2dba9e506ab..4c7a4c52dd06 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -6,26 +6,21 @@ menu "Magnetometer sensors" config AK8975 - tristate "Asahi Kasei AK8975 3-Axis Magnetometer" + tristate "Asahi Kasei AK 3-Axis Magnetometer" depends on I2C depends on GPIOLIB help - Say yes here to build support for Asahi Kasei AK8975 3-Axis - Magnetometer. This driver can also support AK8963, if i2c - device name is identified as ak8963. + Say yes here to build support for Asahi Kasei AK8975, AK8963, + AK09911 or AK09912 3-Axis Magnetometer. To compile this driver as a module, choose M here: the module will be called ak8975. config AK09911 tristate "Asahi Kasei AK09911 3-axis Compass" - depends on I2C + select AK8975 help - Say yes here to build support for Asahi Kasei AK09911 3-Axis - Magnetometer. - - To compile this driver as a module, choose M here: the module - will be called ak09911. + Deprecated: AK09911 is now supported by AK8975 driver. config MAG3110 tristate "Freescale MAG3110 3-Axis Magnetometer" diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index b91315e0b826..0f5d3c985799 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -3,7 +3,6 @@ # # When adding new entries keep the list in alphabetical order -obj-$(CONFIG_AK09911) += ak09911.o obj-$(CONFIG_AK8975) += ak8975.o obj-$(CONFIG_MAG3110) += mag3110.o obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o diff --git a/drivers/iio/magnetometer/ak09911.c b/drivers/iio/magnetometer/ak09911.c deleted file mode 100644 index b2bc942ff6b8..000000000000 --- a/drivers/iio/magnetometer/ak09911.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * AK09911 3-axis compass driver - * Copyright (c) 2014, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/acpi.h> -#include <linux/iio/iio.h> - -#define AK09911_REG_WIA1 0x00 -#define AK09911_REG_WIA2 0x01 -#define AK09911_WIA1_VALUE 0x48 -#define AK09911_WIA2_VALUE 0x05 - -#define AK09911_REG_ST1 0x10 -#define AK09911_REG_HXL 0x11 -#define AK09911_REG_HXH 0x12 -#define AK09911_REG_HYL 0x13 -#define AK09911_REG_HYH 0x14 -#define AK09911_REG_HZL 0x15 -#define AK09911_REG_HZH 0x16 - -#define AK09911_REG_ASAX 0x60 -#define AK09911_REG_ASAY 0x61 -#define AK09911_REG_ASAZ 0x62 - -#define AK09911_REG_CNTL1 0x30 -#define AK09911_REG_CNTL2 0x31 -#define AK09911_REG_CNTL3 0x32 - -#define AK09911_MODE_SNG_MEASURE 0x01 -#define AK09911_MODE_SELF_TEST 0x10 -#define AK09911_MODE_FUSE_ACCESS 0x1F -#define AK09911_MODE_POWERDOWN 0x00 -#define AK09911_RESET_DATA 0x01 - -#define AK09911_REG_CNTL1 0x30 -#define AK09911_REG_CNTL2 0x31 -#define AK09911_REG_CNTL3 0x32 - -#define AK09911_RAW_TO_GAUSS(asa) ((((asa) + 128) * 6000) / 256) - -#define AK09911_MAX_CONVERSION_TIMEOUT_MS 500 -#define AK09911_CONVERSION_DONE_POLL_TIME_MS 10 - -struct ak09911_data { - struct i2c_client *client; - struct mutex lock; - u8 asa[3]; - long raw_to_gauss[3]; -}; - -static const int ak09911_index_to_reg[] = { - AK09911_REG_HXL, AK09911_REG_HYL, AK09911_REG_HZL, -}; - -static int ak09911_set_mode(struct i2c_client *client, u8 mode) -{ - int ret; - - switch (mode) { - case AK09911_MODE_SNG_MEASURE: - case AK09911_MODE_SELF_TEST: - case AK09911_MODE_FUSE_ACCESS: - case AK09911_MODE_POWERDOWN: - ret = i2c_smbus_write_byte_data(client, - AK09911_REG_CNTL2, mode); - if (ret < 0) { - dev_err(&client->dev, "set_mode error\n"); - return ret; - } - /* After mode change wait atleast 100us */ - usleep_range(100, 500); - break; - default: - dev_err(&client->dev, - "%s: Unknown mode(%d).", __func__, mode); - return -EINVAL; - } - - return ret; -} - -/* Get Sensitivity Adjustment value */ -static int ak09911_get_asa(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ak09911_data *data = iio_priv(indio_dev); - int ret; - - ret = ak09911_set_mode(client, AK09911_MODE_FUSE_ACCESS); - if (ret < 0) - return ret; - - /* Get asa data and store in the device data. */ - ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_ASAX, - 3, data->asa); - if (ret < 0) { - dev_err(&client->dev, "Not able to read asa data\n"); - return ret; - } - - ret = ak09911_set_mode(client, AK09911_MODE_POWERDOWN); - if (ret < 0) - return ret; - - data->raw_to_gauss[0] = AK09911_RAW_TO_GAUSS(data->asa[0]); - data->raw_to_gauss[1] = AK09911_RAW_TO_GAUSS(data->asa[1]); - data->raw_to_gauss[2] = AK09911_RAW_TO_GAUSS(data->asa[2]); - - return 0; -} - -static int ak09911_verify_chip_id(struct i2c_client *client) -{ - u8 wia_val[2]; - int ret; - - ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_WIA1, - 2, wia_val); - if (ret < 0) { - dev_err(&client->dev, "Error reading WIA\n"); - return ret; - } - - dev_dbg(&client->dev, "WIA %02x %02x\n", wia_val[0], wia_val[1]); - - if (wia_val[0] != AK09911_WIA1_VALUE || - wia_val[1] != AK09911_WIA2_VALUE) { - dev_err(&client->dev, "Device ak09911 not found\n"); - return -ENODEV; - } - - return 0; -} - -static int wait_conversion_complete_polled(struct ak09911_data *data) -{ - struct i2c_client *client = data->client; - u8 read_status; - u32 timeout_ms = AK09911_MAX_CONVERSION_TIMEOUT_MS; - int ret; - - /* Wait for the conversion to complete. */ - while (timeout_ms) { - msleep_interruptible(AK09911_CONVERSION_DONE_POLL_TIME_MS); - ret = i2c_smbus_read_byte_data(client, AK09911_REG_ST1); - if (ret < 0) { - dev_err(&client->dev, "Error in reading ST1\n"); - return ret; - } - read_status = ret & 0x01; - if (read_status) - break; - timeout_ms -= AK09911_CONVERSION_DONE_POLL_TIME_MS; - } - if (!timeout_ms) { - dev_err(&client->dev, "Conversion timeout happened\n"); - return -EIO; - } - - return read_status; -} - -static int ak09911_read_axis(struct iio_dev *indio_dev, int index, int *val) -{ - struct ak09911_data *data = iio_priv(indio_dev); - struct i2c_client *client = data->client; - int ret; - - mutex_lock(&data->lock); - - ret = ak09911_set_mode(client, AK09911_MODE_SNG_MEASURE); - if (ret < 0) - goto fn_exit; - - ret = wait_conversion_complete_polled(data); - if (ret < 0) - goto fn_exit; - - /* Read data */ - ret = i2c_smbus_read_word_data(client, ak09911_index_to_reg[index]); - if (ret < 0) { - dev_err(&client->dev, "Read axis data fails\n"); - goto fn_exit; - } - - mutex_unlock(&data->lock); - - /* Clamp to valid range. */ - *val = sign_extend32(clamp_t(s16, ret, -8192, 8191), 13); - - return IIO_VAL_INT; - -fn_exit: - mutex_unlock(&data->lock); - - return ret; -} - -static int ak09911_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) -{ - struct ak09911_data *data = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_RAW: - return ak09911_read_axis(indio_dev, chan->address, val); - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = data->raw_to_gauss[chan->address]; - return IIO_VAL_INT_PLUS_MICRO; - } - - return -EINVAL; -} - -#define AK09911_CHANNEL(axis, index) \ - { \ - .type = IIO_MAGN, \ - .modified = 1, \ - .channel2 = IIO_MOD_##axis, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .address = index, \ - } - -static const struct iio_chan_spec ak09911_channels[] = { - AK09911_CHANNEL(X, 0), AK09911_CHANNEL(Y, 1), AK09911_CHANNEL(Z, 2), -}; - -static const struct iio_info ak09911_info = { - .read_raw = &ak09911_read_raw, - .driver_module = THIS_MODULE, -}; - -static const struct acpi_device_id ak_acpi_match[] = { - {"AK009911", 0}, - { }, -}; -MODULE_DEVICE_TABLE(acpi, ak_acpi_match); - -static int ak09911_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct iio_dev *indio_dev; - struct ak09911_data *data; - const char *name; - int ret; - - ret = ak09911_verify_chip_id(client); - if (ret) { - dev_err(&client->dev, "AK00911 not detected\n"); - return -ENODEV; - } - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); - if (indio_dev == NULL) - return -ENOMEM; - - data = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - - data->client = client; - mutex_init(&data->lock); - - ret = ak09911_get_asa(client); - if (ret) - return ret; - - if (id) - name = id->name; - else if (ACPI_HANDLE(&client->dev)) - name = dev_name(&client->dev); - else - return -ENODEV; - - dev_dbg(&client->dev, "Asahi compass chip %s\n", name); - - indio_dev->dev.parent = &client->dev; - indio_dev->channels = ak09911_channels; - indio_dev->num_channels = ARRAY_SIZE(ak09911_channels); - indio_dev->info = &ak09911_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->name = name; - - return devm_iio_device_register(&client->dev, indio_dev); -} - -static const struct i2c_device_id ak09911_id[] = { - {"ak09911", 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, ak09911_id); - -static struct i2c_driver ak09911_driver = { - .driver = { - .name = "ak09911", - .acpi_match_table = ACPI_PTR(ak_acpi_match), - }, - .probe = ak09911_probe, - .id_table = ak09911_id, -}; -module_i2c_driver(ak09911_driver); - -MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("AK09911 Compass driver"); diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index bf5ef077e791..0d10a4baceb6 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -64,10 +64,10 @@ #define AK8975_REG_CNTL 0x0A #define AK8975_REG_CNTL_MODE_SHIFT 0 #define AK8975_REG_CNTL_MODE_MASK (0xF << AK8975_REG_CNTL_MODE_SHIFT) -#define AK8975_REG_CNTL_MODE_POWER_DOWN 0 -#define AK8975_REG_CNTL_MODE_ONCE 1 -#define AK8975_REG_CNTL_MODE_SELF_TEST 8 -#define AK8975_REG_CNTL_MODE_FUSE_ROM 0xF +#define AK8975_REG_CNTL_MODE_POWER_DOWN 0x00 +#define AK8975_REG_CNTL_MODE_ONCE 0x01 +#define AK8975_REG_CNTL_MODE_SELF_TEST 0x08 +#define AK8975_REG_CNTL_MODE_FUSE_ROM 0x0F #define AK8975_REG_RSVC 0x0B #define AK8975_REG_ASTC 0x0C @@ -81,18 +81,278 @@ #define AK8975_MAX_REGS AK8975_REG_ASAZ /* + * AK09912 Register definitions + */ +#define AK09912_REG_WIA1 0x00 +#define AK09912_REG_WIA2 0x01 +#define AK09912_DEVICE_ID 0x04 +#define AK09911_DEVICE_ID 0x05 + +#define AK09911_REG_INFO1 0x02 +#define AK09911_REG_INFO2 0x03 + +#define AK09912_REG_ST1 0x10 + +#define AK09912_REG_ST1_DRDY_SHIFT 0 +#define AK09912_REG_ST1_DRDY_MASK (1 << AK09912_REG_ST1_DRDY_SHIFT) + +#define AK09912_REG_HXL 0x11 +#define AK09912_REG_HXH 0x12 +#define AK09912_REG_HYL 0x13 +#define AK09912_REG_HYH 0x14 +#define AK09912_REG_HZL 0x15 +#define AK09912_REG_HZH 0x16 +#define AK09912_REG_TMPS 0x17 + +#define AK09912_REG_ST2 0x18 +#define AK09912_REG_ST2_HOFL_SHIFT 3 +#define AK09912_REG_ST2_HOFL_MASK (1 << AK09912_REG_ST2_HOFL_SHIFT) + +#define AK09912_REG_CNTL1 0x30 + +#define AK09912_REG_CNTL2 0x31 +#define AK09912_REG_CNTL_MODE_POWER_DOWN 0x00 +#define AK09912_REG_CNTL_MODE_ONCE 0x01 +#define AK09912_REG_CNTL_MODE_SELF_TEST 0x10 +#define AK09912_REG_CNTL_MODE_FUSE_ROM 0x1F +#define AK09912_REG_CNTL2_MODE_SHIFT 0 +#define AK09912_REG_CNTL2_MODE_MASK (0x1F << AK09912_REG_CNTL2_MODE_SHIFT) + +#define AK09912_REG_CNTL3 0x32 + +#define AK09912_REG_TS1 0x33 +#define AK09912_REG_TS2 0x34 +#define AK09912_REG_TS3 0x35 +#define AK09912_REG_I2CDIS 0x36 +#define AK09912_REG_TS4 0x37 + +#define AK09912_REG_ASAX 0x60 +#define AK09912_REG_ASAY 0x61 +#define AK09912_REG_ASAZ 0x62 + +#define AK09912_MAX_REGS AK09912_REG_ASAZ + +/* * Miscellaneous values. */ #define AK8975_MAX_CONVERSION_TIMEOUT 500 #define AK8975_CONVERSION_DONE_POLL_TIME 10 #define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000) -#define RAW_TO_GAUSS_8975(asa) ((((asa) + 128) * 3000) / 256) -#define RAW_TO_GAUSS_8963(asa) ((((asa) + 128) * 6000) / 256) + +/* + * Precalculate scale factor (in Gauss units) for each axis and + * store in the device data. + * + * This scale factor is axis-dependent, and is derived from 3 calibration + * factors ASA(x), ASA(y), and ASA(z). + * + * These ASA values are read from the sensor device at start of day, and + * cached in the device context struct. + * + * Adjusting the flux value with the sensitivity adjustment value should be + * done via the following formula: + * + * Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 ) + * where H is the raw value, ASA is the sensitivity adjustment, and Hadj + * is the resultant adjusted value. + * + * We reduce the formula to: + * + * Hadj = H * (ASA + 128) / 256 + * + * H is in the range of -4096 to 4095. The magnetometer has a range of + * +-1229uT. To go from the raw value to uT is: + * + * HuT = H * 1229/4096, or roughly, 3/10. + * + * Since 1uT = 0.01 gauss, our final scale factor becomes: + * + * Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100 + * Hadj = H * ((ASA + 128) * 0.003) / 256 + * + * Since ASA doesn't change, we cache the resultant scale factor into the + * device context in ak8975_setup(). + * + * Given we use IIO_VAL_INT_PLUS_MICRO bit when displaying the scale, we + * multiply the stored scale value by 1e6. + */ +static long ak8975_raw_to_gauss(u16 data) +{ + return (((long)data + 128) * 3000) / 256; +} + +/* + * For AK8963 and AK09911, same calculation, but the device is less sensitive: + * + * H is in the range of +-8190. The magnetometer has a range of + * +-4912uT. To go from the raw value to uT is: + * + * HuT = H * 4912/8190, or roughly, 6/10, instead of 3/10. + */ + +static long ak8963_09911_raw_to_gauss(u16 data) +{ + return (((long)data + 128) * 6000) / 256; +} + +/* + * For AK09912, same calculation, except the device is more sensitive: + * + * H is in the range of -32752 to 32752. The magnetometer has a range of + * +-4912uT. To go from the raw value to uT is: + * + * HuT = H * 4912/32752, or roughly, 3/20, instead of 3/10. + */ +static long ak09912_raw_to_gauss(u16 data) +{ + return (((long)data + 128) * 1500) / 256; +} /* Compatible Asahi Kasei Compass parts */ enum asahi_compass_chipset { AK8975, AK8963, + AK09911, + AK09912, + AK_MAX_TYPE +}; + +enum ak_ctrl_reg_addr { + ST1, + ST2, + CNTL, + ASA_BASE, + MAX_REGS, + REGS_END, +}; + +enum ak_ctrl_reg_mask { + ST1_DRDY, + ST2_HOFL, + ST2_DERR, + CNTL_MODE, + MASK_END, +}; + +enum ak_ctrl_mode { + POWER_DOWN, + MODE_ONCE, + SELF_TEST, + FUSE_ROM, + MODE_END, +}; + +struct ak_def { + enum asahi_compass_chipset type; + long (*raw_to_gauss)(u16 data); + u16 range; + u8 ctrl_regs[REGS_END]; + u8 ctrl_masks[MASK_END]; + u8 ctrl_modes[MODE_END]; + u8 data_regs[3]; +}; + +static struct ak_def ak_def_array[AK_MAX_TYPE] = { + { + .type = AK8975, + .raw_to_gauss = ak8975_raw_to_gauss, + .range = 4096, + .ctrl_regs = { + AK8975_REG_ST1, + AK8975_REG_ST2, + AK8975_REG_CNTL, + AK8975_REG_ASAX, + AK8975_MAX_REGS}, + .ctrl_masks = { + AK8975_REG_ST1_DRDY_MASK, + AK8975_REG_ST2_HOFL_MASK, + AK8975_REG_ST2_DERR_MASK, + AK8975_REG_CNTL_MODE_MASK}, + .ctrl_modes = { + AK8975_REG_CNTL_MODE_POWER_DOWN, + AK8975_REG_CNTL_MODE_ONCE, + AK8975_REG_CNTL_MODE_SELF_TEST, + AK8975_REG_CNTL_MODE_FUSE_ROM}, + .data_regs = { + AK8975_REG_HXL, + AK8975_REG_HYL, + AK8975_REG_HZL}, + }, + { + .type = AK8963, + .raw_to_gauss = ak8963_09911_raw_to_gauss, + .range = 8190, + .ctrl_regs = { + AK8975_REG_ST1, + AK8975_REG_ST2, + AK8975_REG_CNTL, + AK8975_REG_ASAX, + AK8975_MAX_REGS}, + .ctrl_masks = { + AK8975_REG_ST1_DRDY_MASK, + AK8975_REG_ST2_HOFL_MASK, + 0, + AK8975_REG_CNTL_MODE_MASK}, + .ctrl_modes = { + AK8975_REG_CNTL_MODE_POWER_DOWN, + AK8975_REG_CNTL_MODE_ONCE, + AK8975_REG_CNTL_MODE_SELF_TEST, + AK8975_REG_CNTL_MODE_FUSE_ROM}, + .data_regs = { + AK8975_REG_HXL, + AK8975_REG_HYL, + AK8975_REG_HZL}, + }, + { + .type = AK09911, + .raw_to_gauss = ak8963_09911_raw_to_gauss, + .range = 8192, + .ctrl_regs = { + AK09912_REG_ST1, + AK09912_REG_ST2, + AK09912_REG_CNTL2, + AK09912_REG_ASAX, + AK09912_MAX_REGS}, + .ctrl_masks = { + AK09912_REG_ST1_DRDY_MASK, + AK09912_REG_ST2_HOFL_MASK, + 0, + AK09912_REG_CNTL2_MODE_MASK}, + .ctrl_modes = { + AK09912_REG_CNTL_MODE_POWER_DOWN, + AK09912_REG_CNTL_MODE_ONCE, + AK09912_REG_CNTL_MODE_SELF_TEST, + AK09912_REG_CNTL_MODE_FUSE_ROM}, + .data_regs = { + AK09912_REG_HXL, + AK09912_REG_HYL, + AK09912_REG_HZL}, + }, + { + .type = AK09912, + .raw_to_gauss = ak09912_raw_to_gauss, + .range = 32752, + .ctrl_regs = { + AK09912_REG_ST1, + AK09912_REG_ST2, + AK09912_REG_CNTL2, + AK09912_REG_ASAX, + AK09912_MAX_REGS}, + .ctrl_masks = { + AK09912_REG_ST1_DRDY_MASK, + AK09912_REG_ST2_HOFL_MASK, + 0, + AK09912_REG_CNTL2_MODE_MASK}, + .ctrl_modes = { + AK09912_REG_CNTL_MODE_POWER_DOWN, + AK09912_REG_CNTL_MODE_ONCE, + AK09912_REG_CNTL_MODE_SELF_TEST, + AK09912_REG_CNTL_MODE_FUSE_ROM}, + .data_regs = { + AK09912_REG_HXL, + AK09912_REG_HYL, + AK09912_REG_HZL}, + } }; /* @@ -100,40 +360,82 @@ enum asahi_compass_chipset { */ struct ak8975_data { struct i2c_client *client; + struct ak_def *def; struct attribute_group attrs; struct mutex lock; u8 asa[3]; long raw_to_gauss[3]; - u8 reg_cache[AK8975_MAX_REGS]; int eoc_gpio; int eoc_irq; wait_queue_head_t data_ready_queue; unsigned long flags; - enum asahi_compass_chipset chipset; + u8 cntl_cache; }; -static const int ak8975_index_to_reg[] = { - AK8975_REG_HXL, AK8975_REG_HYL, AK8975_REG_HZL, -}; +/* + * Return 0 if the i2c device is the one we expect. + * return a negative error number otherwise + */ +static int ak8975_who_i_am(struct i2c_client *client, + enum asahi_compass_chipset type) +{ + u8 wia_val[2]; + int ret; + + /* + * Signature for each device: + * Device | WIA1 | WIA2 + * AK09912 | DEVICE_ID | AK09912_DEVICE_ID + * AK09911 | DEVICE_ID | AK09911_DEVICE_ID + * AK8975 | DEVICE_ID | NA + * AK8963 | DEVICE_ID | NA + */ + ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1, + 2, wia_val); + if (ret < 0) { + dev_err(&client->dev, "Error reading WIA\n"); + return ret; + } + + if (wia_val[0] != AK8975_DEVICE_ID) + return -ENODEV; + + switch (type) { + case AK8975: + case AK8963: + return 0; + case AK09911: + if (wia_val[1] == AK09911_DEVICE_ID) + return 0; + break; + case AK09912: + if (wia_val[1] == AK09912_DEVICE_ID) + return 0; + break; + default: + dev_err(&client->dev, "Type %d unknown\n", type); + } + return -ENODEV; +} /* - * Helper function to write to the I2C device's registers. + * Helper function to write to CNTL register. */ -static int ak8975_write_data(struct i2c_client *client, - u8 reg, u8 val, u8 mask, u8 shift) +static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode) { - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ak8975_data *data = iio_priv(indio_dev); u8 regval; int ret; - regval = (data->reg_cache[reg] & ~mask) | (val << shift); - ret = i2c_smbus_write_byte_data(client, reg, regval); + regval = (data->cntl_cache & ~data->def->ctrl_masks[CNTL_MODE]) | + data->def->ctrl_modes[mode]; + ret = i2c_smbus_write_byte_data(data->client, + data->def->ctrl_regs[CNTL], regval); if (ret < 0) { - dev_err(&client->dev, "Write to device fails status %x\n", ret); return ret; } - data->reg_cache[reg] = regval; + data->cntl_cache = regval; + /* After mode change wait atleast 100us */ + usleep_range(100, 500); return 0; } @@ -166,8 +468,8 @@ static int ak8975_setup_irq(struct ak8975_data *data) irq = gpio_to_irq(data->eoc_gpio); rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - dev_name(&client->dev), data); + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + dev_name(&client->dev), data); if (rc < 0) { dev_err(&client->dev, "irq %d request failed, (gpio %d): %d\n", @@ -191,34 +493,18 @@ static int ak8975_setup(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct ak8975_data *data = iio_priv(indio_dev); - u8 device_id; int ret; - /* Confirm that the device we're talking to is really an AK8975. */ - ret = i2c_smbus_read_byte_data(client, AK8975_REG_WIA); - if (ret < 0) { - dev_err(&client->dev, "Error reading WIA\n"); - return ret; - } - device_id = ret; - if (device_id != AK8975_DEVICE_ID) { - dev_err(&client->dev, "Device ak8975 not found\n"); - return -ENODEV; - } - /* Write the fused rom access mode. */ - ret = ak8975_write_data(client, - AK8975_REG_CNTL, - AK8975_REG_CNTL_MODE_FUSE_ROM, - AK8975_REG_CNTL_MODE_MASK, - AK8975_REG_CNTL_MODE_SHIFT); + ret = ak8975_set_mode(data, FUSE_ROM); if (ret < 0) { dev_err(&client->dev, "Error in setting fuse access mode\n"); return ret; } /* Get asa data and store in the device data. */ - ret = i2c_smbus_read_i2c_block_data(client, AK8975_REG_ASAX, + ret = i2c_smbus_read_i2c_block_data(client, + data->def->ctrl_regs[ASA_BASE], 3, data->asa); if (ret < 0) { dev_err(&client->dev, "Not able to read asa data\n"); @@ -226,13 +512,13 @@ static int ak8975_setup(struct i2c_client *client) } /* After reading fuse ROM data set power-down mode */ - ret = ak8975_write_data(client, - AK8975_REG_CNTL, - AK8975_REG_CNTL_MODE_POWER_DOWN, - AK8975_REG_CNTL_MODE_MASK, - AK8975_REG_CNTL_MODE_SHIFT); + ret = ak8975_set_mode(data, POWER_DOWN); + if (ret < 0) { + dev_err(&client->dev, "Error in setting power-down mode\n"); + return ret; + } - if (data->eoc_gpio > 0 || client->irq) { + if (data->eoc_gpio > 0 || client->irq > 0) { ret = ak8975_setup_irq(data); if (ret < 0) { dev_err(&client->dev, @@ -241,61 +527,9 @@ static int ak8975_setup(struct i2c_client *client) } } - if (ret < 0) { - dev_err(&client->dev, "Error in setting power-down mode\n"); - return ret; - } - -/* - * Precalculate scale factor (in Gauss units) for each axis and - * store in the device data. - * - * This scale factor is axis-dependent, and is derived from 3 calibration - * factors ASA(x), ASA(y), and ASA(z). - * - * These ASA values are read from the sensor device at start of day, and - * cached in the device context struct. - * - * Adjusting the flux value with the sensitivity adjustment value should be - * done via the following formula: - * - * Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 ) - * - * where H is the raw value, ASA is the sensitivity adjustment, and Hadj - * is the resultant adjusted value. - * - * We reduce the formula to: - * - * Hadj = H * (ASA + 128) / 256 - * - * H is in the range of -4096 to 4095. The magnetometer has a range of - * +-1229uT. To go from the raw value to uT is: - * - * HuT = H * 1229/4096, or roughly, 3/10. - * - * Since 1uT = 0.01 gauss, our final scale factor becomes: - * - * Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100 - * Hadj = H * ((ASA + 128) * 0.003) / 256 - * - * Since ASA doesn't change, we cache the resultant scale factor into the - * device context in ak8975_setup(). - */ - if (data->chipset == AK8963) { - /* - * H range is +-8190 and magnetometer range is +-4912. - * So HuT using the above explanation for 8975, - * 4912/8190 = ~ 6/10. - * So the Hadj should use 6/10 instead of 3/10. - */ - data->raw_to_gauss[0] = RAW_TO_GAUSS_8963(data->asa[0]); - data->raw_to_gauss[1] = RAW_TO_GAUSS_8963(data->asa[1]); - data->raw_to_gauss[2] = RAW_TO_GAUSS_8963(data->asa[2]); - } else { - data->raw_to_gauss[0] = RAW_TO_GAUSS_8975(data->asa[0]); - data->raw_to_gauss[1] = RAW_TO_GAUSS_8975(data->asa[1]); - data->raw_to_gauss[2] = RAW_TO_GAUSS_8975(data->asa[2]); - } + data->raw_to_gauss[0] = data->def->raw_to_gauss(data->asa[0]); + data->raw_to_gauss[1] = data->def->raw_to_gauss(data->asa[1]); + data->raw_to_gauss[2] = data->def->raw_to_gauss(data->asa[2]); return 0; } @@ -318,7 +552,7 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data) return -EINVAL; } - ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1); + ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]); if (ret < 0) dev_err(&client->dev, "Error in reading ST1\n"); @@ -335,7 +569,8 @@ static int wait_conversion_complete_polled(struct ak8975_data *data) /* Wait for the conversion to complete. */ while (timeout_ms) { msleep(AK8975_CONVERSION_DONE_POLL_TIME); - ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1); + ret = i2c_smbus_read_byte_data(client, + data->def->ctrl_regs[ST1]); if (ret < 0) { dev_err(&client->dev, "Error in reading ST1\n"); return ret; @@ -378,11 +613,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) mutex_lock(&data->lock); /* Set up the device for taking a sample. */ - ret = ak8975_write_data(client, - AK8975_REG_CNTL, - AK8975_REG_CNTL_MODE_ONCE, - AK8975_REG_CNTL_MODE_MASK, - AK8975_REG_CNTL_MODE_SHIFT); + ret = ak8975_set_mode(data, MODE_ONCE); if (ret < 0) { dev_err(&client->dev, "Error in setting operating mode\n"); goto exit; @@ -399,14 +630,15 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) goto exit; /* This will be executed only for non-interrupt based waiting case */ - if (ret & AK8975_REG_ST1_DRDY_MASK) { - ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST2); + if (ret & data->def->ctrl_masks[ST1_DRDY]) { + ret = i2c_smbus_read_byte_data(client, + data->def->ctrl_regs[ST2]); if (ret < 0) { dev_err(&client->dev, "Error in reading ST2\n"); goto exit; } - if (ret & (AK8975_REG_ST2_DERR_MASK | - AK8975_REG_ST2_HOFL_MASK)) { + 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; @@ -415,7 +647,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) /* Read the flux value from the appropriate register (the register is specified in the iio device attributes). */ - ret = i2c_smbus_read_word_data(client, ak8975_index_to_reg[index]); + ret = i2c_smbus_read_word_data(client, data->def->data_regs[index]); if (ret < 0) { dev_err(&client->dev, "Read axis data fails\n"); goto exit; @@ -424,7 +656,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) mutex_unlock(&data->lock); /* Clamp to valid range. */ - *val = clamp_t(s16, ret, -4096, 4095); + *val = clamp_t(s16, ret, -data->def->range, data->def->range); return IIO_VAL_INT; exit: @@ -473,6 +705,8 @@ static const struct acpi_device_id ak_acpi_match[] = { {"AK8975", AK8975}, {"AK8963", AK8963}, {"INVN6500", AK8963}, + {"AK09911", AK09911}, + {"AK09912", AK09912}, { }, }; MODULE_DEVICE_TABLE(acpi, ak_acpi_match); @@ -498,6 +732,7 @@ static int ak8975_probe(struct i2c_client *client, int eoc_gpio; int err; const char *name = NULL; + enum asahi_compass_chipset chipset; /* Grab and set up the supplied GPIO. */ if (client->dev.platform_data) @@ -537,42 +772,49 @@ static int ak8975_probe(struct i2c_client *client, /* id will be NULL when enumerated via ACPI */ if (id) { - data->chipset = - (enum asahi_compass_chipset)(id->driver_data); + chipset = (enum asahi_compass_chipset)(id->driver_data); name = id->name; } else if (ACPI_HANDLE(&client->dev)) - name = ak8975_match_acpi_device(&client->dev, &data->chipset); + name = ak8975_match_acpi_device(&client->dev, &chipset); else return -ENOSYS; + if (chipset >= AK_MAX_TYPE) { + dev_err(&client->dev, "AKM device type unsupported: %d\n", + chipset); + return -ENODEV; + } + + data->def = &ak_def_array[chipset]; + err = ak8975_who_i_am(client, data->def->type); + if (err < 0) { + dev_err(&client->dev, "Unexpected device\n"); + return err; + } dev_dbg(&client->dev, "Asahi compass chip %s\n", name); /* Perform some basic start-of-day setup of the device. */ err = ak8975_setup(client); if (err < 0) { - dev_err(&client->dev, "AK8975 initialization fails\n"); + dev_err(&client->dev, "%s initialization fails\n", name); return err; } - data->client = client; mutex_init(&data->lock); - data->eoc_gpio = eoc_gpio; indio_dev->dev.parent = &client->dev; indio_dev->channels = ak8975_channels; indio_dev->num_channels = ARRAY_SIZE(ak8975_channels); indio_dev->info = &ak8975_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = name; - err = devm_iio_device_register(&client->dev, indio_dev); - if (err < 0) - return err; - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id ak8975_id[] = { {"ak8975", AK8975}, {"ak8963", AK8963}, + {"ak09911", AK09911}, + {"ak09912", AK09912}, {} }; @@ -581,14 +823,20 @@ MODULE_DEVICE_TABLE(i2c, ak8975_id); static const struct of_device_id ak8975_of_match[] = { { .compatible = "asahi-kasei,ak8975", }, { .compatible = "ak8975", }, - { } + { .compatible = "asahi-kasei,ak8963", }, + { .compatible = "ak8963", }, + { .compatible = "asahi-kasei,ak09911", }, + { .compatible = "ak09911", }, + { .compatible = "asahi-kasei,ak09912", }, + { .compatible = "ak09912", }, + {} }; MODULE_DEVICE_TABLE(of, ak8975_of_match); static struct i2c_driver ak8975_driver = { .driver = { .name = "ak8975", - .of_match_table = ak8975_of_match, + .of_match_table = of_match_ptr(ak8975_of_match), .acpi_match_table = ACPI_PTR(ak_acpi_match), }, .probe = ak8975_probe, diff --git a/drivers/iio/pressure/bmp280.c b/drivers/iio/pressure/bmp280.c index 75038dacfff1..7c623e2bd633 100644 --- a/drivers/iio/pressure/bmp280.c +++ b/drivers/iio/pressure/bmp280.c @@ -80,16 +80,12 @@ struct bmp280_data { s32 t_fine; }; -/* Compensation parameters. */ -struct bmp280_comp_temp { - u16 dig_t1; - s16 dig_t2, dig_t3; -}; - -struct bmp280_comp_press { - u16 dig_p1; - s16 dig_p2, dig_p3, dig_p4, dig_p5, dig_p6, dig_p7, dig_p8, dig_p9; -}; +/* + * These enums are used for indexing into the array of compensation + * parameters. + */ +enum { T1, T2, T3 }; +enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 }; static const struct iio_chan_spec bmp280_channels[] = { { @@ -141,54 +137,6 @@ static const struct regmap_config bmp280_regmap_config = { .volatile_reg = bmp280_is_volatile_reg, }; -static int bmp280_read_compensation_temp(struct bmp280_data *data, - struct bmp280_comp_temp *comp) -{ - int ret; - __le16 buf[BMP280_COMP_TEMP_REG_COUNT / 2]; - - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START, - buf, BMP280_COMP_TEMP_REG_COUNT); - if (ret < 0) { - dev_err(&data->client->dev, - "failed to read temperature calibration parameters\n"); - return ret; - } - - comp->dig_t1 = (u16) le16_to_cpu(buf[0]); - comp->dig_t2 = (s16) le16_to_cpu(buf[1]); - comp->dig_t3 = (s16) le16_to_cpu(buf[2]); - - return 0; -} - -static int bmp280_read_compensation_press(struct bmp280_data *data, - struct bmp280_comp_press *comp) -{ - int ret; - __le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2]; - - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START, - buf, BMP280_COMP_PRESS_REG_COUNT); - if (ret < 0) { - dev_err(&data->client->dev, - "failed to read pressure calibration parameters\n"); - return ret; - } - - comp->dig_p1 = (u16) le16_to_cpu(buf[0]); - comp->dig_p2 = (s16) le16_to_cpu(buf[1]); - comp->dig_p3 = (s16) le16_to_cpu(buf[2]); - comp->dig_p4 = (s16) le16_to_cpu(buf[3]); - comp->dig_p5 = (s16) le16_to_cpu(buf[4]); - comp->dig_p6 = (s16) le16_to_cpu(buf[5]); - comp->dig_p7 = (s16) le16_to_cpu(buf[6]); - comp->dig_p8 = (s16) le16_to_cpu(buf[7]); - comp->dig_p9 = (s16) le16_to_cpu(buf[8]); - - return 0; -} - /* * Returns temperature in DegC, resolution is 0.01 DegC. Output value of * "5123" equals 51.23 DegC. t_fine carries fine temperature as global @@ -197,21 +145,35 @@ static int bmp280_read_compensation_press(struct bmp280_data *data, * Taken from datasheet, Section 3.11.3, "Compensation formula". */ static s32 bmp280_compensate_temp(struct bmp280_data *data, - struct bmp280_comp_temp *comp, s32 adc_temp) { - s32 var1, var2, t; + int ret; + s32 var1, var2; + __le16 buf[BMP280_COMP_TEMP_REG_COUNT / 2]; - var1 = (((adc_temp >> 3) - ((s32) comp->dig_t1 << 1)) * - ((s32) comp->dig_t2)) >> 11; - var2 = (((((adc_temp >> 4) - ((s32) comp->dig_t1)) * - ((adc_temp >> 4) - ((s32) comp->dig_t1))) >> 12) * - ((s32) comp->dig_t3)) >> 14; + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START, + buf, BMP280_COMP_TEMP_REG_COUNT); + if (ret < 0) { + dev_err(&data->client->dev, + "failed to read temperature calibration parameters\n"); + return ret; + } - data->t_fine = var1 + var2; - t = (data->t_fine * 5 + 128) >> 8; + /* + * The double casts are necessary because le16_to_cpu returns an + * unsigned 16-bit value. Casting that value directly to a + * signed 32-bit will not do proper sign extension. + * + * Conversely, T1 and P1 are unsigned values, so they can be + * cast straight to the larger type. + */ + var1 = (((adc_temp >> 3) - ((s32)le16_to_cpu(buf[T1]) << 1)) * + ((s32)(s16)le16_to_cpu(buf[T2]))) >> 11; + var2 = (((((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1]))) * + ((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1])))) >> 12) * + ((s32)(s16)le16_to_cpu(buf[T3]))) >> 14; - return t; + return (data->t_fine * 5 + 128) >> 8; } /* @@ -222,29 +184,38 @@ static s32 bmp280_compensate_temp(struct bmp280_data *data, * Taken from datasheet, Section 3.11.3, "Compensation formula". */ static u32 bmp280_compensate_press(struct bmp280_data *data, - struct bmp280_comp_press *comp, s32 adc_press) { + int ret; s64 var1, var2, p; + __le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2]; + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START, + buf, BMP280_COMP_PRESS_REG_COUNT); + if (ret < 0) { + dev_err(&data->client->dev, + "failed to read pressure calibration parameters\n"); + return ret; + } - var1 = ((s64) data->t_fine) - 128000; - var2 = var1 * var1 * (s64) comp->dig_p6; - var2 = var2 + ((var1 * (s64) comp->dig_p5) << 17); - var2 = var2 + (((s64) comp->dig_p4) << 35); - var1 = ((var1 * var1 * (s64) comp->dig_p3) >> 8) + - ((var1 * (s64) comp->dig_p2) << 12); - var1 = (((((s64) 1) << 47) + var1)) * ((s64) comp->dig_p1) >> 33; + var1 = ((s64)data->t_fine) - 128000; + var2 = var1 * var1 * (s64)(s16)le16_to_cpu(buf[P6]); + var2 += (var1 * (s64)(s16)le16_to_cpu(buf[P5])) << 17; + var2 += ((s64)(s16)le16_to_cpu(buf[P4])) << 35; + var1 = ((var1 * var1 * (s64)(s16)le16_to_cpu(buf[P3])) >> 8) + + ((var1 * (s64)(s16)le16_to_cpu(buf[P2])) << 12); + var1 = ((((s64)1) << 47) + var1) * ((s64)le16_to_cpu(buf[P1])) >> 33; if (var1 == 0) return 0; - p = ((((s64) 1048576 - adc_press) << 31) - var2) * 3125; + p = ((((s64)1048576 - adc_press) << 31) - var2) * 3125; p = div64_s64(p, var1); - var1 = (((s64) comp->dig_p9) * (p >> 13) * (p >> 13)) >> 25; - var2 = (((s64) comp->dig_p8) * p) >> 19; - p = ((p + var1 + var2) >> 8) + (((s64) comp->dig_p7) << 4); + var1 = (((s64)(s16)le16_to_cpu(buf[P9])) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((s64)(s16)le16_to_cpu(buf[P8])) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((s64)(s16)le16_to_cpu(buf[P7])) << 4); - return (u32) p; + return (u32)p; } static int bmp280_read_temp(struct bmp280_data *data, @@ -253,11 +224,6 @@ static int bmp280_read_temp(struct bmp280_data *data, int ret; __be32 tmp = 0; s32 adc_temp, comp_temp; - struct bmp280_comp_temp comp; - - ret = bmp280_read_compensation_temp(data, &comp); - if (ret < 0) - return ret; ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, (u8 *) &tmp, 3); @@ -267,7 +233,7 @@ static int bmp280_read_temp(struct bmp280_data *data, } adc_temp = be32_to_cpu(tmp) >> 12; - comp_temp = bmp280_compensate_temp(data, &comp, adc_temp); + comp_temp = bmp280_compensate_temp(data, adc_temp); /* * val might be NULL if we're called by the read_press routine, @@ -288,11 +254,6 @@ static int bmp280_read_press(struct bmp280_data *data, __be32 tmp = 0; s32 adc_press; u32 comp_press; - struct bmp280_comp_press comp; - - ret = bmp280_read_compensation_press(data, &comp); - if (ret < 0) - return ret; /* Read and compensate temperature so we get a reading of t_fine. */ ret = bmp280_read_temp(data, NULL); @@ -307,7 +268,7 @@ static int bmp280_read_press(struct bmp280_data *data, } adc_press = be32_to_cpu(tmp) >> 12; - comp_press = bmp280_compensate_press(data, &comp, adc_press); + comp_press = bmp280_compensate_press(data, adc_press); *val = comp_press; *val2 = 256000; @@ -366,7 +327,7 @@ static int bmp280_chip_init(struct bmp280_data *data) BMP280_MODE_NORMAL); if (ret < 0) { dev_err(&data->client->dev, - "failed to write config register\n"); + "failed to write ctrl_meas register\n"); return ret; } @@ -394,7 +355,6 @@ static int bmp280_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - i2c_set_clientdata(client, indio_dev); data = iio_priv(indio_dev); mutex_init(&data->lock); data->client = client; diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 0c8cdf58f6a1..41a8d8ffa0de 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -17,3 +17,20 @@ config AS3935 module will be called as3935 endmenu + +menu "Proximity sensors" + +config SX9500 + tristate "SX9500 Semtech proximity sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + depends on I2C + help + Say Y here to build a driver for Semtech's SX9500 capacitive + proximity/button sensor. + + To compile this driver as a module, choose M here: the + module will be called sx9500. + +endmenu diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 743adee1c8bf..9818dc562abd 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -4,3 +4,4 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o +obj-$(CONFIG_SX9500) += sx9500.o diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c new file mode 100644 index 000000000000..74dff4e4a11a --- /dev/null +++ b/drivers/iio/proximity/sx9500.c @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2014 Intel Corporation + * + * Driver for Semtech's SX9500 capacitive proximity/button solution. + * Datasheet available at + * <http://www.semtech.com/images/datasheet/sx9500.pdf>. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#define SX9500_DRIVER_NAME "sx9500" +#define SX9500_IRQ_NAME "sx9500_event" +#define SX9500_GPIO_NAME "sx9500_gpio" + +/* Register definitions. */ +#define SX9500_REG_IRQ_SRC 0x00 +#define SX9500_REG_STAT 0x01 +#define SX9500_REG_IRQ_MSK 0x03 + +#define SX9500_REG_PROX_CTRL0 0x06 +#define SX9500_REG_PROX_CTRL1 0x07 +#define SX9500_REG_PROX_CTRL2 0x08 +#define SX9500_REG_PROX_CTRL3 0x09 +#define SX9500_REG_PROX_CTRL4 0x0a +#define SX9500_REG_PROX_CTRL5 0x0b +#define SX9500_REG_PROX_CTRL6 0x0c +#define SX9500_REG_PROX_CTRL7 0x0d +#define SX9500_REG_PROX_CTRL8 0x0e + +#define SX9500_REG_SENSOR_SEL 0x20 +#define SX9500_REG_USE_MSB 0x21 +#define SX9500_REG_USE_LSB 0x22 +#define SX9500_REG_AVG_MSB 0x23 +#define SX9500_REG_AVG_LSB 0x24 +#define SX9500_REG_DIFF_MSB 0x25 +#define SX9500_REG_DIFF_LSB 0x26 +#define SX9500_REG_OFFSET_MSB 0x27 +#define SX9500_REG_OFFSET_LSB 0x28 + +#define SX9500_REG_RESET 0x7f + +/* Write this to REG_RESET to do a soft reset. */ +#define SX9500_SOFT_RESET 0xde + +#define SX9500_SCAN_PERIOD_MASK GENMASK(6, 4) +#define SX9500_SCAN_PERIOD_SHIFT 4 + +/* + * These serve for identifying IRQ source in the IRQ_SRC register, and + * also for masking the IRQs in the IRQ_MSK register. + */ +#define SX9500_CLOSE_IRQ BIT(6) +#define SX9500_FAR_IRQ BIT(5) +#define SX9500_CONVDONE_IRQ BIT(3) + +#define SX9500_PROXSTAT_SHIFT 4 + +#define SX9500_NUM_CHANNELS 4 + +struct sx9500_data { + struct mutex mutex; + struct i2c_client *client; + struct iio_trigger *trig; + struct regmap *regmap; + /* + * Last reading of the proximity status for each channel. We + * only send an event to user space when this changes. + */ + bool prox_stat[SX9500_NUM_CHANNELS]; + bool event_enabled[SX9500_NUM_CHANNELS]; + bool trigger_enabled; + u16 *buffer; +}; + +static const struct iio_event_spec sx9500_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define SX9500_CHANNEL(idx) \ + { \ + .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = sx9500_events, \ + .num_event_specs = ARRAY_SIZE(sx9500_events), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .shift = 0, \ + }, \ + } + +static const struct iio_chan_spec sx9500_channels[] = { + SX9500_CHANNEL(0), + SX9500_CHANNEL(1), + SX9500_CHANNEL(2), + SX9500_CHANNEL(3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct { + int val; + int val2; +} sx9500_samp_freq_table[] = { + {33, 333333}, + {16, 666666}, + {11, 111111}, + {8, 333333}, + {6, 666666}, + {5, 0}, + {3, 333333}, + {2, 500000}, +}; + +static const struct regmap_range sx9500_writable_reg_ranges[] = { + regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK), + regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8), + regmap_reg_range(SX9500_REG_SENSOR_SEL, SX9500_REG_SENSOR_SEL), + regmap_reg_range(SX9500_REG_OFFSET_MSB, SX9500_REG_OFFSET_LSB), + regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET), +}; + +static const struct regmap_access_table sx9500_writeable_regs = { + .yes_ranges = sx9500_writable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(sx9500_writable_reg_ranges), +}; + +/* + * All allocated registers are readable, so we just list unallocated + * ones. + */ +static const struct regmap_range sx9500_non_readable_reg_ranges[] = { + regmap_reg_range(SX9500_REG_STAT + 1, SX9500_REG_STAT + 1), + regmap_reg_range(SX9500_REG_IRQ_MSK + 1, SX9500_REG_PROX_CTRL0 - 1), + regmap_reg_range(SX9500_REG_PROX_CTRL8 + 1, SX9500_REG_SENSOR_SEL - 1), + regmap_reg_range(SX9500_REG_OFFSET_LSB + 1, SX9500_REG_RESET - 1), +}; + +static const struct regmap_access_table sx9500_readable_regs = { + .no_ranges = sx9500_non_readable_reg_ranges, + .n_no_ranges = ARRAY_SIZE(sx9500_non_readable_reg_ranges), +}; + +static const struct regmap_range sx9500_volatile_reg_ranges[] = { + regmap_reg_range(SX9500_REG_IRQ_SRC, SX9500_REG_STAT), + regmap_reg_range(SX9500_REG_USE_MSB, SX9500_REG_OFFSET_LSB), + regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET), +}; + +static const struct regmap_access_table sx9500_volatile_regs = { + .yes_ranges = sx9500_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(sx9500_volatile_reg_ranges), +}; + +static const struct regmap_config sx9500_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SX9500_REG_RESET, + .cache_type = REGCACHE_RBTREE, + + .wr_table = &sx9500_writeable_regs, + .rd_table = &sx9500_readable_regs, + .volatile_table = &sx9500_volatile_regs, +}; + +static int sx9500_read_proximity(struct sx9500_data *data, + const struct iio_chan_spec *chan, + int *val) +{ + int ret; + __be16 regval; + + ret = regmap_write(data->regmap, SX9500_REG_SENSOR_SEL, chan->channel); + if (ret < 0) + return ret; + + ret = regmap_bulk_read(data->regmap, SX9500_REG_USE_MSB, ®val, 2); + if (ret < 0) + return ret; + + *val = 32767 - (s16)be16_to_cpu(regval); + + return IIO_VAL_INT; +} + +static int sx9500_read_samp_freq(struct sx9500_data *data, + int *val, int *val2) +{ + int ret; + unsigned int regval; + + mutex_lock(&data->mutex); + ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, ®val); + mutex_unlock(&data->mutex); + + if (ret < 0) + return ret; + + regval = (regval & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT; + *val = sx9500_samp_freq_table[regval].val; + *val2 = sx9500_samp_freq_table[regval].val2; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int sx9500_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct sx9500_data *data = iio_priv(indio_dev); + int ret; + + switch (chan->type) { + case IIO_PROXIMITY: + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + mutex_lock(&data->mutex); + ret = sx9500_read_proximity(data, chan, val); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return sx9500_read_samp_freq(data, val, val2); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int sx9500_set_samp_freq(struct sx9500_data *data, + int val, int val2) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sx9500_samp_freq_table); i++) + if (val == sx9500_samp_freq_table[i].val && + val2 == sx9500_samp_freq_table[i].val2) + break; + + if (i == ARRAY_SIZE(sx9500_samp_freq_table)) + return -EINVAL; + + mutex_lock(&data->mutex); + + ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0, + SX9500_SCAN_PERIOD_MASK, + i << SX9500_SCAN_PERIOD_SHIFT); + + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9500_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct sx9500_data *data = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_PROXIMITY: + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return sx9500_set_samp_freq(data, val, val2); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static irqreturn_t sx9500_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct sx9500_data *data = iio_priv(indio_dev); + + if (data->trigger_enabled) + iio_trigger_poll(data->trig); + + /* + * Even if no event is enabled, we need to wake the thread to + * clear the interrupt state by reading SX9500_REG_IRQ_SRC. It + * is not possible to do that here because regmap_read takes a + * mutex. + */ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t sx9500_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct sx9500_data *data = iio_priv(indio_dev); + int ret; + unsigned int val, chan; + + mutex_lock(&data->mutex); + + ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val); + if (ret < 0) { + dev_err(&data->client->dev, "i2c transfer error in irq\n"); + goto out; + } + + if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ))) + goto out; + + ret = regmap_read(data->regmap, SX9500_REG_STAT, &val); + if (ret < 0) { + dev_err(&data->client->dev, "i2c transfer error in irq\n"); + goto out; + } + + val >>= SX9500_PROXSTAT_SHIFT; + for (chan = 0; chan < SX9500_NUM_CHANNELS; chan++) { + int dir; + u64 ev; + bool new_prox = val & BIT(chan); + + if (!data->event_enabled[chan]) + continue; + if (new_prox == data->prox_stat[chan]) + /* No change on this channel. */ + continue; + + dir = new_prox ? IIO_EV_DIR_FALLING : + IIO_EV_DIR_RISING; + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, + chan, + IIO_EV_TYPE_THRESH, + dir); + iio_push_event(indio_dev, ev, iio_get_time_ns()); + data->prox_stat[chan] = new_prox; + } + +out: + mutex_unlock(&data->mutex); + + return IRQ_HANDLED; +} + +static int sx9500_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct sx9500_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH || + dir != IIO_EV_DIR_EITHER) + return -EINVAL; + + return data->event_enabled[chan->channel]; +} + +static int sx9500_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct sx9500_data *data = iio_priv(indio_dev); + int ret, i; + bool any_active = false; + unsigned int irqmask; + + if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH || + dir != IIO_EV_DIR_EITHER) + return -EINVAL; + + mutex_lock(&data->mutex); + + data->event_enabled[chan->channel] = state; + + for (i = 0; i < SX9500_NUM_CHANNELS; i++) + if (data->event_enabled[i]) { + any_active = true; + break; + } + + irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ; + if (any_active) + ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK, + irqmask, irqmask); + else + ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK, + irqmask, 0); + + mutex_unlock(&data->mutex); + + return ret; +} + +static int sx9500_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct sx9500_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + kfree(data->buffer); + data->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL); + mutex_unlock(&data->mutex); + + if (data->buffer == NULL) + return -ENOMEM; + + return 0; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "2.500000 3.333333 5 6.666666 8.333333 11.111111 16.666666 33.333333"); + +static struct attribute *sx9500_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group sx9500_attribute_group = { + .attrs = sx9500_attributes, +}; + +static const struct iio_info sx9500_info = { + .driver_module = THIS_MODULE, + .attrs = &sx9500_attribute_group, + .read_raw = &sx9500_read_raw, + .write_raw = &sx9500_write_raw, + .read_event_config = &sx9500_read_event_config, + .write_event_config = &sx9500_write_event_config, + .update_scan_mode = &sx9500_update_scan_mode, +}; + +static int sx9500_set_trigger_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct sx9500_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + + ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK, + SX9500_CONVDONE_IRQ, + state ? SX9500_CONVDONE_IRQ : 0); + if (ret == 0) + data->trigger_enabled = state; + + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_trigger_ops sx9500_trigger_ops = { + .set_trigger_state = sx9500_set_trigger_state, + .owner = THIS_MODULE, +}; + +static irqreturn_t sx9500_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct sx9500_data *data = iio_priv(indio_dev); + int val, bit, ret, i = 0; + + mutex_lock(&data->mutex); + + for_each_set_bit(bit, indio_dev->buffer->scan_mask, + indio_dev->masklength) { + ret = sx9500_read_proximity(data, &indio_dev->channels[bit], + &val); + if (ret < 0) + goto out; + + data->buffer[i++] = val; + } + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_get_time_ns()); + +out: + mutex_unlock(&data->mutex); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +struct sx9500_reg_default { + u8 reg; + u8 def; +}; + +static const struct sx9500_reg_default sx9500_default_regs[] = { + { + .reg = SX9500_REG_PROX_CTRL1, + /* Shield enabled, small range. */ + .def = 0x43, + }, + { + .reg = SX9500_REG_PROX_CTRL2, + /* x8 gain, 167kHz frequency, finest resolution. */ + .def = 0x77, + }, + { + .reg = SX9500_REG_PROX_CTRL3, + /* Doze enabled, 2x scan period doze, no raw filter. */ + .def = 0x40, + }, + { + .reg = SX9500_REG_PROX_CTRL4, + /* Average threshold. */ + .def = 0x30, + }, + { + .reg = SX9500_REG_PROX_CTRL5, + /* + * Debouncer off, lowest average negative filter, + * highest average postive filter. + */ + .def = 0x0f, + }, + { + .reg = SX9500_REG_PROX_CTRL6, + /* Proximity detection threshold: 280 */ + .def = 0x0e, + }, + { + .reg = SX9500_REG_PROX_CTRL7, + /* + * No automatic compensation, compensate each pin + * independently, proximity hysteresis: 32, close + * debouncer off, far debouncer off. + */ + .def = 0x00, + }, + { + .reg = SX9500_REG_PROX_CTRL8, + /* No stuck timeout, no periodic compensation. */ + .def = 0x00, + }, + { + .reg = SX9500_REG_PROX_CTRL0, + /* Scan period: 30ms, all sensors enabled. */ + .def = 0x0f, + }, +}; + +static int sx9500_init_device(struct iio_dev *indio_dev) +{ + struct sx9500_data *data = iio_priv(indio_dev); + int ret, i; + unsigned int val; + + ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0); + if (ret < 0) + return ret; + + ret = regmap_write(data->regmap, SX9500_REG_RESET, + SX9500_SOFT_RESET); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(sx9500_default_regs); i++) { + ret = regmap_write(data->regmap, + sx9500_default_regs[i].reg, + sx9500_default_regs[i].def); + if (ret < 0) + return ret; + } + + return 0; +} + +static int sx9500_gpio_probe(struct i2c_client *client, + struct sx9500_data *data) +{ + struct device *dev; + struct gpio_desc *gpio; + int ret; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* data ready gpio interrupt pin */ + gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0); + if (IS_ERR(gpio)) { + dev_err(dev, "acpi gpio get index failed\n"); + return PTR_ERR(gpio); + } + + ret = gpiod_direction_input(gpio); + if (ret) + return ret; + + ret = gpiod_to_irq(gpio); + + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + + return ret; +} + +static int sx9500_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct sx9500_data *data; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (indio_dev == NULL) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + mutex_init(&data->mutex); + data->trigger_enabled = false; + + data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + sx9500_init_device(indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = SX9500_DRIVER_NAME; + indio_dev->channels = sx9500_channels; + indio_dev->num_channels = ARRAY_SIZE(sx9500_channels); + indio_dev->info = &sx9500_info; + indio_dev->modes = INDIO_DIRECT_MODE; + i2c_set_clientdata(client, indio_dev); + + if (client->irq <= 0) + client->irq = sx9500_gpio_probe(client, data); + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + sx9500_irq_handler, sx9500_irq_thread_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + SX9500_IRQ_NAME, indio_dev); + if (ret < 0) + return ret; + + data->trig = devm_iio_trigger_alloc(&client->dev, + "%s-dev%d", indio_dev->name, indio_dev->id); + if (!data->trig) + return -ENOMEM; + + data->trig->dev.parent = &client->dev; + data->trig->ops = &sx9500_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + + ret = iio_trigger_register(data->trig); + if (ret) + return ret; + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + sx9500_trigger_handler, NULL); + if (ret < 0) + goto out_trigger_unregister; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto out_buffer_cleanup; + + return 0; + +out_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +out_trigger_unregister: + if (client->irq > 0) + iio_trigger_unregister(data->trig); + + return ret; +} + +static int sx9500_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct sx9500_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (client->irq > 0) + iio_trigger_unregister(data->trig); + kfree(data->buffer); + + return 0; +} + +static const struct acpi_device_id sx9500_acpi_match[] = { + {"SSX9500", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match); + +static const struct i2c_device_id sx9500_id[] = { + {"sx9500", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sx9500_id); + +static struct i2c_driver sx9500_driver = { + .driver = { + .name = SX9500_DRIVER_NAME, + .acpi_match_table = ACPI_PTR(sx9500_acpi_match), + }, + .probe = sx9500_probe, + .remove = sx9500_remove, + .id_table = sx9500_id, +}; +module_i2c_driver(sx9500_driver); + +MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>"); +MODULE_DESCRIPTION("Driver for Semtech SX9500 proximity sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 254c7e906127..3dfab2bc6d69 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -135,6 +135,7 @@ static int iio_sysfs_trigger_probe(int id) struct iio_sysfs_trig *t; int ret; bool foundit = false; + mutex_lock(&iio_sysfs_trig_list_mut); list_for_each_entry(t, &iio_sysfs_trig_list, l) if (id == t->id) { @@ -185,6 +186,7 @@ static int iio_sysfs_trigger_remove(int id) { bool foundit = false; struct iio_sysfs_trig *t; + mutex_lock(&iio_sysfs_trig_list_mut); list_for_each_entry(t, &iio_sysfs_trig_list, l) if (id == t->id) { diff --git a/drivers/staging/iio/Documentation/iio_event_monitor.c b/drivers/staging/iio/Documentation/iio_event_monitor.c index 940ed2399e73..def236abcb3e 100644 --- a/drivers/staging/iio/Documentation/iio_event_monitor.c +++ b/drivers/staging/iio/Documentation/iio_event_monitor.c @@ -49,6 +49,8 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_CCT] = "cct", [IIO_PRESSURE] = "pressure", [IIO_HUMIDITYRELATIVE] = "humidityrelative", + [IIO_ACTIVITY] = "activity", + [IIO_STEPS] = "steps", }; static const char * const iio_ev_type_text[] = { @@ -57,6 +59,7 @@ static const char * const iio_ev_type_text[] = { [IIO_EV_TYPE_ROC] = "roc", [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive", [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", + [IIO_EV_TYPE_INSTANCE] = "instance", }; static const char * const iio_ev_dir_text[] = { @@ -92,6 +95,10 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_NORTH_TRUE] = "from_north_true", [IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp", [IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp", + [IIO_MOD_RUNNING] = "running", + [IIO_MOD_JOGGING] = "jogging", + [IIO_MOD_WALKING] = "walking", + [IIO_MOD_STILL] = "still", }; static bool event_is_known(struct iio_event_data *event) @@ -121,6 +128,8 @@ static bool event_is_known(struct iio_event_data *event) case IIO_CCT: case IIO_PRESSURE: case IIO_HUMIDITYRELATIVE: + case IIO_ACTIVITY: + case IIO_STEPS: break; default: return false; @@ -154,6 +163,10 @@ static bool event_is_known(struct iio_event_data *event) case IIO_MOD_NORTH_TRUE: case IIO_MOD_NORTH_MAGN_TILT_COMP: case IIO_MOD_NORTH_TRUE_TILT_COMP: + case IIO_MOD_RUNNING: + case IIO_MOD_JOGGING: + case IIO_MOD_WALKING: + case IIO_MOD_STILL: break; default: return false; @@ -165,6 +178,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_EV_TYPE_ROC: case IIO_EV_TYPE_THRESH_ADAPTIVE: case IIO_EV_TYPE_MAG_ADAPTIVE: + case IIO_EV_TYPE_INSTANCE: break; default: return false; @@ -174,6 +188,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_EV_DIR_EITHER: case IIO_EV_DIR_RISING: case IIO_EV_DIR_FALLING: + case IIO_EV_DIR_NONE: break; default: return false; @@ -214,9 +229,11 @@ static void print_event(struct iio_event_data *event) else if (chan >= 0) printf("channel: %d, ", chan); - printf("evtype: %s, direction: %s\n", - iio_ev_type_text[ev_type], - iio_ev_dir_text[dir]); + printf("evtype: %s", iio_ev_type_text[ev_type]); + + if (dir != IIO_EV_DIR_NONE) + printf(", direction: %s", iio_ev_dir_text[dir]); + printf("\n"); } int main(int argc, char **argv) diff --git a/drivers/staging/iio/Documentation/ring.txt b/drivers/staging/iio/Documentation/ring.txt index e1da43381d0e..18718fcaf259 100644 --- a/drivers/staging/iio/Documentation/ring.txt +++ b/drivers/staging/iio/Documentation/ring.txt @@ -39,9 +39,9 @@ request_update If parameters have changed that require reinitialization or configuration of the buffer this will trigger it. -get_bytes_per_datum, set_bytes_per_datum - Get/set the number of bytes for a complete scan. (All samples + timestamp) +set_bytes_per_datum + Set the number of bytes for a complete scan. (All samples + timestamp) -get_length / set_length - Get/set the number of complete scans that may be held by the buffer. +set_length + Set the number of complete scans that may be held by the buffer. diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c index f5e145caffa9..b78c9c5d5588 100644 --- a/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -716,14 +716,6 @@ static int lis3l02dq_probe(struct spi_device *spi) if (ret) return ret; - ret = iio_buffer_register(indio_dev, - lis3l02dq_channels, - ARRAY_SIZE(lis3l02dq_channels)); - if (ret) { - dev_err(&spi->dev, "failed to initialize the buffer\n"); - goto error_unreg_buffer_funcs; - } - if (spi->irq) { ret = request_threaded_irq(st->us->irq, &lis3l02dq_th, @@ -732,7 +724,7 @@ static int lis3l02dq_probe(struct spi_device *spi) "lis3l02dq", indio_dev); if (ret) - goto error_uninitialize_buffer; + goto error_unreg_buffer_funcs; ret = lis3l02dq_probe_trigger(indio_dev); if (ret) @@ -756,8 +748,6 @@ error_remove_trigger: error_free_interrupt: if (spi->irq) free_irq(st->us->irq, indio_dev); -error_uninitialize_buffer: - iio_buffer_unregister(indio_dev); error_unreg_buffer_funcs: lis3l02dq_unconfigure_buffer(indio_dev); return ret; @@ -804,7 +794,6 @@ static int lis3l02dq_remove(struct spi_device *spi) free_irq(st->us->irq, indio_dev); lis3l02dq_remove_trigger(indio_dev); - iio_buffer_unregister(indio_dev); lis3l02dq_unconfigure_buffer(indio_dev); return 0; diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 9efc77b0ebdd..1fd90090a633 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -393,7 +393,7 @@ int lis3l02dq_configure_buffer(struct iio_dev *indio_dev) int ret; struct iio_buffer *buffer; - buffer = iio_kfifo_allocate(indio_dev); + buffer = iio_kfifo_allocate(); if (!buffer) return -ENOMEM; diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index e4e56391487a..31fb2182c198 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -223,33 +223,6 @@ error_ret: return ret; } -#ifdef SCA3000_DEBUG -/** - * sca3000_check_status() check the status register - * - * Only used for debugging purposes - **/ -static int sca3000_check_status(struct device *dev) -{ - int ret; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct sca3000_state *st = iio_priv(indio_dev); - - mutex_lock(&st->lock); - ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_STATUS, 1); - if (ret < 0) - goto error_ret; - if (st->rx[0] & SCA3000_EEPROM_CS_ERROR) - dev_err(dev, "eeprom error\n"); - if (st->rx[0] & SCA3000_SPI_FRAME_ERROR) - dev_err(dev, "Previous SPI Frame was corrupt\n"); - -error_ret: - mutex_unlock(&st->lock); - return ret; -} -#endif /* SCA3000_DEBUG */ - /** * sca3000_show_rev() - sysfs interface to read the chip revision number **/ @@ -459,6 +432,8 @@ static const struct iio_chan_spec sca3000_channels_with_temp[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), + /* No buffer support */ + .scan_index = -1, }, }; @@ -1154,17 +1129,6 @@ static int sca3000_probe(struct spi_device *spi) if (ret < 0) return ret; - ret = iio_buffer_register(indio_dev, - sca3000_channels, - ARRAY_SIZE(sca3000_channels)); - if (ret < 0) - goto error_unregister_dev; - if (indio_dev->buffer) { - iio_scan_mask_set(indio_dev, indio_dev->buffer, 0); - iio_scan_mask_set(indio_dev, indio_dev->buffer, 1); - iio_scan_mask_set(indio_dev, indio_dev->buffer, 2); - } - if (spi->irq) { ret = request_threaded_irq(spi->irq, NULL, @@ -1173,7 +1137,7 @@ static int sca3000_probe(struct spi_device *spi) "sca3000", indio_dev); if (ret) - goto error_unregister_ring; + goto error_unregister_dev; } sca3000_register_ring_funcs(indio_dev); ret = sca3000_clean_setup(st); @@ -1184,8 +1148,6 @@ static int sca3000_probe(struct spi_device *spi) error_free_irq: if (spi->irq) free_irq(spi->irq, indio_dev); -error_unregister_ring: - iio_buffer_unregister(indio_dev); error_unregister_dev: iio_device_unregister(indio_dev); return ret; @@ -1219,7 +1181,6 @@ static int sca3000_remove(struct spi_device *spi) if (spi->irq) free_irq(spi->irq, indio_dev); iio_device_unregister(indio_dev); - iio_buffer_unregister(indio_dev); sca3000_unconfigure_ring(indio_dev); return 0; diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index 157827651bfa..f76a26885808 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -129,26 +129,11 @@ error_ret: return ret ? ret : num_read; } -/* This is only valid with all 3 elements enabled */ -static int sca3000_ring_get_length(struct iio_buffer *r) -{ - return 64; -} - -/* only valid if resolution is kept at 11bits */ -static int sca3000_ring_get_bytes_per_datum(struct iio_buffer *r) -{ - return 6; -} - static bool sca3000_ring_buf_data_available(struct iio_buffer *r) { return r->stufftoread; } -static IIO_BUFFER_ENABLE_ATTR; -static IIO_BUFFER_LENGTH_ATTR; - /** * sca3000_query_ring_int() is the hardware ring status interrupt enabled **/ @@ -238,20 +223,13 @@ static IIO_DEVICE_ATTR(in_accel_scale, * only apply to the ring buffer. At all times full rate and accuracy * is available via direct reading from registers. */ -static struct attribute *sca3000_ring_attributes[] = { - &dev_attr_length.attr, - &dev_attr_enable.attr, +static const struct attribute *sca3000_ring_attributes[] = { &iio_dev_attr_50_percent.dev_attr.attr, &iio_dev_attr_75_percent.dev_attr.attr, &iio_dev_attr_in_accel_scale.dev_attr.attr, NULL, }; -static struct attribute_group sca3000_ring_attr = { - .attrs = sca3000_ring_attributes, - .name = "buffer", -}; - static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev) { struct iio_buffer *buf; @@ -264,7 +242,8 @@ static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev) ring->private = indio_dev; buf = &ring->buf; buf->stufftoread = 0; - buf->attrs = &sca3000_ring_attr; + buf->length = 64; + buf->attrs = sca3000_ring_attributes; iio_buffer_init(buf); return buf; @@ -277,8 +256,6 @@ static void sca3000_ring_release(struct iio_buffer *r) static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = { .read_first_n = &sca3000_read_first_n_hw_rb, - .get_length = &sca3000_ring_get_length, - .get_bytes_per_datum = &sca3000_ring_get_bytes_per_datum, .data_available = sca3000_ring_buf_data_available, .release = sca3000_ring_release, }; diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index f6526aa22e8a..6f8ce6c6574b 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -612,7 +612,7 @@ static int ad7192_probe(struct spi_device *spi) const struct ad7192_platform_data *pdata = spi->dev.platform_data; struct ad7192_state *st; struct iio_dev *indio_dev; - int ret , voltage_uv = 0; + int ret, voltage_uv = 0; if (!pdata) { dev_err(&spi->dev, "no platform data?\n"); diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index f053535385bf..d9d6fad7cb00 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -436,7 +436,14 @@ static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) */ mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch)); - /* prepare the delay/loop unit according to the oversampling count */ + /* + * prepare the delay/loop unit according to the oversampling count + * + * from the datasheet: + * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1, + * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise, + * the LRADC will not trigger the delay group." + */ mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) | LRADC_DELAY_TRIGGER_DELAYS(0) | LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) | @@ -1495,20 +1502,38 @@ static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc, return -EINVAL; } - lradc->over_sample_cnt = 4; - ret = of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt); - if (ret == 0) + if (of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt)) { + lradc->over_sample_cnt = 4; + } else { + if (adapt < 1 || adapt > 32) { + dev_err(lradc->dev, "Invalid sample count (%u)\n", + adapt); + return -EINVAL; + } lradc->over_sample_cnt = adapt; + } - lradc->over_sample_delay = 2; - ret = of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt); - if (ret == 0) + if (of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt)) { + lradc->over_sample_delay = 2; + } else { + if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) { + dev_err(lradc->dev, "Invalid sample delay (%u)\n", + adapt); + return -EINVAL; + } lradc->over_sample_delay = adapt; + } - lradc->settling_delay = 10; - ret = of_property_read_u32(lradc_node, "fsl,settling", &adapt); - if (ret == 0) + if (of_property_read_u32(lradc_node, "fsl,settling", &adapt)) { + lradc->settling_delay = 10; + } else { + if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) { + dev_err(lradc->dev, "Invalid settling delay (%u)\n", + adapt); + return -EINVAL; + } lradc->settling_delay = adapt; + } return 0; } diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c index 5a804f16ec2f..59ad5a3efe9c 100644 --- a/drivers/staging/iio/iio_dummy_evgen.c +++ b/drivers/staging/iio/iio_dummy_evgen.c @@ -33,6 +33,7 @@ * @base: base of irq range * @enabled: mask of which irqs are enabled * @inuse: mask of which irqs are connected + * @regs: irq regs we are faking * @lock: protect the evgen state */ struct iio_dummy_eventgen { @@ -40,6 +41,7 @@ struct iio_dummy_eventgen { int base; bool enabled[IIO_EVENTGEN_NO]; bool inuse[IIO_EVENTGEN_NO]; + struct iio_dummy_regs regs[IIO_EVENTGEN_NO]; struct mutex lock; }; @@ -136,6 +138,12 @@ int iio_dummy_evgen_release_irq(int irq) } EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq); +struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq) +{ + return &iio_evgen->regs[irq - iio_evgen->base]; +} +EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs); + static void iio_dummy_evgen_free(void) { irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO); @@ -153,6 +161,15 @@ static ssize_t iio_evgen_poke(struct device *dev, size_t len) { struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + unsigned long event; + int ret; + + ret = kstrtoul(buf, 10, &event); + if (ret) + return ret; + + iio_evgen->regs[this_attr->address].reg_id = this_attr->address; + iio_evgen->regs[this_attr->address].reg_data = event; if (iio_evgen->enabled[this_attr->address]) handle_nested_irq(iio_evgen->base + this_attr->address); diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h index 3a180811b315..2ac293ab7c8f 100644 --- a/drivers/staging/iio/iio_dummy_evgen.h +++ b/drivers/staging/iio/iio_dummy_evgen.h @@ -1,6 +1,12 @@ #ifndef _IIO_DUMMY_EVGEN_H_ #define _IIO_DUMMY_EVGEN_H_ +struct iio_dummy_regs { + u32 reg_id; + u32 reg_data; +}; + +struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq); int iio_dummy_evgen_get_irq(void); int iio_dummy_evgen_release_irq(int irq); diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c index bf78e6f0311f..0b8611ac1003 100644 --- a/drivers/staging/iio/iio_simple_dummy.c +++ b/drivers/staging/iio/iio_simple_dummy.c @@ -69,6 +69,34 @@ static const struct iio_event_spec iio_dummy_event = { .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), }; +/* + * simple step detect event - triggered when a step is detected + */ +static const struct iio_event_spec step_detect_event = { + .type = IIO_EV_TYPE_INSTANCE, + .dir = IIO_EV_DIR_NONE, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), +}; + +/* + * simple transition event - triggered when the reported running confidence + * value rises above a threshold value + */ +static const struct iio_event_spec iio_running_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; + +/* + * simple transition event - triggered when the reported walking confidence + * value falls under a threshold value + */ +static const struct iio_event_spec iio_walking_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; #endif /* @@ -211,10 +239,44 @@ static const struct iio_chan_spec iio_dummy_channels[] = { { .type = IIO_VOLTAGE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = -1, /* No buffer support */ .output = 1, .indexed = 1, .channel = 0, }, + { + .type = IIO_STEPS, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_CALIBHEIGHT), + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .event_spec = &step_detect_event, + .num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ + }, + { + .type = IIO_ACTIVITY, + .modified = 1, + .channel2 = IIO_MOD_RUNNING, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .event_spec = &iio_running_event, + .num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ + }, + { + .type = IIO_ACTIVITY, + .modified = 1, + .channel2 = IIO_MOD_WALKING, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .event_spec = &iio_walking_event, + .num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ + }, }; /** @@ -263,24 +325,55 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev, break; } break; + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_STEPS: + *val = st->steps; + ret = IIO_VAL_INT; + break; + case IIO_ACTIVITY: + switch (chan->channel2) { + case IIO_MOD_RUNNING: + *val = st->activity_running; + ret = IIO_VAL_INT; + break; + case IIO_MOD_WALKING: + *val = st->activity_walking; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + default: + break; + } + break; case IIO_CHAN_INFO_OFFSET: /* only single ended adc -> 7 */ *val = 7; ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - switch (chan->differential) { - case 0: - /* only single ended adc -> 0.001333 */ - *val = 0; - *val2 = 1333; - ret = IIO_VAL_INT_PLUS_MICRO; + switch (chan->type) { + case IIO_VOLTAGE: + switch (chan->differential) { + case 0: + /* only single ended adc -> 0.001333 */ + *val = 0; + *val2 = 1333; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case 1: + /* all differential adc channels -> + * 0.000001344 */ + *val = 0; + *val2 = 1344; + ret = IIO_VAL_INT_PLUS_NANO; + } + break; + default: break; - case 1: - /* all differential adc channels -> 0.000001344 */ - *val = 0; - *val2 = 1344; - ret = IIO_VAL_INT_PLUS_NANO; } break; case IIO_CHAN_INFO_CALIBBIAS: @@ -298,6 +391,27 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev, *val2 = 33; ret = IIO_VAL_INT_PLUS_NANO; break; + case IIO_CHAN_INFO_ENABLE: + switch (chan->type) { + case IIO_STEPS: + *val = st->steps_enabled; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_CALIBHEIGHT: + switch (chan->type) { + case IIO_STEPS: + *val = st->height; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + default: break; } @@ -330,14 +444,45 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (chan->output == 0) + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output == 0) + return -EINVAL; + + /* Locking not required as writing single value */ + mutex_lock(&st->lock); + st->dac_val = val; + mutex_unlock(&st->lock); + return 0; + default: return -EINVAL; - - /* Locking not required as writing single value */ - mutex_lock(&st->lock); - st->dac_val = val; - mutex_unlock(&st->lock); - return 0; + } + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_STEPS: + mutex_lock(&st->lock); + st->steps = val; + mutex_unlock(&st->lock); + return 0; + case IIO_ACTIVITY: + if (val < 0) + val = 0; + if (val > 100) + val = 100; + switch (chan->channel2) { + case IIO_MOD_RUNNING: + st->activity_running = val; + return 0; + case IIO_MOD_WALKING: + st->activity_walking = val; + return 0; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } case IIO_CHAN_INFO_CALIBSCALE: mutex_lock(&st->lock); /* Compare against table - hard matching here */ @@ -356,6 +501,24 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev, st->accel_calibbias = val; mutex_unlock(&st->lock); return 0; + case IIO_CHAN_INFO_ENABLE: + switch (chan->type) { + case IIO_STEPS: + mutex_lock(&st->lock); + st->steps_enabled = val; + mutex_unlock(&st->lock); + return 0; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBHEIGHT: + switch (chan->type) { + case IIO_STEPS: + st->height = val; + return 0; + default: + return -EINVAL; + } default: return -EINVAL; @@ -395,6 +558,9 @@ static int iio_dummy_init_device(struct iio_dev *indio_dev) st->accel_val = 34; st->accel_calibbias = -7; st->accel_calibscale = &dummy_scales[0]; + st->steps = 47; + st->activity_running = 98; + st->activity_walking = 4; return 0; } @@ -475,13 +641,7 @@ static int iio_dummy_probe(int index) if (ret < 0) goto error_free_device; - /* - * Configure buffered capture support and register the channels with the - * buffer, but avoid the output channel being registered by reducing the - * number of channels by 1. - */ - ret = iio_simple_dummy_configure_buffer(indio_dev, - iio_dummy_channels, 5); + ret = iio_simple_dummy_configure_buffer(indio_dev); if (ret < 0) goto error_unregister_events; diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h index 3027aed79093..34989bf248a7 100644 --- a/drivers/staging/iio/iio_simple_dummy.h +++ b/drivers/staging/iio/iio_simple_dummy.h @@ -13,6 +13,7 @@ #include <linux/kernel.h> struct iio_dummy_accel_calibscale; +struct iio_dummy_regs; /** * struct iio_dummy_state - device instance specific state. @@ -33,8 +34,14 @@ struct iio_dummy_state { int differential_adc_val[2]; int accel_val; int accel_calibbias; + int activity_running; + int activity_walking; const struct iio_dummy_accel_calibscale *accel_calibscale; struct mutex lock; + struct iio_dummy_regs *regs; + int steps_enabled; + int steps; + int height; #ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS int event_irq; int event_val; @@ -107,12 +114,10 @@ enum iio_simple_dummy_scan_elements { }; #ifdef CONFIG_IIO_SIMPLE_DUMMY_BUFFER -int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev, - const struct iio_chan_spec *channels, unsigned int num_channels); +int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev); void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev); #else -static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev, - const struct iio_chan_spec *channels, unsigned int num_channels) +static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) { return 0; }; diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c index fd74f9166a5f..360a4c980722 100644 --- a/drivers/staging/iio/iio_simple_dummy_buffer.c +++ b/drivers/staging/iio/iio_simple_dummy_buffer.c @@ -115,14 +115,13 @@ static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = { .predisable = &iio_triggered_buffer_predisable, }; -int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev, - const struct iio_chan_spec *channels, unsigned int num_channels) +int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) { int ret; struct iio_buffer *buffer; /* Allocate a buffer to use - here a kfifo */ - buffer = iio_kfifo_allocate(indio_dev); + buffer = iio_kfifo_allocate(); if (buffer == NULL) { ret = -ENOMEM; goto error_ret; @@ -173,14 +172,8 @@ int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev, */ indio_dev->modes |= INDIO_BUFFER_TRIGGERED; - ret = iio_buffer_register(indio_dev, channels, num_channels); - if (ret) - goto error_dealloc_pollfunc; - return 0; -error_dealloc_pollfunc: - iio_dealloc_pollfunc(indio_dev->pollfunc); error_free_buffer: iio_kfifo_free(indio_dev->buffer); error_ret: @@ -194,7 +187,6 @@ error_ret: */ void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) { - iio_buffer_unregister(indio_dev); iio_dealloc_pollfunc(indio_dev->pollfunc); iio_kfifo_free(indio_dev->buffer); } diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c index 64b45b077549..ac15a44ba271 100644 --- a/drivers/staging/iio/iio_simple_dummy_events.c +++ b/drivers/staging/iio/iio_simple_dummy_events.c @@ -72,6 +72,22 @@ int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, st->event_en = state; else return -EINVAL; + default: + return -EINVAL; + } + break; + case IIO_ACTIVITY: + switch (type) { + case IIO_EV_TYPE_THRESH: + st->event_en = state; + break; + default: + return -EINVAL; + } + case IIO_STEPS: + switch (type) { + case IIO_EV_TYPE_INSTANCE: + st->event_en = state; break; default: return -EINVAL; @@ -148,12 +164,50 @@ int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) { struct iio_dev *indio_dev = private; + struct iio_dummy_state *st = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "id %x event %x\n", + st->regs->reg_id, st->regs->reg_data); + + switch (st->regs->reg_data) { + case 0: + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0, + IIO_EV_DIR_RISING, + IIO_EV_TYPE_THRESH, 0, 0, 0), + iio_get_time_ns()); + break; + case 1: + if (st->activity_running > st->event_val) + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_ACTIVITY, 0, + IIO_MOD_RUNNING, + IIO_EV_DIR_RISING, + IIO_EV_TYPE_THRESH, + 0, 0, 0), + iio_get_time_ns()); + break; + case 2: + if (st->activity_walking < st->event_val) + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_ACTIVITY, 0, + IIO_MOD_WALKING, + IIO_EV_DIR_FALLING, + IIO_EV_TYPE_THRESH, + 0, 0, 0), + iio_get_time_ns()); + break; + case 3: + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, + IIO_EV_DIR_NONE, + IIO_EV_TYPE_INSTANCE, 0, 0, 0), + iio_get_time_ns()); + break; + default: + break; + } - iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, 0, 0, 0), - iio_get_time_ns()); return IRQ_HANDLED; } @@ -179,6 +233,8 @@ int iio_simple_dummy_events_register(struct iio_dev *indio_dev) ret = st->event_irq; goto error_ret; } + st->regs = iio_dummy_evgen_get_regs(st->event_irq); + ret = request_threaded_irq(st->event_irq, NULL, &iio_simple_dummy_event_handler, diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index b6bd609c3655..e90653ff9159 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -89,7 +89,6 @@ struct ad5933_state { struct i2c_client *client; struct regulator *reg; - struct ad5933_platform_data *pdata; struct delayed_work work; unsigned long mclk_hz; unsigned char ctrl_hb; @@ -113,7 +112,8 @@ static const struct iio_chan_spec ad5933_channels[] = { .type = IIO_TEMP, .indexed = 1, .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), .address = AD5933_REG_TEMP_DATA, .scan_index = -1, .scan_type = { @@ -520,12 +520,11 @@ static int ad5933_read_raw(struct iio_dev *indio_dev, { struct ad5933_state *st = iio_priv(indio_dev); __be16 dat; - int ret = -EINVAL; + int ret; - mutex_lock(&indio_dev->mlock); switch (m) { case IIO_CHAN_INFO_RAW: - case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&indio_dev->mlock); if (iio_buffer_enabled(indio_dev)) { ret = -EBUSY; goto out; @@ -543,16 +542,16 @@ static int ad5933_read_raw(struct iio_dev *indio_dev, if (ret < 0) goto out; mutex_unlock(&indio_dev->mlock); - ret = be16_to_cpu(dat); - /* Temp in Milli degrees Celsius */ - if (ret < 8192) - *val = ret * 1000 / 32; - else - *val = (ret - 16384) * 1000 / 32; + *val = sign_extend32(be16_to_cpu(dat), 13); return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 1000; + *val2 = 5; + return IIO_VAL_FRACTIONAL_LOG2; } + return -EINVAL; out: mutex_unlock(&indio_dev->mlock); return ret; @@ -626,7 +625,7 @@ static int ad5933_register_ring_funcs_and_init(struct iio_dev *indio_dev) { struct iio_buffer *buffer; - buffer = iio_kfifo_allocate(indio_dev); + buffer = iio_kfifo_allocate(); if (!buffer) return -ENOMEM; @@ -712,9 +711,7 @@ static int ad5933_probe(struct i2c_client *client, st->client = client; if (!pdata) - st->pdata = &ad5933_default_pdata; - else - st->pdata = pdata; + pdata = &ad5933_default_pdata; st->reg = devm_regulator_get(&client->dev, "vcc"); if (!IS_ERR(st->reg)) { @@ -727,10 +724,10 @@ static int ad5933_probe(struct i2c_client *client, if (voltage_uv) st->vref_mv = voltage_uv / 1000; else - st->vref_mv = st->pdata->vref_mv; + st->vref_mv = pdata->vref_mv; - if (st->pdata->ext_clk_Hz) { - st->mclk_hz = st->pdata->ext_clk_Hz; + if (pdata->ext_clk_Hz) { + st->mclk_hz = pdata->ext_clk_Hz; st->ctrl_lb = AD5933_CTRL_EXT_SYSCLK; } else { st->mclk_hz = AD5933_INT_OSC_FREQ_Hz; @@ -752,27 +749,16 @@ static int ad5933_probe(struct i2c_client *client, if (ret) goto error_disable_reg; - ret = iio_buffer_register(indio_dev, ad5933_channels, - ARRAY_SIZE(ad5933_channels)); - if (ret) - goto error_unreg_ring; - - /* enable both REAL and IMAG channels by default */ - iio_scan_mask_set(indio_dev, indio_dev->buffer, 0); - iio_scan_mask_set(indio_dev, indio_dev->buffer, 1); - ret = ad5933_setup(st); if (ret) - goto error_uninitialize_ring; + goto error_unreg_ring; ret = iio_device_register(indio_dev); if (ret) - goto error_uninitialize_ring; + goto error_unreg_ring; return 0; -error_uninitialize_ring: - iio_buffer_unregister(indio_dev); error_unreg_ring: iio_kfifo_free(indio_dev->buffer); error_disable_reg: @@ -788,7 +774,6 @@ static int ad5933_remove(struct i2c_client *client) struct ad5933_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); - iio_buffer_unregister(indio_dev); iio_kfifo_free(indio_dev->buffer); if (!IS_ERR(st->reg)) regulator_disable(st->reg); diff --git a/drivers/staging/iio/meter/ade7758.h b/drivers/staging/iio/meter/ade7758.h index e8c98cf57070..f6739e2c24b1 100644 --- a/drivers/staging/iio/meter/ade7758.h +++ b/drivers/staging/iio/meter/ade7758.h @@ -145,7 +145,6 @@ ssize_t ade7758_read_data_from_ring(struct device *dev, int ade7758_configure_ring(struct iio_dev *indio_dev); void ade7758_unconfigure_ring(struct iio_dev *indio_dev); -void ade7758_uninitialize_ring(struct iio_dev *indio_dev); int ade7758_set_irq(struct device *dev, bool enable); int ade7758_spi_write_reg_8(struct device *dev, diff --git a/drivers/staging/iio/meter/ade7758_core.c b/drivers/staging/iio/meter/ade7758_core.c index fb373b89dcc2..70e96b20c2eb 100644 --- a/drivers/staging/iio/meter/ade7758_core.c +++ b/drivers/staging/iio/meter/ade7758_core.c @@ -850,23 +850,15 @@ static int ade7758_probe(struct spi_device *spi) if (ret) goto error_free_tx; - ret = iio_buffer_register(indio_dev, - &ade7758_channels[0], - ARRAY_SIZE(ade7758_channels)); - if (ret) { - dev_err(&spi->dev, "failed to initialize the ring\n"); - goto error_unreg_ring_funcs; - } - /* Get the device into a sane initial state */ ret = ade7758_initial_setup(indio_dev); if (ret) - goto error_uninitialize_ring; + goto error_unreg_ring_funcs; if (spi->irq) { ret = ade7758_probe_trigger(indio_dev); if (ret) - goto error_uninitialize_ring; + goto error_unreg_ring_funcs; } ret = iio_device_register(indio_dev); @@ -878,8 +870,6 @@ static int ade7758_probe(struct spi_device *spi) error_remove_trigger: if (spi->irq) ade7758_remove_trigger(indio_dev); -error_uninitialize_ring: - ade7758_uninitialize_ring(indio_dev); error_unreg_ring_funcs: ade7758_unconfigure_ring(indio_dev); error_free_tx: @@ -897,7 +887,6 @@ static int ade7758_remove(struct spi_device *spi) iio_device_unregister(indio_dev); ade7758_stop_device(&indio_dev->dev); ade7758_remove_trigger(indio_dev); - ade7758_uninitialize_ring(indio_dev); ade7758_unconfigure_ring(indio_dev); kfree(st->tx); kfree(st->rx); diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c index 6e9006490742..3792b5761645 100644 --- a/drivers/staging/iio/meter/ade7758_ring.c +++ b/drivers/staging/iio/meter/ade7758_ring.c @@ -118,7 +118,7 @@ int ade7758_configure_ring(struct iio_dev *indio_dev) struct iio_buffer *buffer; int ret = 0; - buffer = iio_kfifo_allocate(indio_dev); + buffer = iio_kfifo_allocate(); if (!buffer) { ret = -ENOMEM; return ret; @@ -180,8 +180,3 @@ error_iio_kfifo_free: iio_kfifo_free(indio_dev->buffer); return ret; } - -void ade7758_uninitialize_ring(struct iio_dev *indio_dev) -{ - iio_buffer_unregister(indio_dev); -} diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c index 7d217430616a..b0c7dbc8a428 100644 --- a/drivers/staging/iio/meter/ade7759.c +++ b/drivers/staging/iio/meter/ade7759.c @@ -116,7 +116,7 @@ static int ade7759_spi_read_reg_40(struct device *dev, mutex_lock(&st->buf_lock); st->tx[0] = ADE7759_READ_REG(reg_address); - memset(&st->tx[1], 0 , 5); + memset(&st->tx[1], 0, 5); ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); if (ret) { |