diff options
Diffstat (limited to 'drivers/iio')
36 files changed, 2230 insertions, 96 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b2f963be399..9af763a90d9 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -70,5 +70,9 @@ source "drivers/iio/gyro/Kconfig" source "drivers/iio/imu/Kconfig" source "drivers/iio/light/Kconfig" source "drivers/iio/magnetometer/Kconfig" +if IIO_TRIGGER + source "drivers/iio/trigger/Kconfig" +endif #IIO_TRIGGER +source "drivers/iio/pressure/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index a0e8cdd67e4..7a3866c2d2a 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -21,3 +21,5 @@ obj-y += frequency/ obj-y += imu/ obj-y += light/ obj-y += magnetometer/ +obj-y += trigger/ +obj-y += pressure/ diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index bb594963f91..719d83fe51d 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -28,7 +28,6 @@ config IIO_ST_ACCEL_3AXIS select IIO_ST_ACCEL_I2C_3AXIS if (I2C) select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) - select IIO_ST_ACCEL_BUFFER if (IIO_TRIGGERED_BUFFER) help Say yes here to build support for STMicroelectronics accelerometers: LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index e0f5a3ceba5..4aec121261d 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -26,6 +26,8 @@ #include <linux/iio/common/st_sensors.h> #include "st_accel.h" +#define ST_ACCEL_NUMBER_DATA_CHANNELS 3 + /* DEFAULT VALUE FOR SENSORS */ #define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28 #define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a @@ -125,22 +127,34 @@ #define ST_ACCEL_3_MULTIREAD_BIT false static const struct iio_chan_spec st_accel_12bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, - ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, - ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, - ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 12, 16, + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_accel_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -442,6 +456,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev) if (err < 0) goto st_accel_common_probe_error; + adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS; adata->multiread_bit = adata->sensor->multi_read_bit; indio_dev->channels = adata->sensor->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ab0767e6727..93129ec4b64 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -133,6 +133,16 @@ config MAX1363 max11646, max11647) Provides direct access via sysfs and buffered data via the iio dev interface. +config MCP320X + tristate "Microchip Technology MCP3204/08" + depends on SPI + help + Say yes here to build support for Microchip Technology's MCP3204 or + MCP3208 analog to digital converter. + + This driver can also be built as a module. If so, the module will be + called mcp320x. + config TI_ADC081C tristate "Texas Instruments ADC081C021/027" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 0a825bed43f..8f475d31fe4 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index e5b88d5d3b5..b6db6a0e09c 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -774,11 +774,13 @@ static int at91_adc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id at91_adc_dt_ids[] = { { .compatible = "atmel,at91sam9260-adc" }, {}, }; MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); +#endif static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index b3d03d33594..9809fc9a35d 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -270,16 +270,16 @@ static int exynos_adc_probe(struct platform_device *pdev) info = iio_priv(indio_dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->regs = devm_request_and_ioremap(&pdev->dev, mem); - if (!info->regs) { - ret = -ENOMEM; + info->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->regs)) { + ret = PTR_ERR(info->regs); goto err_iio; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - info->enable_reg = devm_request_and_ioremap(&pdev->dev, mem); - if (!info->enable_reg) { - ret = -ENOMEM; + info->enable_reg = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->enable_reg)) { + ret = PTR_ERR(info->enable_reg); goto err_iio; } diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 9e6da72ad82..f148d00b83f 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -660,7 +660,7 @@ static ssize_t max1363_monitor_store_freq(struct device *dev, unsigned long val; bool found = false; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (ret) return -EINVAL; for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c new file mode 100644 index 00000000000..ebc015922a7 --- /dev/null +++ b/drivers/iio/adc/mcp320x.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com> + * + * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips. + * Datasheet can be found here: + * http://ww1.microchip.com/downloads/en/devicedoc/21298c.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/err.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> + +#define MCP_SINGLE_ENDED (1 << 3) +#define MCP_START_BIT (1 << 4) + +enum { + mcp3204, + mcp3208, +}; + +struct mcp320x { + struct spi_device *spi; + struct spi_message msg; + struct spi_transfer transfer[2]; + + u8 tx_buf; + u8 rx_buf[2]; + + struct regulator *reg; + struct mutex lock; +}; + +static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg) +{ + int ret; + + adc->tx_buf = msg; + ret = spi_sync(adc->spi, &adc->msg); + if (ret < 0) + return ret; + + return ((adc->rx_buf[0] & 0x3f) << 6) | + (adc->rx_buf[1] >> 2); +} + +static int mcp320x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mcp320x *adc = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&adc->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (channel->differential) + ret = mcp320x_adc_conversion(adc, + MCP_START_BIT | channel->address); + else + ret = mcp320x_adc_conversion(adc, + MCP_START_BIT | MCP_SINGLE_ENDED | + channel->address); + if (ret < 0) + goto out; + + *val = ret; + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + /* Digital output code = (4096 * Vin) / Vref */ + ret = regulator_get_voltage(adc->reg); + if (ret < 0) + goto out; + + *val = ret / 1000; + *val2 = 12; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + + default: + break; + } + +out: + mutex_unlock(&adc->lock); + + return ret; +} + +#define MCP320X_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num), \ + .address = (num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num * 2), \ + .channel2 = (num * 2 + 1), \ + .address = (num * 2), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +static const struct iio_chan_spec mcp3204_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL_DIFF(0), + MCP320X_VOLTAGE_CHANNEL_DIFF(1), +}; + +static const struct iio_chan_spec mcp3208_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL(4), + MCP320X_VOLTAGE_CHANNEL(5), + MCP320X_VOLTAGE_CHANNEL(6), + MCP320X_VOLTAGE_CHANNEL(7), + MCP320X_VOLTAGE_CHANNEL_DIFF(0), + MCP320X_VOLTAGE_CHANNEL_DIFF(1), + MCP320X_VOLTAGE_CHANNEL_DIFF(2), + MCP320X_VOLTAGE_CHANNEL_DIFF(3), +}; + +static const struct iio_info mcp320x_info = { + .read_raw = mcp320x_read_raw, + .driver_module = THIS_MODULE, +}; + +struct mcp3208_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +static const struct mcp3208_chip_info mcp3208_chip_infos[] = { + [mcp3204] = { + .channels = mcp3204_channels, + .num_channels = ARRAY_SIZE(mcp3204_channels) + }, + [mcp3208] = { + .channels = mcp3208_channels, + .num_channels = ARRAY_SIZE(mcp3208_channels) + }, +}; + +static int mcp320x_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp320x *adc; + const struct mcp3208_chip_info *chip_info; + int ret; + + indio_dev = iio_device_alloc(sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp320x_info; + + chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data]; + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + + adc->transfer[0].tx_buf = &adc->tx_buf; + adc->transfer[0].len = sizeof(adc->tx_buf); + adc->transfer[1].rx_buf = adc->rx_buf; + adc->transfer[1].len = sizeof(adc->rx_buf); + + spi_message_init_with_transfers(&adc->msg, adc->transfer, + ARRAY_SIZE(adc->transfer)); + + adc->reg = regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) { + ret = PTR_ERR(adc->reg); + goto iio_free; + } + + ret = regulator_enable(adc->reg); + if (ret < 0) + goto reg_free; + + mutex_init(&adc->lock); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto reg_disable; + + return 0; + +reg_disable: + regulator_disable(adc->reg); +reg_free: + regulator_put(adc->reg); +iio_free: + iio_device_free(indio_dev); + + return ret; +} + +static int mcp320x_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct mcp320x *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(adc->reg); + regulator_put(adc->reg); + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id mcp320x_id[] = { + { "mcp3204", mcp3204 }, + { "mcp3208", mcp3208 }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp320x_id); + +static struct spi_driver mcp320x_driver = { + .driver = { + .name = "mcp320x", + .owner = THIS_MODULE, + }, + .probe = mcp320x_probe, + .remove = mcp320x_remove, + .id_table = mcp320x_id, +}; +module_spi_driver(mcp320x_driver); + +MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>"); +MODULE_DESCRIPTION("Microchip Technology MCP3204/08"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index 09b236d6ee8..71a2c5f63b9 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -24,11 +24,20 @@ int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) { + u8 *addr; int i, n = 0, len; - u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS]; struct st_sensor_data *sdata = iio_priv(indio_dev); + unsigned int num_data_channels = sdata->num_data_channels; + unsigned int byte_for_channel = + indio_dev->channels[0].scan_type.storagebits >> 3; - for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) { + addr = kmalloc(num_data_channels, GFP_KERNEL); + if (!addr) { + len = -ENOMEM; + goto st_sensors_get_buffer_element_error; + } + + for (i = 0; i < num_data_channels; i++) { if (test_bit(i, indio_dev->active_scan_mask)) { addr[n] = indio_dev->channels[i].address; n++; @@ -37,52 +46,58 @@ int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) switch (n) { case 1: len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, - addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf, - sdata->multiread_bit); + addr[0], byte_for_channel, buf, sdata->multiread_bit); break; case 2: - if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) { + if ((addr[1] - addr[0]) == byte_for_channel) { len = sdata->tf->read_multiple_byte(&sdata->tb, - sdata->dev, addr[0], - ST_SENSORS_BYTE_FOR_CHANNEL*n, - buf, sdata->multiread_bit); + sdata->dev, addr[0], byte_for_channel * n, + buf, sdata->multiread_bit); } else { - u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL* - ST_SENSORS_NUMBER_DATA_CHANNELS]; + u8 *rx_array; + rx_array = kmalloc(byte_for_channel * num_data_channels, + GFP_KERNEL); + if (!rx_array) { + len = -ENOMEM; + goto st_sensors_free_memory; + } + len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, addr[0], - ST_SENSORS_BYTE_FOR_CHANNEL* - ST_SENSORS_NUMBER_DATA_CHANNELS, + byte_for_channel * num_data_channels, rx_array, sdata->multiread_bit); - if (len < 0) - goto read_data_channels_error; + if (len < 0) { + kfree(rx_array); + goto st_sensors_free_memory; + } - for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS; - i++) { + for (i = 0; i < n * num_data_channels; i++) { if (i < n) buf[i] = rx_array[i]; else buf[i] = rx_array[n + i]; } - len = ST_SENSORS_BYTE_FOR_CHANNEL*n; + kfree(rx_array); + len = byte_for_channel * n; } break; case 3: len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, - addr[0], ST_SENSORS_BYTE_FOR_CHANNEL* - ST_SENSORS_NUMBER_DATA_CHANNELS, + addr[0], byte_for_channel * num_data_channels, buf, sdata->multiread_bit); break; default: len = -EINVAL; - goto read_data_channels_error; + goto st_sensors_free_memory; } - if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) { + if (len != byte_for_channel * n) { len = -EIO; - goto read_data_channels_error; + goto st_sensors_free_memory; } -read_data_channels_error: +st_sensors_free_memory: + kfree(addr); +st_sensors_get_buffer_element_error: return len; } EXPORT_SYMBOL(st_sensors_get_buffer_element); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index ed9bc8ae933..865b1781df6 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -20,6 +20,11 @@ #define ST_SENSORS_WAI_ADDRESS 0x0f +static inline u32 st_sensors_get_unaligned_le24(const u8 *p) +{ + return ((s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8); +} + static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr, u8 mask, u8 data) { @@ -112,7 +117,8 @@ st_sensors_match_odr_error: return ret; } -static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs) +static int st_sensors_set_fullscale(struct iio_dev *indio_dev, + unsigned int fs) { int err, i = 0; struct st_sensor_data *sdata = iio_priv(indio_dev); @@ -273,21 +279,33 @@ st_sensors_match_scale_error: EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain); static int st_sensors_read_axis_data(struct iio_dev *indio_dev, - u8 ch_addr, int *data) + struct iio_chan_spec const *ch, int *data) { int err; - u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL]; + u8 *outdata; struct st_sensor_data *sdata = iio_priv(indio_dev); + unsigned int byte_for_channel = ch->scan_type.storagebits >> 3; + + outdata = kmalloc(byte_for_channel, GFP_KERNEL); + if (!outdata) { + err = -EINVAL; + goto st_sensors_read_axis_data_error; + } err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, - ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL, + ch->address, byte_for_channel, outdata, sdata->multiread_bit); if (err < 0) - goto read_error; + goto st_sensors_free_memory; - *data = (s16)get_unaligned_le16(outdata); + if (byte_for_channel == 2) + *data = (s16)get_unaligned_le16(outdata); + else if (byte_for_channel == 3) + *data = (s32)st_sensors_get_unaligned_le24(outdata); -read_error: +st_sensors_free_memory: + kfree(outdata); +st_sensors_read_axis_data_error: return err; } @@ -307,7 +325,7 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev, goto read_error; msleep((sdata->sensor->bootime * 1000) / sdata->odr); - err = st_sensors_read_axis_data(indio_dev, ch->address, val); + err = st_sensors_read_axis_data(indio_dev, ch, val); if (err < 0) goto read_error; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b61160bd935..c9c33ce32d3 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -130,6 +130,16 @@ config AD5686 To compile this driver as a module, choose M here: the module will be called ad5686. +config AD7303 + tristate "Analog Devices Analog Devices AD7303 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD7303 Digital to Analog + Converters (DAC). + + To compile this driver as module choose M here: the module will be called + ad7303. + config MAX517 tristate "Maxim MAX517/518/519 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 5b528ebb334..c8d7ab6bff0 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o +obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MCP4725) += mcp4725.o diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c new file mode 100644 index 00000000000..85aeef60dc5 --- /dev/null +++ b/drivers/iio/dac/ad7303.c @@ -0,0 +1,315 @@ +/* + * AD7303 Digital to analog converters driver + * + * Copyright 2013 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/regulator/consumer.h> +#include <linux/of.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <linux/platform_data/ad7303.h> + +#define AD7303_CFG_EXTERNAL_VREF BIT(15) +#define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch)) +#define AD7303_CFG_ADDR_OFFSET 10 + +#define AD7303_CMD_UPDATE_DAC (0x3 << 8) + +/** + * struct ad7303_state - driver instance specific data + * @spi: the device for this driver instance + * @config: cached config register value + * @dac_cache: current DAC raw value (chip does not support readback) + * @data: spi transfer buffer + */ + +struct ad7303_state { + struct spi_device *spi; + uint16_t config; + uint8_t dac_cache[2]; + + struct regulator *vdd_reg; + struct regulator *vref_reg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 data ____cacheline_aligned; +}; + +static int ad7303_write(struct ad7303_state *st, unsigned int chan, + uint8_t val) +{ + st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC | + (chan << AD7303_CFG_ADDR_OFFSET) | + st->config | val); + + return spi_write(st->spi, &st->data, sizeof(st->data)); +} + +static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad7303_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", (bool)(st->config & + AD7303_CFG_POWER_DOWN(chan->channel))); +} + +static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct ad7303_state *st = iio_priv(indio_dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + if (pwr_down) + st->config |= AD7303_CFG_POWER_DOWN(chan->channel); + else + st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel); + + /* There is no noop cmd which allows us to only update the powerdown + * mode, so just write one of the DAC channels again */ + ad7303_write(st, chan->channel, st->dac_cache[chan->channel]); + + mutex_unlock(&indio_dev->mlock); + return ret ? ret : len; +} + +static int ad7303_get_vref(struct ad7303_state *st, + struct iio_chan_spec const *chan) +{ + int ret; + + if (st->config & AD7303_CFG_EXTERNAL_VREF) + return regulator_get_voltage(st->vref_reg); + + ret = regulator_get_voltage(st->vdd_reg); + if (ret < 0) + return ret; + return ret / 2; +} + +static int ad7303_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad7303_state *st = iio_priv(indio_dev); + int vref_uv; + + switch (info) { + case IIO_CHAN_INFO_RAW: + *val = st->dac_cache[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref_uv = ad7303_get_vref(st, chan); + if (vref_uv < 0) + return vref_uv; + + *val = 2 * vref_uv / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + return -EINVAL; +} + +static int ad7303_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct ad7303_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + ret = ad7303_write(st, chan->address, val); + if (ret == 0) + st->dac_cache[chan->channel] = val; + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad7303_info = { + .read_raw = ad7303_read_raw, + .write_raw = ad7303_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { + { + .name = "powerdown", + .read = ad7303_read_dac_powerdown, + .write = ad7303_write_dac_powerdown, + }, + { }, +}; + +#define AD7303_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (chan), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = '8', \ + .storagebits = '8', \ + .shift = '0', \ + }, \ + .ext_info = ad7303_ext_info, \ +} + +static const struct iio_chan_spec ad7303_channels[] = { + AD7303_CHANNEL(0), + AD7303_CHANNEL(1), +}; + +static int ad7303_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct ad7303_state *st; + bool ext_ref; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + st->vdd_reg = regulator_get(&spi->dev, "Vdd"); + if (IS_ERR(st->vdd_reg)) { + ret = PTR_ERR(st->vdd_reg); + goto err_free; + } + + ret = regulator_enable(st->vdd_reg); + if (ret) + goto err_put_vdd_reg; + + if (spi->dev.of_node) { + ext_ref = of_property_read_bool(spi->dev.of_node, + "REF-supply"); + } else { + struct ad7303_platform_data *pdata = spi->dev.platform_data; + if (pdata && pdata->use_external_ref) + ext_ref = true; + else + ext_ref = false; + } + + if (ext_ref) { + st->vref_reg = regulator_get(&spi->dev, "REF"); + if (IS_ERR(st->vref_reg)) + goto err_disable_vdd_reg; + + ret = regulator_enable(st->vref_reg); + if (ret) + goto err_put_vref_reg; + + st->config |= AD7303_CFG_EXTERNAL_VREF; + } + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = id->name; + indio_dev->info = &ad7303_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7303_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7303_channels); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_disable_vref_reg; + + return 0; + +err_disable_vref_reg: + if (st->vref_reg) + regulator_disable(st->vref_reg); +err_put_vref_reg: + if (st->vref_reg) + regulator_put(st->vref_reg); +err_disable_vdd_reg: + regulator_disable(st->vdd_reg); +err_put_vdd_reg: + regulator_put(st->vdd_reg); +err_free: + iio_device_free(indio_dev); + + return ret; +} + +static int ad7303_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7303_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (st->vref_reg) { + regulator_disable(st->vref_reg); + regulator_put(st->vref_reg); + } + regulator_disable(st->vdd_reg); + regulator_put(st->vdd_reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7303_spi_ids[] = { + { "ad7303", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); + +static struct spi_driver ad7303_driver = { + .driver = { + .name = "ad7303", + .owner = THIS_MODULE, + }, + .probe = ad7303_probe, + .remove = ad7303_remove, + .id_table = ad7303_spi_ids, +}; +module_spi_driver(ad7303_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index e76d4ace53f..a4157cdb314 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -1,7 +1,7 @@ /* * ADF4350/ADF4351 SPI Wideband Synthesizer driver * - * Copyright 2012 Analog Devices Inc. + * Copyright 2012-2013 Analog Devices Inc. * * Licensed under the GPL-2. */ @@ -17,6 +17,9 @@ #include <linux/gcd.h> #include <linux/gpio.h> #include <asm/div64.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -33,6 +36,7 @@ struct adf4350_state { struct spi_device *spi; struct regulator *reg; struct adf4350_platform_data *pdata; + struct clk *clk; unsigned long clkin; unsigned long chspc; /* Channel Spacing */ unsigned long fpfd; /* Phase Frequency Detector */ @@ -43,7 +47,7 @@ struct adf4350_state { unsigned r4_rf_div_sel; unsigned long regs[6]; unsigned long regs_hw[6]; - + unsigned long long freq_req; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. @@ -52,7 +56,6 @@ struct adf4350_state { }; static struct adf4350_platform_data default_pdata = { - .clkin = 122880000, .channel_spacing = 10000, .r2_user_settings = ADF4350_REG2_PD_POLARITY_POS | ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500), @@ -235,6 +238,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) ADF4350_REG4_MUTE_TILL_LOCK_EN)); st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL; + st->freq_req = freq; return adf4350_sync_config(st); } @@ -246,6 +250,7 @@ static ssize_t adf4350_write(struct iio_dev *indio_dev, { struct adf4350_state *st = iio_priv(indio_dev); unsigned long long readin; + unsigned long tmp; int ret; ret = kstrtoull(buf, 10, &readin); @@ -258,10 +263,23 @@ static ssize_t adf4350_write(struct iio_dev *indio_dev, ret = adf4350_set_freq(st, readin); break; case ADF4350_FREQ_REFIN: - if (readin > ADF4350_MAX_FREQ_REFIN) + if (readin > ADF4350_MAX_FREQ_REFIN) { ret = -EINVAL; - else - st->clkin = readin; + break; + } + + if (st->clk) { + tmp = clk_round_rate(st->clk, readin); + if (tmp != readin) { + ret = -EINVAL; + break; + } + ret = clk_set_rate(st->clk, tmp); + if (ret < 0) + break; + } + st->clkin = readin; + ret = adf4350_set_freq(st, st->freq_req); break; case ADF4350_FREQ_RESOLUTION: if (readin == 0) @@ -308,6 +326,9 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev, } break; case ADF4350_FREQ_REFIN: + if (st->clk) + st->clkin = clk_get_rate(st->clk); + val = st->clkin; break; case ADF4350_FREQ_RESOLUTION: @@ -318,6 +339,7 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev, break; default: ret = -EINVAL; + val = 0; } mutex_unlock(&indio_dev->mlock); @@ -355,19 +377,153 @@ static const struct iio_info adf4350_info = { .driver_module = THIS_MODULE, }; +#ifdef CONFIG_OF +static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct adf4350_platform_data *pdata; + unsigned int tmp; + int ret; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for platform data\n"); + return NULL; + } + + strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1); + + tmp = 10000; + of_property_read_u32(np, "adi,channel-spacing", &tmp); + pdata->channel_spacing = tmp; + + tmp = 0; + of_property_read_u32(np, "adi,power-up-frequency", &tmp); + pdata->power_up_frequency = tmp; + + tmp = 0; + of_property_read_u32(np, "adi,reference-div-factor", &tmp); + pdata->ref_div_factor = tmp; + + ret = of_get_gpio(np, 0); + if (ret < 0) + pdata->gpio_lock_detect = -1; + else + pdata->gpio_lock_detect = ret; + + pdata->ref_doubler_en = of_property_read_bool(np, + "adi,reference-doubler-enable"); + pdata->ref_div2_en = of_property_read_bool(np, + "adi,reference-div2-enable"); + + /* r2_user_settings */ + pdata->r2_user_settings = of_property_read_bool(np, + "adi,phase-detector-polarity-positive-enable") ? + ADF4350_REG2_PD_POLARITY_POS : 0; + pdata->r2_user_settings |= of_property_read_bool(np, + "adi,lock-detect-precision-6ns-enable") ? + ADF4350_REG2_LDP_6ns : 0; + pdata->r2_user_settings |= of_property_read_bool(np, + "adi,lock-detect-function-integer-n-enable") ? + ADF4350_REG2_LDF_INT_N : 0; + + tmp = 2500; + of_property_read_u32(np, "adi,charge-pump-current", &tmp); + pdata->r2_user_settings |= ADF4350_REG2_CHARGE_PUMP_CURR_uA(tmp); + + tmp = 0; + of_property_read_u32(np, "adi,muxout-select", &tmp); + pdata->r2_user_settings |= ADF4350_REG2_MUXOUT(tmp); + + pdata->r2_user_settings |= of_property_read_bool(np, + "adi,low-spur-mode-enable") ? + ADF4350_REG2_NOISE_MODE(0x3) : 0; + + /* r3_user_settings */ + + pdata->r3_user_settings = of_property_read_bool(np, + "adi,cycle-slip-reduction-enable") ? + ADF4350_REG3_12BIT_CSR_EN : 0; + pdata->r3_user_settings |= of_property_read_bool(np, + "adi,charge-cancellation-enable") ? + ADF4351_REG3_CHARGE_CANCELLATION_EN : 0; + + pdata->r3_user_settings |= of_property_read_bool(np, + "adi,anti-backlash-3ns-enable") ? + ADF4351_REG3_ANTI_BACKLASH_3ns_EN : 0; + pdata->r3_user_settings |= of_property_read_bool(np, + "adi,band-select-clock-mode-high-enable") ? + ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH : 0; + + tmp = 0; + of_property_read_u32(np, "adi,12bit-clk-divider", &tmp); + pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV(tmp); + + tmp = 0; + of_property_read_u32(np, "adi,clk-divider-mode", &tmp); + pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV_MODE(tmp); + + /* r4_user_settings */ + + pdata->r4_user_settings = of_property_read_bool(np, + "adi,aux-output-enable") ? + ADF4350_REG4_AUX_OUTPUT_EN : 0; + pdata->r4_user_settings |= of_property_read_bool(np, + "adi,aux-output-fundamental-enable") ? + ADF4350_REG4_AUX_OUTPUT_FUND : 0; + pdata->r4_user_settings |= of_property_read_bool(np, + "adi,mute-till-lock-enable") ? + ADF4350_REG4_MUTE_TILL_LOCK_EN : 0; + + tmp = 0; + of_property_read_u32(np, "adi,output-power", &tmp); + pdata->r4_user_settings |= ADF4350_REG4_OUTPUT_PWR(tmp); + + tmp = 0; + of_property_read_u32(np, "adi,aux-output-power", &tmp); + pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_PWR(tmp); + + return pdata; +} +#else +static +struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + static int adf4350_probe(struct spi_device *spi) { - struct adf4350_platform_data *pdata = spi->dev.platform_data; + struct adf4350_platform_data *pdata; struct iio_dev *indio_dev; struct adf4350_state *st; + struct clk *clk = NULL; int ret; + if (spi->dev.of_node) { + pdata = adf4350_parse_dt(&spi->dev); + if (pdata == NULL) + return -EINVAL; + } else { + pdata = spi->dev.platform_data; + } + if (!pdata) { dev_warn(&spi->dev, "no platform data? using default\n"); - pdata = &default_pdata; } + if (!pdata->clkin) { + clk = clk_get(&spi->dev, "clkin"); + if (IS_ERR(clk)) + return -EPROBE_DEFER; + + ret = clk_prepare_enable(clk); + if (ret < 0) + return ret; + } + indio_dev = iio_device_alloc(sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; @@ -395,7 +551,12 @@ static int adf4350_probe(struct spi_device *spi) indio_dev->num_channels = 1; st->chspc = pdata->channel_spacing; - st->clkin = pdata->clkin; + if (clk) { + st->clk = clk; + st->clkin = clk_get_rate(clk); + } else { + st->clkin = pdata->clkin; + } st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ? ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ; @@ -435,6 +596,8 @@ error_put_reg: if (!IS_ERR(st->reg)) regulator_put(st->reg); + if (clk) + clk_disable_unprepare(clk); iio_device_free(indio_dev); return ret; @@ -451,6 +614,9 @@ static int adf4350_remove(struct spi_device *spi) iio_device_unregister(indio_dev); + if (st->clk) + clk_disable_unprepare(st->clk); + if (!IS_ERR(reg)) { regulator_disable(reg); regulator_put(reg); @@ -481,6 +647,6 @@ static struct spi_driver adf4350_driver = { }; module_spi_driver(adf4350_driver); -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 6be4628faff..8498e9dcda6 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -10,6 +10,13 @@ config ADIS16080 Say yes here to build support for Analog Devices ADIS16080, ADIS16100 Yaw Rate Gyroscope with SPI. +config ADIS16130 + tristate "Analog Devices ADIS16130 High Precision Angular Rate Sensor driver" + depends on SPI + help + Say yes here to build support for Analog Devices ADIS16130 High Precision + Angular Rate Sensor driver. + config ADIS16136 tristate "Analog devices ADIS16136 and similar gyroscopes driver" depends on SPI_MASTER @@ -47,7 +54,6 @@ config IIO_ST_GYRO_3AXIS select IIO_ST_GYRO_I2C_3AXIS if (I2C) select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) - select IIO_ST_GYRO_BUFFER if (IIO_TRIGGERED_BUFFER) help Say yes here to build support for STMicroelectronics gyroscopes: L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330. diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile index 225d289082e..e9dc034aa18 100644 --- a/drivers/iio/gyro/Makefile +++ b/drivers/iio/gyro/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_ADIS16080) += adis16080.o +obj-$(CONFIG_ADIS16130) += adis16130.o obj-$(CONFIG_ADIS16136) += adis16136.o obj-$(CONFIG_ADXRS450) += adxrs450.o diff --git a/drivers/iio/gyro/adis16130.c b/drivers/iio/gyro/adis16130.c new file mode 100644 index 00000000000..129acdf801a --- /dev/null +++ b/drivers/iio/gyro/adis16130.c @@ -0,0 +1,207 @@ +/* + * ADIS16130 Digital Output, High Precision Angular Rate Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/mutex.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> + +#define ADIS16130_CON 0x0 +#define ADIS16130_CON_RD (1 << 6) +#define ADIS16130_IOP 0x1 + +/* 1 = data-ready signal low when unread data on all channels; */ +#define ADIS16130_IOP_ALL_RDY (1 << 3) +#define ADIS16130_IOP_SYNC (1 << 0) /* 1 = synchronization enabled */ +#define ADIS16130_RATEDATA 0x8 /* Gyroscope output, rate of rotation */ +#define ADIS16130_TEMPDATA 0xA /* Temperature output */ +#define ADIS16130_RATECS 0x28 /* Gyroscope channel setup */ +#define ADIS16130_RATECS_EN (1 << 3) /* 1 = channel enable; */ +#define ADIS16130_TEMPCS 0x2A /* Temperature channel setup */ +#define ADIS16130_TEMPCS_EN (1 << 3) +#define ADIS16130_RATECONV 0x30 +#define ADIS16130_TEMPCONV 0x32 +#define ADIS16130_MODE 0x38 +#define ADIS16130_MODE_24BIT (1 << 1) /* 1 = 24-bit resolution; */ + +/** + * struct adis16130_state - device instance specific data + * @us: actual spi_device to write data + * @buf_lock: mutex to protect tx and rx + * @buf: unified tx/rx buffer + **/ +struct adis16130_state { + struct spi_device *us; + struct mutex buf_lock; + u8 buf[4] ____cacheline_aligned; +}; + +static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val) +{ + int ret; + struct adis16130_state *st = iio_priv(indio_dev); + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = st->buf, + .rx_buf = st->buf, + .len = 4, + }; + + mutex_lock(&st->buf_lock); + + st->buf[0] = ADIS16130_CON_RD | reg_addr; + st->buf[1] = st->buf[2] = st->buf[3] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + ret = spi_sync(st->us, &msg); + + if (ret == 0) + *val = (st->buf[1] << 16) | (st->buf[2] << 8) | st->buf[3]; + mutex_unlock(&st->buf_lock); + + return ret; +} + +static int adis16130_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + int ret; + u32 temp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16130_spi_read(indio_dev, chan->address, &temp); + mutex_unlock(&indio_dev->mlock); + if (ret) + return ret; + *val = temp; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: + /* 0 degree = 838860, 250 degree = 14260608 */ + *val = 250; + *val2 = 336440817; /* RAD_TO_DEGREE(14260608 - 8388608) */ + return IIO_VAL_FRACTIONAL; + case IIO_TEMP: + /* 0C = 8036283, 105C = 9516048 */ + *val = 105000; + *val2 = 9516048 - 8036283; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_ANGL_VEL: + *val = -8388608; + return IIO_VAL_INT; + case IIO_TEMP: + *val = -8036283; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + } + + return -EINVAL; +} + +static const struct iio_chan_spec adis16130_channels[] = { + { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = ADIS16130_RATEDATA, + }, { + .type = IIO_TEMP, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = ADIS16130_TEMPDATA, + } +}; + +static const struct iio_info adis16130_info = { + .read_raw = &adis16130_read_raw, + .driver_module = THIS_MODULE, +}; + +static int adis16130_probe(struct spi_device *spi) +{ + int ret; + struct adis16130_state *st; + struct iio_dev *indio_dev; + + /* setup the industrialio driver allocated elements */ + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + st->us = spi; + mutex_init(&st->buf_lock); + indio_dev->name = spi->dev.driver->name; + indio_dev->channels = adis16130_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16130_channels); + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16130_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_dev; + + return 0; + +error_free_dev: + iio_device_free(indio_dev); + +error_ret: + return ret; +} + +static int adis16130_remove(struct spi_device *spi) +{ + iio_device_unregister(spi_get_drvdata(spi)); + iio_device_free(spi_get_drvdata(spi)); + + return 0; +} + +static struct spi_driver adis16130_driver = { + .driver = { + .name = "adis16130", + .owner = THIS_MODULE, + }, + .probe = adis16130_probe, + .remove = adis16130_remove, +}; +module_spi_driver(adis16130_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16130 High Precision Angular Rate"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16130"); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index fa9b2421998..f9ed3488c31 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -27,6 +27,8 @@ #include <linux/iio/common/st_sensors.h> #include "st_gyro.h" +#define ST_GYRO_NUMBER_DATA_CHANNELS 3 + /* DEFAULT VALUE FOR SENSORS */ #define ST_GYRO_DEFAULT_OUT_X_L_ADDR 0x28 #define ST_GYRO_DEFAULT_OUT_Y_L_ADDR 0x2a @@ -86,15 +88,18 @@ #define ST_GYRO_2_MULTIREAD_BIT true static const struct iio_chan_spec st_gyro_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_X, - IIO_MOD_X, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS, - ST_GYRO_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Y, - IIO_MOD_Y, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS, - ST_GYRO_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Z, - IIO_MOD_Z, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS, - ST_GYRO_DEFAULT_OUT_Z_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, + ST_GYRO_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, + ST_GYRO_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, + ST_GYRO_DEFAULT_OUT_Z_L_ADDR), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -310,6 +315,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) if (err < 0) goto st_gyro_common_probe_error; + gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS; gdata->multiread_bit = gdata->sensor->multi_read_bit; indio_dev->channels = gdata->sensor->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index aaadd32f9f0..e73033f3839 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -542,8 +542,7 @@ int iio_update_buffers(struct iio_dev *indio_dev, ret = indio_dev->setup_ops->preenable(indio_dev); if (ret) { printk(KERN_ERR - "Buffer not started:" - "buffer preenable failed\n"); + "Buffer not started: buffer preenable failed (%d)\n", ret); goto error_remove_inserted; } } @@ -556,8 +555,7 @@ int iio_update_buffers(struct iio_dev *indio_dev, ret = buffer->access->request_update(buffer); if (ret) { printk(KERN_INFO - "Buffer not started:" - "buffer parameter update failed\n"); + "Buffer not started: buffer parameter update failed (%d)\n", ret); goto error_run_postdisable; } } @@ -566,7 +564,7 @@ int iio_update_buffers(struct iio_dev *indio_dev, ->update_scan_mode(indio_dev, indio_dev->active_scan_mask); if (ret < 0) { - printk(KERN_INFO "update scan mode failed\n"); + printk(KERN_INFO "Buffer not started: update scan mode failed (%d)\n", ret); goto error_run_postdisable; } } @@ -590,7 +588,7 @@ int iio_update_buffers(struct iio_dev *indio_dev, ret = indio_dev->setup_ops->postenable(indio_dev); if (ret) { printk(KERN_INFO - "Buffer not started: postenable failed\n"); + "Buffer not started: postenable failed (%d)\n", ret); indio_dev->currentmode = INDIO_DIRECT_MODE; if (indio_dev->setup_ops->postdisable) indio_dev->setup_ops->postdisable(indio_dev); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 80d68ff02d2..cdc2cad0f01 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -31,7 +31,7 @@ #include "../common/hid-sensors/hid-sensor-trigger.h" /*Format: HID-SENSOR-usage_id_in_hex*/ -/*Usage ID from spec for Accelerometer-3D: 0x200041*/ +/*Usage ID from spec for Ambiant-Light: 0x200041*/ #define DRIVER_NAME "HID-SENSOR-200041" #define CHANNEL_SCAN_INDEX_ILLUM 0 diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index bd1cfb66669..c332b0ae4a3 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -32,7 +32,6 @@ config IIO_ST_MAGN_3AXIS select IIO_ST_MAGN_I2C_3AXIS if (I2C) select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER) select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) - select IIO_ST_MAGN_BUFFER if (IIO_TRIGGERED_BUFFER) help Say yes here to build support for STMicroelectronics magnetometers: LSM303DLHC, LSM303DLM, LIS3MDL. diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index af6c320a534..7105f22d6cd 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -24,11 +24,13 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/interrupt.h> #include <linux/err.h> #include <linux/mutex.h> #include <linux/delay.h> - +#include <linux/bitops.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -82,6 +84,7 @@ */ #define AK8975_MAX_CONVERSION_TIMEOUT 500 #define AK8975_CONVERSION_DONE_POLL_TIME 10 +#define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000) /* * Per-instance context data for the device. @@ -94,6 +97,9 @@ struct ak8975_data { 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; }; static const int ak8975_index_to_reg[] = { @@ -123,6 +129,51 @@ static int ak8975_write_data(struct i2c_client *client, } /* + * Handle data ready irq + */ +static irqreturn_t ak8975_irq_handler(int irq, void *data) +{ + struct ak8975_data *ak8975 = data; + + set_bit(0, &ak8975->flags); + wake_up(&ak8975->data_ready_queue); + + return IRQ_HANDLED; +} + +/* + * Install data ready interrupt handler + */ +static int ak8975_setup_irq(struct ak8975_data *data) +{ + struct i2c_client *client = data->client; + int rc; + int irq; + + if (client->irq) + irq = client->irq; + else + irq = gpio_to_irq(data->eoc_gpio); + + rc = request_irq(irq, ak8975_irq_handler, + 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", + irq, data->eoc_gpio, rc); + return rc; + } + + init_waitqueue_head(&data->data_ready_queue); + clear_bit(0, &data->flags); + data->eoc_irq = irq; + + return rc; +} + + +/* * Perform some start-of-day setup, including reading the asa calibration * values and caching them. */ @@ -170,6 +221,16 @@ static int ak8975_setup(struct i2c_client *client) AK8975_REG_CNTL_MODE_POWER_DOWN, AK8975_REG_CNTL_MODE_MASK, AK8975_REG_CNTL_MODE_SHIFT); + + if (data->eoc_gpio > 0 || client->irq) { + ret = ak8975_setup_irq(data); + if (ret < 0) { + dev_err(&client->dev, + "Error setting data ready interrupt\n"); + return ret; + } + } + if (ret < 0) { dev_err(&client->dev, "Error in setting power-down mode\n"); return ret; @@ -266,9 +327,23 @@ static int wait_conversion_complete_polled(struct ak8975_data *data) dev_err(&client->dev, "Conversion timeout happened\n"); return -EINVAL; } + return read_status; } +/* Returns 0 if the end of conversion interrupt occured or -ETIME otherwise */ +static int wait_conversion_complete_interrupt(struct ak8975_data *data) +{ + int ret; + + ret = wait_event_timeout(data->data_ready_queue, + test_bit(0, &data->flags), + AK8975_DATA_READY_TIMEOUT); + clear_bit(0, &data->flags); + + return ret > 0 ? 0 : -ETIME; +} + /* * Emits the raw flux value for the x, y, or z axis. */ @@ -294,13 +369,16 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) } /* Wait for the conversion to complete. */ - if (gpio_is_valid(data->eoc_gpio)) + if (data->eoc_irq) + ret = wait_conversion_complete_interrupt(data); + else if (gpio_is_valid(data->eoc_gpio)) ret = wait_conversion_complete_gpio(data); else ret = wait_conversion_complete_polled(data); if (ret < 0) 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 < 0) { @@ -384,10 +462,15 @@ static int ak8975_probe(struct i2c_client *client, int err; /* Grab and set up the supplied GPIO. */ - if (client->dev.platform_data == NULL) - eoc_gpio = -1; - else + if (client->dev.platform_data) eoc_gpio = *(int *)(client->dev.platform_data); + else if (client->dev.of_node) + eoc_gpio = of_get_gpio(client->dev.of_node, 0); + else + eoc_gpio = -1; + + if (eoc_gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; /* We may not have a GPIO based IRQ to scan, that is fine, we will poll if so */ @@ -409,6 +492,11 @@ static int ak8975_probe(struct i2c_client *client, } data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); + + data->client = client; + data->eoc_gpio = eoc_gpio; + data->eoc_irq = 0; + /* Perform some basic start-of-day setup of the device. */ err = ak8975_setup(client); if (err < 0) { @@ -433,6 +521,8 @@ static int ak8975_probe(struct i2c_client *client, exit_free_iio: iio_device_free(indio_dev); + if (data->eoc_irq) + free_irq(data->eoc_irq, data); exit_gpio: if (gpio_is_valid(eoc_gpio)) gpio_free(eoc_gpio); @@ -447,6 +537,9 @@ static int ak8975_remove(struct i2c_client *client) iio_device_unregister(indio_dev); + if (data->eoc_irq) + free_irq(data->eoc_irq, data); + if (gpio_is_valid(data->eoc_gpio)) gpio_free(data->eoc_gpio); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 16f0d6df239..ebfe8f11a0c 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -26,6 +26,8 @@ #include <linux/iio/common/st_sensors.h> #include "st_magn.h" +#define ST_MAGN_NUMBER_DATA_CHANNELS 3 + /* DEFAULT VALUE FOR SENSORS */ #define ST_MAGN_DEFAULT_OUT_X_L_ADDR 0X04 #define ST_MAGN_DEFAULT_OUT_Y_L_ADDR 0X08 @@ -113,22 +115,34 @@ #define ST_MAGN_2_OUT_Z_L_ADDR 0x2c static const struct iio_chan_spec st_magn_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Z_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, + ST_MAGN_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, + ST_MAGN_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, + ST_MAGN_DEFAULT_OUT_Z_L_ADDR), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_magn_2_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, - ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Z_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, + ST_MAGN_2_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, + ST_MAGN_2_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, + ST_MAGN_2_OUT_Z_L_ADDR), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -344,6 +358,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev) if (err < 0) goto st_magn_common_probe_error; + mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS; mdata->multiread_bit = mdata->sensor->multi_read_bit; indio_dev->channels = mdata->sensor->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig new file mode 100644 index 00000000000..9427f01e149 --- /dev/null +++ b/drivers/iio/pressure/Kconfig @@ -0,0 +1,35 @@ +# +# Pressure drivers +# +menu "Pressure Sensors" + +config IIO_ST_PRESS + tristate "STMicroelectronics pressures Driver" + depends on (I2C || SPI_MASTER) && SYSFS + select IIO_ST_SENSORS_CORE + select IIO_ST_PRESS_I2C if (I2C) + select IIO_ST_PRESS_SPI if (SPI_MASTER) + select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) + help + Say yes here to build support for STMicroelectronics pressures: + LPS331AP. + + This driver can also be built as a module. If so, will be created + these modules: + - st_pressure (core functions for the driver [it is mandatory]); + - st_pressure_i2c (necessary for the I2C devices [optional*]); + - st_pressure_spi (necessary for the SPI devices [optional*]); + + (*) one of these is necessary to do something. + +config IIO_ST_PRESS_I2C + tristate + depends on IIO_ST_PRESS + depends on IIO_ST_SENSORS_I2C + +config IIO_ST_PRESS_SPI + tristate + depends on IIO_ST_PRESS + depends on IIO_ST_SENSORS_SPI + +endmenu diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile new file mode 100644 index 00000000000..d4bb33e5c84 --- /dev/null +++ b/drivers/iio/pressure/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for industrial I/O pressure drivers +# + +obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o +st_pressure-y := st_pressure_core.o +st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o + +obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o +obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h new file mode 100644 index 00000000000..414e45ac9b9 --- /dev/null +++ b/drivers/iio/pressure/st_pressure.h @@ -0,0 +1,39 @@ +/* + * STMicroelectronics pressures driver + * + * Copyright 2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * v. 1.0.0 + * Licensed under the GPL-2. + */ + +#ifndef ST_PRESS_H +#define ST_PRESS_H + +#include <linux/types.h> +#include <linux/iio/common/st_sensors.h> + +#define LPS331AP_PRESS_DEV_NAME "lps331ap" + +int st_press_common_probe(struct iio_dev *indio_dev); +void st_press_common_remove(struct iio_dev *indio_dev); + +#ifdef CONFIG_IIO_BUFFER +int st_press_allocate_ring(struct iio_dev *indio_dev); +void st_press_deallocate_ring(struct iio_dev *indio_dev); +int st_press_trig_set_state(struct iio_trigger *trig, bool state); +#define ST_PRESS_TRIGGER_SET_STATE (&st_press_trig_set_state) +#else /* CONFIG_IIO_BUFFER */ +static inline int st_press_allocate_ring(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void st_press_deallocate_ring(struct iio_dev *indio_dev) +{ +} +#define ST_PRESS_TRIGGER_SET_STATE NULL +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_PRESS_H */ diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c new file mode 100644 index 00000000000..f877ef8af52 --- /dev/null +++ b/drivers/iio/pressure/st_pressure_buffer.c @@ -0,0 +1,105 @@ +/* + * STMicroelectronics pressures driver + * + * Copyright 2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/common/st_sensors.h> +#include "st_pressure.h" + +int st_press_trig_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + + return st_sensors_set_dataready_irq(indio_dev, state); +} + +static int st_press_buffer_preenable(struct iio_dev *indio_dev) +{ + int err; + + err = st_sensors_set_enable(indio_dev, true); + if (err < 0) + goto st_press_set_enable_error; + + err = iio_sw_buffer_preenable(indio_dev); + +st_press_set_enable_error: + return err; +} + +static int st_press_buffer_postenable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *pdata = iio_priv(indio_dev); + + pdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (pdata->buffer_data == NULL) { + err = -ENOMEM; + goto allocate_memory_error; + } + + err = iio_triggered_buffer_postenable(indio_dev); + if (err < 0) + goto st_press_buffer_postenable_error; + + return err; + +st_press_buffer_postenable_error: + kfree(pdata->buffer_data); +allocate_memory_error: + return err; +} + +static int st_press_buffer_predisable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *pdata = iio_priv(indio_dev); + + err = iio_triggered_buffer_predisable(indio_dev); + if (err < 0) + goto st_press_buffer_predisable_error; + + err = st_sensors_set_enable(indio_dev, false); + +st_press_buffer_predisable_error: + kfree(pdata->buffer_data); + return err; +} + +static const struct iio_buffer_setup_ops st_press_buffer_setup_ops = { + .preenable = &st_press_buffer_preenable, + .postenable = &st_press_buffer_postenable, + .predisable = &st_press_buffer_predisable, +}; + +int st_press_allocate_ring(struct iio_dev *indio_dev) +{ + return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &st_sensors_trigger_handler, &st_press_buffer_setup_ops); +} + +void st_press_deallocate_ring(struct iio_dev *indio_dev) +{ + iio_triggered_buffer_cleanup(indio_dev); +} + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics pressures buffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c new file mode 100644 index 00000000000..9c343b40665 --- /dev/null +++ b/drivers/iio/pressure/st_pressure_core.c @@ -0,0 +1,272 @@ +/* + * STMicroelectronics pressures driver + * + * Copyright 2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/buffer.h> +#include <asm/unaligned.h> + +#include <linux/iio/common/st_sensors.h> +#include "st_pressure.h" + +#define ST_PRESS_MBAR_TO_KPASCAL(x) (x * 10) +#define ST_PRESS_NUMBER_DATA_CHANNELS 1 + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_PRESS_DEFAULT_OUT_XL_ADDR 0x28 +#define ST_TEMP_DEFAULT_OUT_L_ADDR 0x2b + +/* FULLSCALE */ +#define ST_PRESS_FS_AVL_1260MB 1260 + +/* CUSTOM VALUES FOR SENSOR 1 */ +#define ST_PRESS_1_WAI_EXP 0xbb +#define ST_PRESS_1_ODR_ADDR 0x20 +#define ST_PRESS_1_ODR_MASK 0x70 +#define ST_PRESS_1_ODR_AVL_1HZ_VAL 0x01 +#define ST_PRESS_1_ODR_AVL_7HZ_VAL 0x05 +#define ST_PRESS_1_ODR_AVL_13HZ_VAL 0x06 +#define ST_PRESS_1_ODR_AVL_25HZ_VAL 0x07 +#define ST_PRESS_1_PW_ADDR 0x20 +#define ST_PRESS_1_PW_MASK 0x80 +#define ST_PRESS_1_FS_ADDR 0x23 +#define ST_PRESS_1_FS_MASK 0x30 +#define ST_PRESS_1_FS_AVL_1260_VAL 0x00 +#define ST_PRESS_1_FS_AVL_1260_GAIN ST_PRESS_MBAR_TO_KPASCAL(244141) +#define ST_PRESS_1_FS_AVL_TEMP_GAIN 2083000 +#define ST_PRESS_1_BDU_ADDR 0x20 +#define ST_PRESS_1_BDU_MASK 0x04 +#define ST_PRESS_1_DRDY_IRQ_ADDR 0x22 +#define ST_PRESS_1_DRDY_IRQ_MASK 0x04 +#define ST_PRESS_1_MULTIREAD_BIT true +#define ST_PRESS_1_TEMP_OFFSET 42500 + +static const struct iio_chan_spec st_press_channels[] = { + ST_SENSORS_LSM_CHANNELS(IIO_PRESSURE, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 0, IIO_NO_MOD, 'u', IIO_LE, 24, 24, + ST_PRESS_DEFAULT_OUT_XL_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_TEMP, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + -1, 0, IIO_NO_MOD, 's', IIO_LE, 16, 16, + ST_TEMP_DEFAULT_OUT_L_ADDR), + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +static const struct st_sensors st_press_sensors[] = { + { + .wai = ST_PRESS_1_WAI_EXP, + .sensors_supported = { + [0] = LPS331AP_PRESS_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_press_channels, + .odr = { + .addr = ST_PRESS_1_ODR_ADDR, + .mask = ST_PRESS_1_ODR_MASK, + .odr_avl = { + { 1, ST_PRESS_1_ODR_AVL_1HZ_VAL, }, + { 7, ST_PRESS_1_ODR_AVL_7HZ_VAL, }, + { 13, ST_PRESS_1_ODR_AVL_13HZ_VAL, }, + { 25, ST_PRESS_1_ODR_AVL_25HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_PRESS_1_PW_ADDR, + .mask = ST_PRESS_1_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .addr = ST_PRESS_1_FS_ADDR, + .mask = ST_PRESS_1_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .value = ST_PRESS_1_FS_AVL_1260_VAL, + .gain = ST_PRESS_1_FS_AVL_1260_GAIN, + .gain2 = ST_PRESS_1_FS_AVL_TEMP_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_PRESS_1_BDU_ADDR, + .mask = ST_PRESS_1_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_PRESS_1_DRDY_IRQ_ADDR, + .mask = ST_PRESS_1_DRDY_IRQ_MASK, + }, + .multi_read_bit = ST_PRESS_1_MULTIREAD_BIT, + .bootime = 2, + }, +}; + +static int st_press_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val, + int *val2, long mask) +{ + int err; + struct st_sensor_data *pdata = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = st_sensors_read_info_raw(indio_dev, ch, val); + if (err < 0) + goto read_error; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + + switch (ch->type) { + case IIO_PRESSURE: + *val2 = pdata->current_fullscale->gain; + break; + case IIO_TEMP: + *val2 = pdata->current_fullscale->gain2; + break; + default: + err = -EINVAL; + goto read_error; + } + + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + switch (ch->type) { + case IIO_TEMP: + *val = 425; + *val2 = 10; + break; + default: + err = -EINVAL; + goto read_error; + } + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + +read_error: + return err; +} + +static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); +static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); + +static struct attribute *st_press_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_press_attribute_group = { + .attrs = st_press_attributes, +}; + +static const struct iio_info press_info = { + .driver_module = THIS_MODULE, + .attrs = &st_press_attribute_group, + .read_raw = &st_press_read_raw, +}; + +#ifdef CONFIG_IIO_TRIGGER +static const struct iio_trigger_ops st_press_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = ST_PRESS_TRIGGER_SET_STATE, +}; +#define ST_PRESS_TRIGGER_OPS (&st_press_trigger_ops) +#else +#define ST_PRESS_TRIGGER_OPS NULL +#endif + +int st_press_common_probe(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *pdata = iio_priv(indio_dev); + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &press_info; + + err = st_sensors_check_device_support(indio_dev, + ARRAY_SIZE(st_press_sensors), st_press_sensors); + if (err < 0) + goto st_press_common_probe_error; + + pdata->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS; + pdata->multiread_bit = pdata->sensor->multi_read_bit; + indio_dev->channels = pdata->sensor->ch; + indio_dev->num_channels = ARRAY_SIZE(st_press_channels); + + pdata->current_fullscale = (struct st_sensor_fullscale_avl *) + &pdata->sensor->fs.fs_avl[0]; + pdata->odr = pdata->sensor->odr.odr_avl[0].hz; + + err = st_sensors_init_sensor(indio_dev); + if (err < 0) + goto st_press_common_probe_error; + + if (pdata->get_irq_data_ready(indio_dev) > 0) { + err = st_press_allocate_ring(indio_dev); + if (err < 0) + goto st_press_common_probe_error; + + err = st_sensors_allocate_trigger(indio_dev, + ST_PRESS_TRIGGER_OPS); + if (err < 0) + goto st_press_probe_trigger_error; + } + + err = iio_device_register(indio_dev); + if (err) + goto st_press_device_register_error; + + return err; + +st_press_device_register_error: + if (pdata->get_irq_data_ready(indio_dev) > 0) + st_sensors_deallocate_trigger(indio_dev); +st_press_probe_trigger_error: + if (pdata->get_irq_data_ready(indio_dev) > 0) + st_press_deallocate_ring(indio_dev); +st_press_common_probe_error: + return err; +} +EXPORT_SYMBOL(st_press_common_probe); + +void st_press_common_remove(struct iio_dev *indio_dev) +{ + struct st_sensor_data *pdata = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (pdata->get_irq_data_ready(indio_dev) > 0) { + st_sensors_deallocate_trigger(indio_dev); + st_press_deallocate_ring(indio_dev); + } + iio_device_free(indio_dev); +} +EXPORT_SYMBOL(st_press_common_remove); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics pressures driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c new file mode 100644 index 00000000000..7cebcc73bfb --- /dev/null +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -0,0 +1,77 @@ +/* + * STMicroelectronics pressures driver + * + * Copyright 2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> + +#include <linux/iio/common/st_sensors.h> +#include <linux/iio/common/st_sensors_i2c.h> +#include "st_pressure.h" + +static int st_press_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct st_sensor_data *pdata; + int err; + + indio_dev = iio_device_alloc(sizeof(*pdata)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto iio_device_alloc_error; + } + + pdata = iio_priv(indio_dev); + pdata->dev = &client->dev; + + st_sensors_i2c_configure(indio_dev, client, pdata); + + err = st_press_common_probe(indio_dev); + if (err < 0) + goto st_press_common_probe_error; + + return 0; + +st_press_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_press_i2c_remove(struct i2c_client *client) +{ + st_press_common_remove(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id st_press_id_table[] = { + { LPS331AP_PRESS_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_press_id_table); + +static struct i2c_driver st_press_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st-press-i2c", + }, + .probe = st_press_i2c_probe, + .remove = st_press_i2c_remove, + .id_table = st_press_id_table, +}; +module_i2c_driver(st_press_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics pressures i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c new file mode 100644 index 00000000000..17a14907940 --- /dev/null +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -0,0 +1,76 @@ +/* + * STMicroelectronics pressures driver + * + * Copyright 2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> + +#include <linux/iio/common/st_sensors.h> +#include <linux/iio/common/st_sensors_spi.h> +#include "st_pressure.h" + +static int st_press_spi_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct st_sensor_data *pdata; + int err; + + indio_dev = iio_device_alloc(sizeof(*pdata)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto iio_device_alloc_error; + } + + pdata = iio_priv(indio_dev); + pdata->dev = &spi->dev; + + st_sensors_spi_configure(indio_dev, spi, pdata); + + err = st_press_common_probe(indio_dev); + if (err < 0) + goto st_press_common_probe_error; + + return 0; + +st_press_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_press_spi_remove(struct spi_device *spi) +{ + st_press_common_remove(spi_get_drvdata(spi)); + + return 0; +} + +static const struct spi_device_id st_press_id_table[] = { + { LPS331AP_PRESS_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(spi, st_press_id_table); + +static struct spi_driver st_press_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st-press-spi", + }, + .probe = st_press_spi_probe, + .remove = st_press_spi_remove, + .id_table = st_press_id_table, +}; +module_spi_driver(st_press_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics pressures spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig new file mode 100644 index 00000000000..360fd508b08 --- /dev/null +++ b/drivers/iio/trigger/Kconfig @@ -0,0 +1,26 @@ +# +# Industrial I/O standalone triggers +# +menu "Triggers - standalone" + +config IIO_INTERRUPT_TRIGGER + tristate "Generic interrupt trigger" + help + Provides support for using an interrupt of any type as an IIO + trigger. This may be provided by a gpio driver for example. + + To compile this driver as a module, choose M here: the + module will be called iio-trig-interrupt. + +config IIO_SYSFS_TRIGGER + tristate "SYSFS trigger" + depends on SYSFS + select IRQ_WORK + help + Provides support for using SYSFS entry as IIO triggers. + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called iio-trig-sysfs. + +endmenu diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile new file mode 100644 index 00000000000..ce319a51b6a --- /dev/null +++ b/drivers/iio/trigger/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for triggers not associated with iio-devices +# + +obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o +obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c new file mode 100644 index 00000000000..02577ec54c6 --- /dev/null +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -0,0 +1,121 @@ +/* + * Industrial I/O - generic interrupt based trigger support + * + * Copyright (c) 2008-2013 Jonathan Cameron + * + * 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/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + + +struct iio_interrupt_trigger_info { + unsigned int irq; +}; + +static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) +{ + /* Timestamp not currently provided */ + iio_trigger_poll(private, 0); + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops iio_interrupt_trigger_ops = { + .owner = THIS_MODULE, +}; + +static int iio_interrupt_trigger_probe(struct platform_device *pdev) +{ + struct iio_interrupt_trigger_info *trig_info; + struct iio_trigger *trig; + unsigned long irqflags; + struct resource *irq_res; + int irq, ret = 0; + + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + if (irq_res == NULL) + return -ENODEV; + + irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; + + irq = irq_res->start; + + trig = iio_trigger_alloc("irqtrig%d", irq); + if (!trig) { + ret = -ENOMEM; + goto error_ret; + } + + trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); + if (!trig_info) { + ret = -ENOMEM; + goto error_put_trigger; + } + iio_trigger_set_drvdata(trig, trig_info); + trig_info->irq = irq; + trig->ops = &iio_interrupt_trigger_ops; + ret = request_irq(irq, iio_interrupt_trigger_poll, + irqflags, trig->name, trig); + if (ret) { + dev_err(&pdev->dev, + "request IRQ-%d failed", irq); + goto error_free_trig_info; + } + + ret = iio_trigger_register(trig); + if (ret) + goto error_release_irq; + platform_set_drvdata(pdev, trig); + + return 0; + +/* First clean up the partly allocated trigger */ +error_release_irq: + free_irq(irq, trig); +error_free_trig_info: + kfree(trig_info); +error_put_trigger: + iio_trigger_put(trig); +error_ret: + return ret; +} + +static int iio_interrupt_trigger_remove(struct platform_device *pdev) +{ + struct iio_trigger *trig; + struct iio_interrupt_trigger_info *trig_info; + + trig = platform_get_drvdata(pdev); + trig_info = iio_trigger_get_drvdata(trig); + iio_trigger_unregister(trig); + free_irq(trig_info->irq, trig); + kfree(trig_info); + iio_trigger_put(trig); + + return 0; +} + +static struct platform_driver iio_interrupt_trigger_driver = { + .probe = iio_interrupt_trigger_probe, + .remove = iio_interrupt_trigger_remove, + .driver = { + .name = "iio_interrupt_trigger", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(iio_interrupt_trigger_driver); + +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); +MODULE_DESCRIPTION("Interrupt trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c new file mode 100644 index 00000000000..effcd0ac98d --- /dev/null +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -0,0 +1,227 @@ +/* + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/irq_work.h> + +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +struct iio_sysfs_trig { + struct iio_trigger *trig; + struct irq_work work; + int id; + struct list_head l; +}; + +static LIST_HEAD(iio_sysfs_trig_list); +static DEFINE_MUTEX(iio_syfs_trig_list_mut); + +static int iio_sysfs_trigger_probe(int id); +static ssize_t iio_sysfs_trig_add(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + int ret; + unsigned long input; + + ret = kstrtoul(buf, 10, &input); + if (ret) + return ret; + ret = iio_sysfs_trigger_probe(input); + if (ret) + return ret; + return len; +} +static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add); + +static int iio_sysfs_trigger_remove(int id); +static ssize_t iio_sysfs_trig_remove(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + int ret; + unsigned long input; + + ret = kstrtoul(buf, 10, &input); + if (ret) + return ret; + ret = iio_sysfs_trigger_remove(input); + if (ret) + return ret; + return len; +} + +static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove); + +static struct attribute *iio_sysfs_trig_attrs[] = { + &dev_attr_add_trigger.attr, + &dev_attr_remove_trigger.attr, + NULL, +}; + +static const struct attribute_group iio_sysfs_trig_group = { + .attrs = iio_sysfs_trig_attrs, +}; + +static const struct attribute_group *iio_sysfs_trig_groups[] = { + &iio_sysfs_trig_group, + NULL +}; + + +/* Nothing to actually do upon release */ +static void iio_trigger_sysfs_release(struct device *dev) +{ +} + +static struct device iio_sysfs_trig_dev = { + .bus = &iio_bus_type, + .groups = iio_sysfs_trig_groups, + .release = &iio_trigger_sysfs_release, +}; + +static void iio_sysfs_trigger_work(struct irq_work *work) +{ + struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig, + work); + + iio_trigger_poll(trig->trig, 0); +} + +static ssize_t iio_sysfs_trigger_poll(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig); + + irq_work_queue(&sysfs_trig->work); + + return count; +} + +static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll); + +static struct attribute *iio_sysfs_trigger_attrs[] = { + &dev_attr_trigger_now.attr, + NULL, +}; + +static const struct attribute_group iio_sysfs_trigger_attr_group = { + .attrs = iio_sysfs_trigger_attrs, +}; + +static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = { + &iio_sysfs_trigger_attr_group, + NULL +}; + +static const struct iio_trigger_ops iio_sysfs_trigger_ops = { + .owner = THIS_MODULE, +}; + +static int iio_sysfs_trigger_probe(int id) +{ + struct iio_sysfs_trig *t; + int ret; + bool foundit = false; + mutex_lock(&iio_syfs_trig_list_mut); + list_for_each_entry(t, &iio_sysfs_trig_list, l) + if (id == t->id) { + foundit = true; + break; + } + if (foundit) { + ret = -EINVAL; + goto out1; + } + t = kmalloc(sizeof(*t), GFP_KERNEL); + if (t == NULL) { + ret = -ENOMEM; + goto out1; + } + t->id = id; + t->trig = iio_trigger_alloc("sysfstrig%d", id); + if (!t->trig) { + ret = -ENOMEM; + goto free_t; + } + + t->trig->dev.groups = iio_sysfs_trigger_attr_groups; + t->trig->ops = &iio_sysfs_trigger_ops; + t->trig->dev.parent = &iio_sysfs_trig_dev; + iio_trigger_set_drvdata(t->trig, t); + + init_irq_work(&t->work, iio_sysfs_trigger_work); + + ret = iio_trigger_register(t->trig); + if (ret) + goto out2; + list_add(&t->l, &iio_sysfs_trig_list); + __module_get(THIS_MODULE); + mutex_unlock(&iio_syfs_trig_list_mut); + return 0; + +out2: + iio_trigger_put(t->trig); +free_t: + kfree(t); +out1: + mutex_unlock(&iio_syfs_trig_list_mut); + return ret; +} + +static int iio_sysfs_trigger_remove(int id) +{ + bool foundit = false; + struct iio_sysfs_trig *t; + mutex_lock(&iio_syfs_trig_list_mut); + list_for_each_entry(t, &iio_sysfs_trig_list, l) + if (id == t->id) { + foundit = true; + break; + } + if (!foundit) { + mutex_unlock(&iio_syfs_trig_list_mut); + return -EINVAL; + } + + iio_trigger_unregister(t->trig); + iio_trigger_free(t->trig); + + list_del(&t->l); + kfree(t); + module_put(THIS_MODULE); + mutex_unlock(&iio_syfs_trig_list_mut); + return 0; +} + + +static int __init iio_sysfs_trig_init(void) +{ + device_initialize(&iio_sysfs_trig_dev); + dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger"); + return device_add(&iio_sysfs_trig_dev); +} +module_init(iio_sysfs_trig_init); + +static void __exit iio_sysfs_trig_exit(void) +{ + device_unregister(&iio_sysfs_trig_dev); +} +module_exit(iio_sysfs_trig_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:iio-trig-sysfs"); |