diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-02 11:40:23 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-02 11:40:23 -0700 |
commit | ce49b6289fa3878b190f15192e54bb23dca552b6 (patch) | |
tree | 666f246067e02968d9526fb3e1a5f6ba70c7a4ca /drivers/iio | |
parent | 0de10f9ea67d1046b1e16c91fa26accf23939aab (diff) | |
parent | 0ad1ea69545b1965be4c93ee03fdc685c6beb23d (diff) |
Merge tag 'staging-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging tree update from Greg KH:
"Here's the large staging tree merge for 3.11-rc1
Huge thing here is the Lustre client code. Unfortunatly, due to it
not building properly on a wide variety of different architectures
(this was production code???), it is currently disabled from the build
so as to not annoy people.
Other than Lustre, there are loads of comedi patches, working to clean
up that subsystem, iio updates and new drivers, and a load of cleanups
from the OPW applicants in their quest to get a summer internship.
All of these have been in the linux-next releases for a while (hence
the Lustre code being disabled)"
Fixed up trivial conflict in drivers/staging/serqt_usb2/serqt_usb2.c due
to independent renamings in the staging driver cleanup and the USB
tree..
* tag 'staging-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (868 commits)
Revert "Revert "Revert "staging/lustre: drop CONFIG_BROKEN dependency"""
staging: rtl8192u: fix line length in r819xU_phy.h
staging: rtl8192u: rename variables in r819xU_phy.h
staging: rtl8192u: fix comments in r819xU_phy.h
staging: rtl8192u: fix whitespace in r819xU_phy.h
staging: rtl8192u: fix newlines in r819xU_phy.c
staging: comedi: unioxx5: use comedi_alloc_spriv()
staging: comedi: unioxx5: fix unioxx5_detach()
silicom: checkpatch: errors caused by macros
Staging: silicom: remove the board_t typedef in bpctl_mod.c
Staging: silicom: capitalize labels in the bp_media_type enum
Staging: silicom: remove bp_media_type enum typedef
staging: rtl8192u: replace msleep(1) with usleep_range() in r819xU_phy.c
staging: rtl8192u: rename dwRegRead and rtStatus in r819xU_phy.c
staging: rtl8192u: replace __FUNCTION__ in r819xU_phy.c
staging: rtl8192u: limit line size in r819xU_phy.c
zram: allow request end to coincide with disksize
staging: drm/imx: use generic irq chip unused field to block out invalid irqs
staging: drm/imx: use generic irqchip
staging: drm/imx: ipu-dmfc: use defines for ipu channel numbers
...
Diffstat (limited to 'drivers/iio')
36 files changed, 2230 insertions, 96 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b2f963be3993..9af763a90d93 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 a0e8cdd67e4d..7a3866c2d2a1 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 bb594963f91e..719d83fe51dd 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 e0f5a3ceba5e..4aec121261d7 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 ab0767e6727e..93129ec4b649 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 0a825bed43f6..8f475d31fe4d 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 e5b88d5d3b59..b6db6a0e09cd 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 b3d03d335948..9809fc9a35d2 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 9e6da72ad823..f148d00b83f7 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 000000000000..ebc015922a79 --- /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 09b236d6ee89..71a2c5f63b9c 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 ed9bc8ae9330..865b1781df66 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 b61160bd935e..c9c33ce32d3a 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 5b528ebb3343..c8d7ab6bff01 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 000000000000..85aeef60dc5f --- /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 e76d4ace53ff..a4157cdb314d 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 6be4628faffe..8498e9dcda68 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 225d289082e6..e9dc034aa18b 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 000000000000..129acdf801a4 --- /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 fa9b24219987..f9ed3488c314 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 aaadd32f9f0d..e73033f3839a 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 80d68ff02d29..cdc2cad0f01b 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 bd1cfb666695..c332b0ae4a3b 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 af6c320a534e..7105f22d6cd7 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 16f0d6df239f..ebfe8f11a0c2 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 000000000000..9427f01e1499 --- /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 000000000000..d4bb33e5c846 --- /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 000000000000..414e45ac9b9b --- /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 000000000000..f877ef8af520 --- /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 000000000000..9c343b40665e --- /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 000000000000..7cebcc73bfb0 --- /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 000000000000..17a14907940a --- /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 000000000000..360fd508b088 --- /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 000000000000..ce319a51b6af --- /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 000000000000..02577ec54c6b --- /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 000000000000..effcd0ac98d8 --- /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"); |