From 43ece27e70b2c756e45306791955507f0533e248 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 23 Sep 2016 17:19:41 +0200 Subject: iio:trigger: Add helper function to verify that a trigger belongs to the same device Some triggers can only be attached to the IIO device that corresponds to the same physical device. Currently each driver that requires this implements its own trigger validation function. Introduce a new helper function called iio_trigger_validate_own_device() that can be used to do this check. Having a common implementation avoids code duplication and unnecessary boiler-plate code. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- include/linux/iio/trigger.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index 4f1154f7a33c..ea08302f2d7b 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -170,6 +170,8 @@ void iio_trigger_free(struct iio_trigger *trig); */ bool iio_trigger_using_own(struct iio_dev *indio_dev); +int iio_trigger_validate_own_device(struct iio_trigger *trig, + struct iio_dev *indio_dev); #else struct iio_trigger; -- cgit v1.2.3-70-g09d2 From 0023e67dd8951737588b8af0469446df3ec52afe Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Fri, 23 Sep 2016 23:04:07 -0700 Subject: iio: inkern: add iio_read_channel_offset helper Allow access to underlying channel IIO_CHAN_INFO_OFFSET from a consumer. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 39 ++++++++++++++++++++++++++------------- include/linux/iio/consumer.h | 13 +++++++++++++ 2 files changed, 39 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index c4757e6367e7..29df11572858 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -658,6 +658,31 @@ err_unlock: } EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); +static int iio_read_channel_attribute(struct iio_channel *chan, + int *val, int *val2, + enum iio_chan_info_enum attribute) +{ + int ret; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (chan->indio_dev->info == NULL) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_read(chan, val, val2, attribute); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} + +int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) +{ + return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET); +} +EXPORT_SYMBOL_GPL(iio_read_channel_offset); + int iio_read_channel_processed(struct iio_channel *chan, int *val) { int ret; @@ -687,19 +712,7 @@ EXPORT_SYMBOL_GPL(iio_read_channel_processed); int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { - int ret; - - mutex_lock(&chan->indio_dev->info_exist_lock); - if (chan->indio_dev->info == NULL) { - ret = -ENODEV; - goto err_unlock; - } - - ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE); -err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); - - return ret; + return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE); } EXPORT_SYMBOL_GPL(iio_read_channel_scale); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 9edccfba1ffb..638157234357 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -235,6 +235,19 @@ int iio_write_channel_raw(struct iio_channel *chan, int val); int iio_get_channel_type(struct iio_channel *channel, enum iio_chan_type *type); +/** + * iio_read_channel_offset() - read the offset value for a channel + * @chan: The channel being queried. + * @val: First part of value read back. + * @val2: Second part of value read back. + * + * Note returns a description of what is in val and val2, such + * as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val + * + val2/1e6 + */ +int iio_read_channel_offset(struct iio_channel *chan, int *val, + int *val2); + /** * iio_read_channel_scale() - read the scale value for a channel * @chan: The channel being queried. -- cgit v1.2.3-70-g09d2 From a9a0d64a8b7af406f03b660cbad948cfd34ed2b0 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sat, 1 Oct 2016 15:27:18 +0530 Subject: iio: Declare event_attrs field of iio_info structure as const The event_attrs field of iio_info structure is only initialized once whenever an object of iio_info is created. After that this field is never modified again anywhere in the kernel. So, declare event_attrs field of iio_info as a const struct attribute_group. Checked for occurences throughout the kernel using grep and coccinelle. Signed-off-by: Bhumika Goyal Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index b4a0679e4a49..4591d8ea41bd 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -381,7 +381,7 @@ struct iio_dev; **/ struct iio_info { struct module *driver_module; - struct attribute_group *event_attrs; + const struct attribute_group *event_attrs; const struct attribute_group *attrs; int (*read_raw)(struct iio_dev *indio_dev, -- cgit v1.2.3-70-g09d2 From f3b0deea89039373f0d22eafd1ff65a36e957266 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 26 Sep 2016 20:20:16 -0400 Subject: include: linux: iio: add IIO_ATTR_{RO, WO, RW} and IIO_DEVICE_ATTR_{RO, WO, RW} macros Add new macros: IIO_ATTR_RO, IIO_ATTR_WO, IIO_ATTR_RW, IIO_DEVICE_ATTR_RO, IIO_DEVICE_ATTR_WO and IIO_DEVICE_ATTR_RW to reduce the amount of boiler plate code that is needed for creating new attributes. This mimics the *_RO, *_WO, and *_RW macros that are found in include/linux/device.h and include/linux/sysfs.h. Signed-off-by: Brian Masney Acked-by: Greg Kroah-Hartman Signed-off-by: Jonathan Cameron --- include/linux/iio/sysfs.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'include') diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h index 9cd8f747212f..ce9426c507fd 100644 --- a/include/linux/iio/sysfs.h +++ b/include/linux/iio/sysfs.h @@ -55,10 +55,34 @@ struct iio_const_attr { { .dev_attr = __ATTR(_name, _mode, _show, _store), \ .address = _addr } +#define IIO_ATTR_RO(_name, _addr) \ + { .dev_attr = __ATTR_RO(_name), \ + .address = _addr } + +#define IIO_ATTR_WO(_name, _addr) \ + { .dev_attr = __ATTR_WO(_name), \ + .address = _addr } + +#define IIO_ATTR_RW(_name, _addr) \ + { .dev_attr = __ATTR_RW(_name), \ + .address = _addr } + #define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr) \ struct iio_dev_attr iio_dev_attr_##_name \ = IIO_ATTR(_name, _mode, _show, _store, _addr) +#define IIO_DEVICE_ATTR_RO(_name, _addr) \ + struct iio_dev_attr iio_dev_attr_##_name \ + = IIO_ATTR_RO(_name, _addr) + +#define IIO_DEVICE_ATTR_WO(_name, _addr) \ + struct iio_dev_attr iio_dev_attr_##_name \ + = IIO_ATTR_WO(_name, _addr) + +#define IIO_DEVICE_ATTR_RW(_name, _addr) \ + struct iio_dev_attr iio_dev_attr_##_name \ + = IIO_ATTR_RW(_name, _addr) + #define IIO_DEVICE_ATTR_NAMED(_vname, _name, _mode, _show, _store, _addr) \ struct iio_dev_attr iio_dev_attr_##_vname \ = IIO_ATTR(_name, _mode, _show, _store, _addr) -- cgit v1.2.3-70-g09d2 From 1a8f324aa1f2237caef1c6633734785bbdcffeed Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 28 Sep 2016 13:59:49 -0400 Subject: iio: Implement counter channel type and info constants Quadrature encoders, such as rotary encoders and linear encoders, are devices which are capable of encoding the relative position and direction of motion of a shaft. This patch introduces several IIO constants for supporting quadrature encoder counter devices. IIO_COUNT: Current count (main data provided by the counter device) IIO_INDEX: Counter device index value Signed-off-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 18 ++++++++++++++++++ drivers/iio/industrialio-core.c | 2 ++ include/uapi/linux/iio/types.h | 2 ++ 3 files changed, 22 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index fee35c00cc4e..b8f220f978dd 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -329,6 +329,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale +What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -1579,3 +1580,20 @@ Contact: linux-iio@vger.kernel.org Description: Raw (unscaled no offset etc.) electric conductivity reading that can be processed to siemens per meter. + +What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw +KernelVersion: 4.9 +Contact: linux-iio@vger.kernel.org +Description: + Raw counter device counts from channel Y. For quadrature + counters, multiplication by an available [Y]_scale results in + the counts of a single quadrature signal phase from channel Y. + +What: /sys/bus/iio/devices/iio:deviceX/in_indexY_raw +KernelVersion: 4.9 +Contact: linux-iio@vger.kernel.org +Description: + Raw counter device index value from channel Y. This attribute + provides an absolute positional reference (e.g. a pulse once per + revolution) which may be used to home positional systems as + required. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index fc340ed3dca1..649725bc15c1 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -81,6 +81,8 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_PH] = "ph", [IIO_UVINDEX] = "uvindex", [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity", + [IIO_COUNT] = "count", + [IIO_INDEX] = "index", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 22e5e589a274..e54d14a7f876 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -40,6 +40,8 @@ enum iio_chan_type { IIO_PH, IIO_UVINDEX, IIO_ELECTRICALCONDUCTIVITY, + IIO_COUNT, + IIO_INDEX, }; enum iio_modifier { -- cgit v1.2.3-70-g09d2 From a13e831fcaa7e8af0387aef629d1835cf39c59f0 Mon Sep 17 00:00:00 2001 From: Eva Rachel Retuya Date: Wed, 5 Oct 2016 11:06:21 +0800 Subject: staging: iio: ad7192: implement IIO_CHAN_INFO_SAMP_FREQ This driver predates the availability of IIO_CHAN_INFO_SAMP_FREQ attribute wherein usage has some advantages like it can be accessed by in-kernel consumers as well as reduces the code size. Therefore, use IIO_CHAN_INFO_SAMP_FREQ to implement the sampling_frequency attribute instead of using IIO_DEV_ATTR_SAMP_FREQ() macro. Move code from the functions associated with IIO_DEV_ATTR_SAMP_FREQ() into respective read and write hooks with the mask set to IIO_CHAN_INFO_SAMP_FREQ. Signed-off-by: Eva Rachel Retuya Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7192.c | 84 ++++++++++++---------------------- include/linux/iio/adc/ad_sigma_delta.h | 1 + 2 files changed, 30 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index 1cf6b79801a9..bfa12ceb1e1f 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -322,57 +322,6 @@ out: return ret; } -static ssize_t ad7192_read_frequency(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7192_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->mclk / - (st->f_order * 1024 * AD7192_MODE_RATE(st->mode))); -} - -static ssize_t ad7192_write_frequency(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7192_state *st = iio_priv(indio_dev); - unsigned long lval; - int div, ret; - - ret = kstrtoul(buf, 10, &lval); - if (ret) - return ret; - if (lval == 0) - return -EINVAL; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - div = st->mclk / (lval * st->f_order * 1024); - if (div < 1 || div > 1023) { - ret = -EINVAL; - goto out; - } - - st->mode &= ~AD7192_MODE_RATE(-1); - st->mode |= AD7192_MODE_RATE(div); - ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); - -out: - iio_device_release_direct_mode(indio_dev); - - return ret ? ret : len; -} - -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, - ad7192_read_frequency, - ad7192_write_frequency); - static ssize_t ad7192_show_scale_available(struct device *dev, struct device_attribute *attr, char *buf) @@ -471,7 +420,6 @@ static IIO_DEVICE_ATTR(ac_excitation_en, S_IRUGO | S_IWUSR, AD7192_REG_MODE); static struct attribute *ad7192_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr, &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, &iio_dev_attr_bridge_switch_en.dev_attr.attr, @@ -484,7 +432,6 @@ static const struct attribute_group ad7192_attribute_group = { }; static struct attribute *ad7195_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr, &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, &iio_dev_attr_bridge_switch_en.dev_attr.attr, @@ -536,6 +483,10 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, if (chan->type == IIO_TEMP) *val -= 273 * ad7192_get_temp_scale(unipolar); return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->mclk / + (st->f_order * 1024 * AD7192_MODE_RATE(st->mode)); + return IIO_VAL_INT; } return -EINVAL; @@ -548,7 +499,7 @@ static int ad7192_write_raw(struct iio_dev *indio_dev, long mask) { struct ad7192_state *st = iio_priv(indio_dev); - int ret, i; + int ret, i, div; unsigned int tmp; ret = iio_device_claim_direct_mode(indio_dev); @@ -572,6 +523,22 @@ static int ad7192_write_raw(struct iio_dev *indio_dev, break; } break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) { + ret = -EINVAL; + break; + } + + div = st->mclk / (val * st->f_order * 1024); + if (div < 1 || div > 1023) { + ret = -EINVAL; + break; + } + + st->mode &= ~AD7192_MODE_RATE(-1); + st->mode |= AD7192_MODE_RATE(div); + ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + break; default: ret = -EINVAL; } @@ -585,7 +552,14 @@ static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) { - return IIO_VAL_INT_PLUS_NANO; + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return -EINVAL; + } } static const struct iio_info ad7192_info = { diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index e7fdec4db9da..5ba430cc9a87 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -136,6 +136,7 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig); .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = (_si), \ .scan_type = { \ .sign = 'u', \ -- cgit v1.2.3-70-g09d2 From e0cb35095753c038f42d1b6bf68c4cd063b3fd21 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 3 Oct 2016 11:52:08 -0700 Subject: ARM: bcm2835: Add #define for VCHIQ property message. This comes from the downstream tree and is needed for the new VCHIQ driver in staging. Signed-off-by: Eric Anholt Signed-off-by: Greg Kroah-Hartman --- include/soc/bcm2835/raspberrypi-firmware.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h index 3fb357193f09..a06baffdf580 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -109,6 +109,8 @@ enum rpi_firmware_property_tag { RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, + RPI_FIRMWARE_VCHIQ_INIT = 0x00048010, + RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001, RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, }; -- cgit v1.2.3-70-g09d2 From d45f1a563b92dac7eeff817e8f5178caa47e2c16 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2016 15:16:55 +0200 Subject: staging: vc04_services: fix up rpi firmware functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The raspberrypi-firmware.h file should provide empty functions if we aren't building in that option. This makes it easier to test-build code, and not have odd warnings about unused variables if you just try to #define away the functions. Cc: Daniel Stone Cc: "Noralf Trønnes" Cc: Pranith Kumar Cc: popcornmix Cc: Eric Anholt Signed-off-by: Greg Kroah-Hartman --- include/soc/bcm2835/raspberrypi-firmware.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'include') diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h index a06baffdf580..cb979ad90401 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -115,10 +115,29 @@ enum rpi_firmware_property_tag { RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, }; +#if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE) int rpi_firmware_property(struct rpi_firmware *fw, u32 tag, void *data, size_t len); int rpi_firmware_property_list(struct rpi_firmware *fw, void *data, size_t tag_size); struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node); +#else +static inline int rpi_firmware_property(struct rpi_firmware *fw, u32 tag, + void *data, size_t len) +{ + return 0; +} + +static inline int rpi_firmware_property_list(struct rpi_firmware *fw, + void *data, size_t tag_size) +{ + return 0; +} + +static inline struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node) +{ + return NULL; +} +#endif #endif /* __SOC_RASPBERRY_FIRMWARE_H__ */ -- cgit v1.2.3-70-g09d2 From b440f1d90ec54fd2586537ea46e958343ad4b151 Mon Sep 17 00:00:00 2001 From: Tomas Novotny Date: Tue, 11 Oct 2016 15:57:40 +0200 Subject: iio: dac: mcp4725: use regulator framework Use a standard framework to get the reference voltage. It is done that way in the iio subsystem and it will simplify extending of the driver. Structure mcp4725_platform_data is left undeleted because it used in the next patch. This change breaks the current users of the driver, but there is no mainline user of struct mcp4725_platform_data. Signed-off-by: Tomas Novotny Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4725.c | 46 +++++++++++++++++++++++++++++++++-------- include/linux/iio/dac/mcp4725.h | 1 - 2 files changed, 37 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index cca935c06f2b..2b28b1f5b3a2 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -28,10 +29,10 @@ struct mcp4725_data { struct i2c_client *client; - u16 vref_mv; u16 dac_value; bool powerdown; unsigned powerdown_mode; + struct regulator *vdd_reg; }; static int mcp4725_suspend(struct device *dev) @@ -283,13 +284,18 @@ static int mcp4725_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct mcp4725_data *data = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: *val = data->dac_value; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->vref_mv; + ret = regulator_get_voltage(data->vdd_reg); + if (ret < 0) + return ret; + + *val = ret / 1000; *val2 = 12; return IIO_VAL_FRACTIONAL_LOG2; } @@ -328,12 +334,12 @@ static int mcp4725_probe(struct i2c_client *client, { struct mcp4725_data *data; struct iio_dev *indio_dev; - struct mcp4725_platform_data *platform_data = client->dev.platform_data; + struct mcp4725_platform_data *pdata = dev_get_platdata(&client->dev); u8 inbuf[3]; u8 pd; int err; - if (!platform_data || !platform_data->vref_mv) { + if (!pdata) { dev_err(&client->dev, "invalid platform data"); return -EINVAL; } @@ -345,6 +351,14 @@ static int mcp4725_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; + data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_reg)) + return PTR_ERR(data->vdd_reg); + + err = regulator_enable(data->vdd_reg); + if (err) + return err; + indio_dev->dev.parent = &client->dev; indio_dev->name = id->name; indio_dev->info = &mcp4725_info; @@ -352,25 +366,39 @@ static int mcp4725_probe(struct i2c_client *client, indio_dev->num_channels = 1; indio_dev->modes = INDIO_DIRECT_MODE; - data->vref_mv = platform_data->vref_mv; - /* read current DAC value */ err = i2c_master_recv(client, inbuf, 3); if (err < 0) { dev_err(&client->dev, "failed to read DAC value"); - return err; + goto err_disable_vdd_reg; } pd = (inbuf[0] >> 1) & 0x3; data->powerdown = pd > 0 ? true : false; data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); - return iio_device_register(indio_dev); + err = iio_device_register(indio_dev); + if (err) + goto err_disable_vdd_reg; + + return 0; + + +err_disable_vdd_reg: + regulator_disable(data->vdd_reg); + + return err; } static int mcp4725_remove(struct i2c_client *client) { - iio_device_unregister(i2c_get_clientdata(client)); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct mcp4725_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + regulator_disable(data->vdd_reg); + return 0; } diff --git a/include/linux/iio/dac/mcp4725.h b/include/linux/iio/dac/mcp4725.h index 91530e6611e9..7c062e8d2a48 100644 --- a/include/linux/iio/dac/mcp4725.h +++ b/include/linux/iio/dac/mcp4725.h @@ -10,7 +10,6 @@ #define IIO_DAC_MCP4725_H_ struct mcp4725_platform_data { - u16 vref_mv; }; #endif /* IIO_DAC_MCP4725_H_ */ -- cgit v1.2.3-70-g09d2 From 29157c6d601db8cb9f3bea93fc933b73db3bf869 Mon Sep 17 00:00:00 2001 From: Tomas Novotny Date: Tue, 18 Oct 2016 19:43:08 +0200 Subject: iio: dac: mcp4725: support voltage reference selection MCP47x6 chip supports selection of a voltage reference (VDD, VREF buffered or unbuffered). MCP4725 doesn't have this feature thus the eventual setting is ignored and user is warned. The setting is stored only in the volatile memory of the chip. You need to manually store it to the EEPROM of the chip via 'store_eeprom' sysfs entry. Signed-off-by: Tomas Novotny Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4725.c | 99 ++++++++++++++++++++++++++++++++++++++--- include/linux/iio/dac/mcp4725.h | 11 +++++ 2 files changed, 103 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 5b2dfa0a0d2c..1e9d8f387e00 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -27,12 +27,20 @@ #define MCP4725_DRV_NAME "mcp4725" +#define MCP472X_REF_VDD 0x00 +#define MCP472X_REF_VREF_UNBUFFERED 0x02 +#define MCP472X_REF_VREF_BUFFERED 0x03 + struct mcp4725_data { struct i2c_client *client; + int id; + unsigned ref_mode; + bool vref_buffered; u16 dac_value; bool powerdown; unsigned powerdown_mode; struct regulator *vdd_reg; + struct regulator *vref_reg; }; static int mcp4725_suspend(struct device *dev) @@ -87,6 +95,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev, return 0; inoutbuf[0] = 0x60; /* write EEPROM */ + inoutbuf[0] |= data->ref_mode << 3; inoutbuf[1] = data->dac_value >> 4; inoutbuf[2] = (data->dac_value & 0xf) << 4; @@ -279,6 +288,28 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val) return 0; } +static int mcp4726_set_cfg(struct iio_dev *indio_dev) +{ + struct mcp4725_data *data = iio_priv(indio_dev); + u8 outbuf[3]; + int ret; + + outbuf[0] = 0x40; + outbuf[0] |= data->ref_mode << 3; + if (data->powerdown) + outbuf[0] |= data->powerdown << 1; + outbuf[1] = data->dac_value >> 4; + outbuf[2] = (data->dac_value & 0xf) << 4; + + ret = i2c_master_send(data->client, outbuf, 3); + if (ret < 0) + return ret; + else if (ret != 3) + return -EIO; + else + return 0; +} + static int mcp4725_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -291,7 +322,11 @@ static int mcp4725_read_raw(struct iio_dev *indio_dev, *val = data->dac_value; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(data->vdd_reg); + if (data->ref_mode == MCP472X_REF_VDD) + ret = regulator_get_voltage(data->vdd_reg); + else + ret = regulator_get_voltage(data->vref_reg); + if (ret < 0) return ret; @@ -335,8 +370,9 @@ static int mcp4725_probe(struct i2c_client *client, struct mcp4725_data *data; struct iio_dev *indio_dev; struct mcp4725_platform_data *pdata = dev_get_platdata(&client->dev); - u8 inbuf[3]; + u8 inbuf[4]; u8 pd; + u8 ref; int err; if (!pdata) { @@ -350,6 +386,26 @@ static int mcp4725_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; + data->id = id->driver_data; + + if (data->id == MCP4725 && pdata->use_vref) { + dev_err(&client->dev, + "external reference is unavailable on MCP4725"); + return -EINVAL; + } + + if (!pdata->use_vref && pdata->vref_buffered) { + dev_err(&client->dev, + "buffering is unavailable on the internal reference"); + return -EINVAL; + } + + if (!pdata->use_vref) + data->ref_mode = MCP472X_REF_VDD; + else + data->ref_mode = pdata->vref_buffered ? + MCP472X_REF_VREF_BUFFERED : + MCP472X_REF_VREF_UNBUFFERED; data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(data->vdd_reg)) @@ -359,6 +415,18 @@ static int mcp4725_probe(struct i2c_client *client, if (err) return err; + if (pdata->use_vref) { + data->vref_reg = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(data->vref_reg)) { + err = PTR_ERR(data->vdd_reg); + goto err_disable_vdd_reg; + } + + err = regulator_enable(data->vref_reg); + if (err) + goto err_disable_vdd_reg; + } + indio_dev->dev.parent = &client->dev; indio_dev->name = id->name; indio_dev->info = &mcp4725_info; @@ -366,23 +434,38 @@ static int mcp4725_probe(struct i2c_client *client, indio_dev->num_channels = 1; indio_dev->modes = INDIO_DIRECT_MODE; - /* read current DAC value */ - err = i2c_master_recv(client, inbuf, 3); + /* read current DAC value and settings */ + err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4); + if (err < 0) { dev_err(&client->dev, "failed to read DAC value"); - goto err_disable_vdd_reg; + goto err_disable_vref_reg; } pd = (inbuf[0] >> 1) & 0x3; data->powerdown = pd > 0 ? true : false; data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); - + if (data->id == MCP4726) + ref = (inbuf[3] >> 3) & 0x3; + + if (data->id == MCP4726 && ref != data->ref_mode) { + dev_info(&client->dev, + "voltage reference mode differs (conf: %u, eeprom: %u), setting %u", + data->ref_mode, ref, data->ref_mode); + err = mcp4726_set_cfg(indio_dev); + if (err < 0) + goto err_disable_vref_reg; + } + err = iio_device_register(indio_dev); if (err) - goto err_disable_vdd_reg; + goto err_disable_vref_reg; return 0; +err_disable_vref_reg: + if (data->vref_reg) + regulator_disable(data->vref_reg); err_disable_vdd_reg: regulator_disable(data->vdd_reg); @@ -397,6 +480,8 @@ static int mcp4725_remove(struct i2c_client *client) iio_device_unregister(indio_dev); + if (data->vref_reg) + regulator_disable(data->vref_reg); regulator_disable(data->vdd_reg); return 0; diff --git a/include/linux/iio/dac/mcp4725.h b/include/linux/iio/dac/mcp4725.h index 7c062e8d2a48..628b2cf54c50 100644 --- a/include/linux/iio/dac/mcp4725.h +++ b/include/linux/iio/dac/mcp4725.h @@ -9,7 +9,18 @@ #ifndef IIO_DAC_MCP4725_H_ #define IIO_DAC_MCP4725_H_ +/** + * struct mcp4725_platform_data - MCP4725/6 DAC specific data. + * @use_vref: Whether an external reference voltage on Vref pin should be used. + * Additional vref-supply must be specified when used. + * @vref_buffered: Controls buffering of the external reference voltage. + * + * Vref related settings are available only on MCP4756. See + * Documentation/devicetree/bindings/iio/dac/mcp4725.txt for more information. + */ struct mcp4725_platform_data { + bool use_vref; + bool vref_buffered; }; #endif /* IIO_DAC_MCP4725_H_ */ -- cgit v1.2.3-70-g09d2 From 974e6f02e27e1b46c6c5e600e70ced25079f73eb Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 1 Aug 2016 11:54:35 +0200 Subject: iio: cros_ec_sensors_core: Add common functions for the ChromeOS EC Sensor Hub. Add the core functions to be able to support the sensors attached behind the ChromeOS Embedded Controller and used by other IIO cros-ec sensor drivers. The cros_ec_sensor_core driver matches with current driver in ChromeOS 4.4 tree, so it includes all the fixes at the moment. The support for this driver was made by Gwendal Grignou. The original patch and all the fixes has been squashed and rebased on top of mainline. Signed-off-by: Gwendal Grignou Signed-off-by: Guenter Roeck [eballetbo: split, squash and rebase on top of mainline the patches found in ChromeOS tree] Signed-off-by: Enric Balletbo i Serra Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-cros-ec | 18 + drivers/iio/common/Kconfig | 1 + drivers/iio/common/Makefile | 1 + drivers/iio/common/cros_ec_sensors/Kconfig | 14 + drivers/iio/common/cros_ec_sensors/Makefile | 5 + .../common/cros_ec_sensors/cros_ec_sensors_core.c | 450 +++++++++++++++++++++ .../common/cros_ec_sensors/cros_ec_sensors_core.h | 175 ++++++++ include/linux/mfd/cros_ec.h | 9 + include/linux/mfd/cros_ec_commands.h | 99 ++++- 9 files changed, 767 insertions(+), 5 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-cros-ec create mode 100644 drivers/iio/common/cros_ec_sensors/Kconfig create mode 100644 drivers/iio/common/cros_ec_sensors/Makefile create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c create mode 100644 drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec new file mode 100644 index 000000000000..297b9720f024 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec @@ -0,0 +1,18 @@ +What: /sys/bus/iio/devices/iio:deviceX/calibrate +Date: July 2015 +KernelVersion: 4.7 +Contact: linux-iio@vger.kernel.org +Description: + Writing '1' will perform a FOC (Fast Online Calibration). The + corresponding calibration offsets can be read from *_calibbias + entries. + +What: /sys/bus/iio/devices/iio:deviceX/location +Date: July 2015 +KernelVersion: 4.7 +Contact: linux-iio@vger.kernel.org +Description: + This attribute returns a string with the physical location where + the motion sensor is placed. For example, in a laptop a motion + sensor can be located on the base or on the lid. Current valid + values are 'base' and 'lid'. diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index 26a6026de614..e108996a9627 100644 --- a/drivers/iio/common/Kconfig +++ b/drivers/iio/common/Kconfig @@ -2,6 +2,7 @@ # IIO common modules # +source "drivers/iio/common/cros_ec_sensors/Kconfig" source "drivers/iio/common/hid-sensors/Kconfig" source "drivers/iio/common/ms_sensors/Kconfig" source "drivers/iio/common/ssp_sensors/Kconfig" diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile index 585da6a1b188..6fa760e1bdd5 100644 --- a/drivers/iio/common/Makefile +++ b/drivers/iio/common/Makefile @@ -7,6 +7,7 @@ # # When adding new entries keep the list in alphabetical order +obj-y += cros_ec_sensors/ obj-y += hid-sensors/ obj-y += ms_sensors/ obj-y += ssp_sensors/ diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig new file mode 100644 index 000000000000..24743be15a5b --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/Kconfig @@ -0,0 +1,14 @@ +# +# Chrome OS Embedded Controller managed sensors library +# +config IIO_CROS_EC_SENSORS_CORE + tristate "ChromeOS EC Sensors Core" + depends on SYSFS && MFD_CROS_EC + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Base module for the ChromeOS EC Sensors module. + Contains core functions used by other IIO CrosEC sensor + drivers. + Define common attributes and sysfs interrupt handler. + diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile new file mode 100644 index 000000000000..95b690139bfd --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sensors seen through the ChromeOS EC sensor hub. +# + +obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c new file mode 100644 index 000000000000..a3be7991355e --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -0,0 +1,450 @@ +/* + * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver. + * + * Copyright (C) 2016 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cros_ec_sensors_core.h" + +static char *cros_ec_loc[] = { + [MOTIONSENSE_LOC_BASE] = "base", + [MOTIONSENSE_LOC_LID] = "lid", + [MOTIONSENSE_LOC_MAX] = "unknown", +}; + +int cros_ec_sensors_core_init(struct platform_device *pdev, + struct iio_dev *indio_dev, + bool physical_device) +{ + struct device *dev = &pdev->dev; + struct cros_ec_sensors_core_state *state = iio_priv(indio_dev); + struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev); + + platform_set_drvdata(pdev, indio_dev); + + state->ec = ec->ec_dev; + state->msg = devm_kzalloc(&pdev->dev, + max((u16)sizeof(struct ec_params_motion_sense), + state->ec->max_response), GFP_KERNEL); + if (!state->msg) + return -ENOMEM; + + state->resp = (struct ec_response_motion_sense *)state->msg->data; + + mutex_init(&state->cmd_lock); + + /* Set up the host command structure. */ + state->msg->version = 2; + state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; + state->msg->outsize = sizeof(struct ec_params_motion_sense); + + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = pdev->name; + + if (physical_device) { + indio_dev->modes = INDIO_DIRECT_MODE; + + state->param.cmd = MOTIONSENSE_CMD_INFO; + state->param.info.sensor_num = sensor_platform->sensor_num; + if (cros_ec_motion_send_host_cmd(state, 0)) { + dev_warn(dev, "Can not access sensor info\n"); + return -EIO; + } + state->type = state->resp->info.type; + state->loc = state->resp->info.location; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init); + +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state, + u16 opt_length) +{ + int ret; + + if (opt_length) + state->msg->insize = min(opt_length, state->ec->max_response); + else + state->msg->insize = state->ec->max_response; + + memcpy(state->msg->data, &state->param, sizeof(state->param)); + + ret = cros_ec_cmd_xfer_status(state->ec, state->msg); + if (ret < 0) + return -EIO; + + if (ret && + state->resp != (struct ec_response_motion_sense *)state->msg->data) + memcpy(state->resp, state->msg->data, ret); + + return 0; +} +EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd); + +static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); + int ret, i; + bool calibrate; + + ret = strtobool(buf, &calibrate); + if (ret < 0) + return ret; + if (!calibrate) + return -EINVAL; + + mutex_lock(&st->cmd_lock); + st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB; + ret = cros_ec_motion_send_host_cmd(st, 0); + if (ret != 0) { + dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n"); + } else { + /* Save values */ + for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++) + st->calib[i] = st->resp->perform_calib.offset[i]; + } + mutex_unlock(&st->cmd_lock); + + return ret ? ret : len; +} + +static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + char *buf) +{ + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", cros_ec_loc[st->loc]); +} + +const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = { + { + .name = "calibrate", + .shared = IIO_SHARED_BY_ALL, + .write = cros_ec_sensors_calibrate + }, + { + .name = "location", + .shared = IIO_SHARED_BY_ALL, + .read = cros_ec_sensors_loc + }, + { }, +}; +EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info); + +/** + * cros_ec_sensors_idx_to_reg - convert index into offset in shared memory + * @st: pointer to state information for device + * @idx: sensor index (should be element of enum sensor_index) + * + * Return: address to read at + */ +static unsigned int cros_ec_sensors_idx_to_reg( + struct cros_ec_sensors_core_state *st, + unsigned int idx) +{ + /* + * When using LPC interface, only space for 2 Accel and one Gyro. + * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle. + */ + if (st->type == MOTIONSENSE_TYPE_ACCEL) + return EC_MEMMAP_ACC_DATA + sizeof(u16) * + (1 + idx + st->param.info.sensor_num * + CROS_EC_SENSOR_MAX_AXIS); + + return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx; +} + +static int cros_ec_sensors_cmd_read_u8(struct cros_ec_device *ec, + unsigned int offset, u8 *dest) +{ + return ec->cmd_readmem(ec, offset, 1, dest); +} + +static int cros_ec_sensors_cmd_read_u16(struct cros_ec_device *ec, + unsigned int offset, u16 *dest) +{ + __le16 tmp; + int ret = ec->cmd_readmem(ec, offset, 2, &tmp); + + if (ret >= 0) + *dest = le16_to_cpu(tmp); + + return ret; +} + +/** + * cros_ec_sensors_read_until_not_busy() - read until is not busy + * + * @st: pointer to state information for device + * + * Read from EC status byte until it reads not busy. + * Return: 8-bit status if ok, -errno on failure. + */ +static int cros_ec_sensors_read_until_not_busy( + struct cros_ec_sensors_core_state *st) +{ + struct cros_ec_device *ec = st->ec; + u8 status; + int ret, attempts = 0; + + ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status); + if (ret < 0) + return ret; + + while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) { + /* Give up after enough attempts, return error. */ + if (attempts++ >= 50) + return -EIO; + + /* Small delay every so often. */ + if (attempts % 5 == 0) + msleep(25); + + ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, + &status); + if (ret < 0) + return ret; + } + + return status; +} + +/** + * read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory + * @indio_dev: pointer to IIO device + * @scan_mask: bitmap of the sensor indices to scan + * @data: location to store data + * + * This is the unsafe function for reading the EC data. It does not guarantee + * that the EC will not modify the data as it is being read in. + * + * Return: 0 on success, -errno on failure. + */ +static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev, + unsigned long scan_mask, s16 *data) +{ + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); + struct cros_ec_device *ec = st->ec; + unsigned int i; + int ret; + + /* Read all sensors enabled in scan_mask. Each value is 2 bytes. */ + for_each_set_bit(i, &scan_mask, indio_dev->masklength) { + ret = cros_ec_sensors_cmd_read_u16(ec, + cros_ec_sensors_idx_to_reg(st, i), + data); + if (ret < 0) + return ret; + + data++; + } + + return 0; +} + +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, + unsigned long scan_mask, s16 *data) +{ + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); + struct cros_ec_device *ec = st->ec; + u8 samp_id = 0xff, status = 0; + int ret, attempts = 0; + + /* + * Continually read all data from EC until the status byte after + * all reads reflects that the EC is not busy and the sample id + * matches the sample id from before all reads. This guarantees + * that data read in was not modified by the EC while reading. + */ + while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT | + EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) { + /* If we have tried to read too many times, return error. */ + if (attempts++ >= 5) + return -EIO; + + /* Read status byte until EC is not busy. */ + status = cros_ec_sensors_read_until_not_busy(st); + if (status < 0) + return status; + + /* + * Store the current sample id so that we can compare to the + * sample id after reading the data. + */ + samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; + + /* Read all EC data, format it, and store it into data. */ + ret = cros_ec_sensors_read_data_unsafe(indio_dev, scan_mask, + data); + if (ret < 0) + return ret; + + /* Read status byte. */ + ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, + &status); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc); + +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, + unsigned long scan_mask, s16 *data) +{ + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); + int ret; + unsigned int i; + + /* Read all sensor data through a command. */ + st->param.cmd = MOTIONSENSE_CMD_DATA; + ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data)); + if (ret != 0) { + dev_warn(&indio_dev->dev, "Unable to read sensor data\n"); + return ret; + } + + for_each_set_bit(i, &scan_mask, indio_dev->masklength) { + *data = st->resp->data.data[i]; + data++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd); + +irqreturn_t cros_ec_sensors_capture(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->cmd_lock); + + /* Clear capture data. */ + memset(st->samples, 0, indio_dev->scan_bytes); + + /* Read data based on which channels are enabled in scan mask. */ + ret = st->read_ec_sensors_data(indio_dev, + *(indio_dev->active_scan_mask), + (s16 *)st->samples); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->samples, + iio_get_time_ns(indio_dev)); + +done: + /* + * Tell the core we are done with this trigger and ready for the + * next one. + */ + iio_trigger_notify_done(indio_dev->trig); + + mutex_unlock(&st->cmd_lock); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(cros_ec_sensors_capture); + +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret = IIO_VAL_INT; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; + st->param.ec_rate.data = + EC_MOTION_SENSE_NO_VALUE; + + if (cros_ec_motion_send_host_cmd(st, 0)) + ret = -EIO; + else + *val = st->resp->ec_rate.ret; + break; + case IIO_CHAN_INFO_FREQUENCY: + st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR; + st->param.sensor_odr.data = + EC_MOTION_SENSE_NO_VALUE; + + if (cros_ec_motion_send_host_cmd(st, 0)) + ret = -EIO; + else + *val = st->resp->sensor_odr.ret; + break; + default: + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read); + +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR; + st->param.sensor_odr.data = val; + + /* Always roundup, so caller gets at least what it asks for. */ + st->param.sensor_odr.roundup = 1; + + if (cros_ec_motion_send_host_cmd(st, 0)) + ret = -EIO; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; + st->param.ec_rate.data = val; + + if (cros_ec_motion_send_host_cmd(st, 0)) + ret = -EIO; + else + st->curr_sampl_freq = val; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write); + +MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h new file mode 100644 index 000000000000..8bc2ca3c2e2e --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h @@ -0,0 +1,175 @@ +/* + * ChromeOS EC sensor hub + * + * Copyright (C) 2016 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CROS_EC_SENSORS_CORE_H +#define __CROS_EC_SENSORS_CORE_H + +#include + +enum { + CROS_EC_SENSOR_X, + CROS_EC_SENSOR_Y, + CROS_EC_SENSOR_Z, + CROS_EC_SENSOR_MAX_AXIS, +}; + +/* EC returns sensor values using signed 16 bit registers */ +#define CROS_EC_SENSOR_BITS 16 + +/* + * 4 16 bit channels are allowed. + * Good enough for current sensors, they use up to 3 16 bit vectors. + */ +#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2) + +/* Minimum sampling period to use when device is suspending */ +#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */ + +/** + * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver + * @ec: cros EC device structure + * @cmd_lock: lock used to prevent simultaneous access to the + * commands. + * @msg: cros EC command structure + * @param: motion sensor parameters structure + * @resp: motion sensor response structure + * @type: type of motion sensor + * @loc: location where the motion sensor is placed + * @calib: calibration parameters. Note that trigger + * captured data will always provide the calibrated + * data + * @samples: static array to hold data from a single capture. + * For each channel we need 2 bytes, except for + * the timestamp. The timestamp is always last and + * is always 8-byte aligned. + * @read_ec_sensors_data: function used for accessing sensors values + * @cuur_sampl_freq: current sampling period + */ +struct cros_ec_sensors_core_state { + struct cros_ec_device *ec; + struct mutex cmd_lock; + + struct cros_ec_command *msg; + struct ec_params_motion_sense param; + struct ec_response_motion_sense *resp; + + enum motionsensor_type type; + enum motionsensor_location loc; + + s16 calib[CROS_EC_SENSOR_MAX_AXIS]; + + u8 samples[CROS_EC_SAMPLE_SIZE]; + + int (*read_ec_sensors_data)(struct iio_dev *indio_dev, + unsigned long scan_mask, s16 *data); + + int curr_sampl_freq; +}; + +/** + * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory + * @indio_dev: pointer to IIO device + * @scan_mask: bitmap of the sensor indices to scan + * @data: location to store data + * + * This is the safe function for reading the EC data. It guarantees that the + * data sampled was not modified by the EC while being read. + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask, + s16 *data); + +/** + * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol + * @indio_dev: pointer to IIO device + * @scan_mask: bitmap of the sensor indices to scan + * @data: location to store data + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask, + s16 *data); + +/** + * cros_ec_sensors_core_init() - basic initialization of the core structure + * @pdev: platform device created for the sensors + * @indio_dev: iio device structure of the device + * @physical_device: true if the device refers to a physical device + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_sensors_core_init(struct platform_device *pdev, + struct iio_dev *indio_dev, bool physical_device); + +/** + * cros_ec_sensors_capture() - the trigger handler function + * @irq: the interrupt number. + * @p: a pointer to the poll function. + * + * On a trigger event occurring, if the pollfunc is attached then this + * handler is called as a threaded interrupt (and hence may sleep). It + * is responsible for grabbing data from the device and pushing it into + * the associated buffer. + * + * Return: IRQ_HANDLED + */ +irqreturn_t cros_ec_sensors_capture(int irq, void *p); + +/** + * cros_ec_motion_send_host_cmd() - send motion sense host command + * @st: pointer to state information for device + * @opt_length: optional length to reduce the response size, useful on the data + * path. Otherwise, the maximal allowed response size is used + * + * When called, the sub-command is assumed to be set in param->cmd. + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st, + u16 opt_length); + +/** + * cros_ec_sensors_core_read() - function to request a value from the sensor + * @st: pointer to state information for device + * @chan: channel specification structure table + * @val: will contain one element making up the returned value + * @val2: will contain another element making up the returned value + * @mask: specifies which values to be requested + * + * Return: the type of value returned by the device + */ +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask); + +/** + * cros_ec_sensors_core_write() - function to write a value to the sensor + * @st: pointer to state information for device + * @chan: channel specification structure table + * @val: first part of value to write + * @val2: second part of value to write + * @mask: specifies which values to write + * + * Return: the type of value returned by the device + */ +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, + struct iio_chan_spec const *chan, + int val, int val2, long mask); + +/* List of extended channel specification for all sensors */ +extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[]; + +#endif /* __CROS_EC_SENSORS_CORE_H */ diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 76f7ef4d3a0d..1f85b7aff097 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -148,6 +148,15 @@ struct cros_ec_device { int event_size; }; +/** + * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information + * + * @sensor_num: Id of the sensor, as reported by the EC. + */ +struct cros_ec_sensor_platform { + u8 sensor_num; +}; + /* struct cros_ec_platform - ChromeOS EC platform information * * @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...) diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index 76728ff37d01..8826e0f64b0e 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h @@ -1315,6 +1315,24 @@ enum motionsense_command { */ MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5, + /* + * Returns a single sensor data. + */ + MOTIONSENSE_CMD_DATA = 6, + + /* + * Perform low level calibration.. On sensors that support it, ask to + * do offset calibration. + */ + MOTIONSENSE_CMD_PERFORM_CALIB = 10, + + /* + * Sensor Offset command is a setter/getter command for the offset used + * for calibration. The offsets can be calculated by the host, or via + * PERFORM_CALIB command. + */ + MOTIONSENSE_CMD_SENSOR_OFFSET = 11, + /* Number of motionsense sub-commands. */ MOTIONSENSE_NUM_CMDS }; @@ -1335,12 +1353,18 @@ enum motionsensor_id { enum motionsensor_type { MOTIONSENSE_TYPE_ACCEL = 0, MOTIONSENSE_TYPE_GYRO = 1, + MOTIONSENSE_TYPE_MAG = 2, + MOTIONSENSE_TYPE_PROX = 3, + MOTIONSENSE_TYPE_LIGHT = 4, + MOTIONSENSE_TYPE_ACTIVITY = 5, + MOTIONSENSE_TYPE_MAX }; /* List of motion sensor locations. */ enum motionsensor_location { MOTIONSENSE_LOC_BASE = 0, MOTIONSENSE_LOC_LID = 1, + MOTIONSENSE_LOC_MAX, }; /* List of motion sensor chips. */ @@ -1361,6 +1385,31 @@ enum motionsensor_chip { */ #define EC_MOTION_SENSE_NO_VALUE -1 +#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000 + +/* Set Calibration information */ +#define MOTION_SENSE_SET_OFFSET 1 + +struct ec_response_motion_sensor_data { + /* Flags for each sensor. */ + uint8_t flags; + /* Sensor number the data comes from */ + uint8_t sensor_num; + /* Each sensor is up to 3-axis. */ + union { + int16_t data[3]; + struct { + uint16_t rsvd; + uint32_t timestamp; + } __packed; + struct { + uint8_t activity; /* motionsensor_activity */ + uint8_t state; + int16_t add_info[2]; + }; + }; +} __packed; + struct ec_params_motion_sense { uint8_t cmd; union { @@ -1378,9 +1427,37 @@ struct ec_params_motion_sense { int16_t data; } ec_rate, kb_wake_angle; + /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */ + struct { + uint8_t sensor_num; + + /* + * bit 0: If set (MOTION_SENSE_SET_OFFSET), set + * the calibration information in the EC. + * If unset, just retrieve calibration information. + */ + uint16_t flags; + + /* + * Temperature at calibration, in units of 0.01 C + * 0x8000: invalid / unknown. + * 0x0: 0C + * 0x7fff: +327.67C + */ + int16_t temp; + + /* + * Offset for calibration. + * Unit: + * Accelerometer: 1/1024 g + * Gyro: 1/1024 deg/s + * Compass: 1/16 uT + */ + int16_t offset[3]; + } __packed sensor_offset; + /* Used for MOTIONSENSE_CMD_INFO. */ struct { - /* Should be element of enum motionsensor_id. */ uint8_t sensor_num; } info; @@ -1410,11 +1487,14 @@ struct ec_response_motion_sense { /* Flags representing the motion sensor module. */ uint8_t module_flags; - /* Flags for each sensor in enum motionsensor_id. */ - uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT]; + /* Number of sensors managed directly by the EC. */ + uint8_t sensor_count; - /* Array of all sensor data. Each sensor is 3-axis. */ - int16_t data[3*EC_MOTION_SENSOR_COUNT]; + /* + * Sensor data is truncated if response_max is too small + * for holding all the data. + */ + struct ec_response_motion_sensor_data sensor[0]; } dump; /* Used for MOTIONSENSE_CMD_INFO. */ @@ -1429,6 +1509,9 @@ struct ec_response_motion_sense { uint8_t chip; } info; + /* Used for MOTIONSENSE_CMD_DATA */ + struct ec_response_motion_sensor_data data; + /* * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR, * MOTIONSENSE_CMD_SENSOR_RANGE, and @@ -1438,6 +1521,12 @@ struct ec_response_motion_sense { /* Current value of the parameter queried. */ int32_t ret; } ec_rate, sensor_odr, sensor_range, kb_wake_angle; + + /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */ + struct { + int16_t temp; + int16_t offset[3]; + } sensor_offset, perform_calib; }; } __packed; -- cgit v1.2.3-70-g09d2 From e4244ebddae27e9200146bba897f12a3950ce722 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Mon, 1 Aug 2016 11:54:37 +0200 Subject: platform/chrome: Introduce a new function to check EC features. Use the EC_CMD_GET_FEATURES message to check the supported features for each MCU. Signed-off-by: Vincent Palatin [tomeu: adapted to changes in mainline] Signed-off-by: Tomeu Vizoso [enric: remove references to USB PD feature and do it more generic] Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck For the MFD changes: Acked-by: Lee Jones Signed-off-by: Jonathan Cameron --- drivers/platform/chrome/cros_ec_dev.c | 37 +++++++++++++++ include/linux/mfd/cros_ec.h | 1 + include/linux/mfd/cros_ec_commands.h | 84 +++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) (limited to 'include') diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index 8abd80dbcbed..7eb53078b03d 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -87,6 +87,41 @@ exit: return ret; } +static int cros_ec_check_features(struct cros_ec_dev *ec, int feature) +{ + struct cros_ec_command *msg; + int ret; + + if (ec->features[0] == -1U && ec->features[1] == -1U) { + /* features bitmap not read yet */ + + msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; + msg->insize = sizeof(ec->features); + msg->outsize = 0; + + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + if (ret < 0 || msg->result != EC_RES_SUCCESS) { + dev_warn(ec->dev, "cannot get EC features: %d/%d\n", + ret, msg->result); + memset(ec->features, 0, sizeof(ec->features)); + } + + memcpy(ec->features, msg->data, sizeof(ec->features)); + + dev_dbg(ec->dev, "EC features %08x %08x\n", + ec->features[0], ec->features[1]); + + kfree(msg); + } + + return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); +} + /* Device file ops */ static int ec_device_open(struct inode *inode, struct file *filp) { @@ -245,6 +280,8 @@ static int ec_device_probe(struct platform_device *pdev) ec->ec_dev = dev_get_drvdata(dev->parent); ec->dev = dev; ec->cmd_offset = ec_platform->cmd_offset; + ec->features[0] = -1U; /* Not cached yet */ + ec->features[1] = -1U; /* Not cached yet */ device_initialize(&ec->class_dev); cdev_init(&ec->cdev, &fops); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 1f85b7aff097..f62043a75f43 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -184,6 +184,7 @@ struct cros_ec_dev { struct cros_ec_device *ec_dev; struct device *dev; u16 cmd_offset; + u32 features[2]; }; /** diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index 8826e0f64b0e..1683003603f3 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h @@ -713,6 +713,90 @@ struct ec_response_get_set_value { /* More than one command can use these structs to get/set paramters. */ #define EC_CMD_GSV_PAUSE_IN_S5 0x0c +/*****************************************************************************/ +/* List the features supported by the firmware */ +#define EC_CMD_GET_FEATURES 0x0d + +/* Supported features */ +enum ec_feature_code { + /* + * This image contains a limited set of features. Another image + * in RW partition may support more features. + */ + EC_FEATURE_LIMITED = 0, + /* + * Commands for probing/reading/writing/erasing the flash in the + * EC are present. + */ + EC_FEATURE_FLASH = 1, + /* + * Can control the fan speed directly. + */ + EC_FEATURE_PWM_FAN = 2, + /* + * Can control the intensity of the keyboard backlight. + */ + EC_FEATURE_PWM_KEYB = 3, + /* + * Support Google lightbar, introduced on Pixel. + */ + EC_FEATURE_LIGHTBAR = 4, + /* Control of LEDs */ + EC_FEATURE_LED = 5, + /* Exposes an interface to control gyro and sensors. + * The host goes through the EC to access these sensors. + * In addition, the EC may provide composite sensors, like lid angle. + */ + EC_FEATURE_MOTION_SENSE = 6, + /* The keyboard is controlled by the EC */ + EC_FEATURE_KEYB = 7, + /* The AP can use part of the EC flash as persistent storage. */ + EC_FEATURE_PSTORE = 8, + /* The EC monitors BIOS port 80h, and can return POST codes. */ + EC_FEATURE_PORT80 = 9, + /* + * Thermal management: include TMP specific commands. + * Higher level than direct fan control. + */ + EC_FEATURE_THERMAL = 10, + /* Can switch the screen backlight on/off */ + EC_FEATURE_BKLIGHT_SWITCH = 11, + /* Can switch the wifi module on/off */ + EC_FEATURE_WIFI_SWITCH = 12, + /* Monitor host events, through for example SMI or SCI */ + EC_FEATURE_HOST_EVENTS = 13, + /* The EC exposes GPIO commands to control/monitor connected devices. */ + EC_FEATURE_GPIO = 14, + /* The EC can send i2c messages to downstream devices. */ + EC_FEATURE_I2C = 15, + /* Command to control charger are included */ + EC_FEATURE_CHARGER = 16, + /* Simple battery support. */ + EC_FEATURE_BATTERY = 17, + /* + * Support Smart battery protocol + * (Common Smart Battery System Interface Specification) + */ + EC_FEATURE_SMART_BATTERY = 18, + /* EC can dectect when the host hangs. */ + EC_FEATURE_HANG_DETECT = 19, + /* Report power information, for pit only */ + EC_FEATURE_PMU = 20, + /* Another Cros EC device is present downstream of this one */ + EC_FEATURE_SUB_MCU = 21, + /* Support USB Power delivery (PD) commands */ + EC_FEATURE_USB_PD = 22, + /* Control USB multiplexer, for audio through USB port for instance. */ + EC_FEATURE_USB_MUX = 23, + /* Motion Sensor code has an internal software FIFO */ + EC_FEATURE_MOTION_SENSE_FIFO = 24, +}; + +#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32)) +#define EC_FEATURE_MASK_1(event_code) (1UL << (event_code - 32)) +struct ec_response_get_features { + uint32_t flags[2]; +} __packed; /*****************************************************************************/ /* Flash commands */ -- cgit v1.2.3-70-g09d2 From c9329d8638cfa1a86faf4fb8bd4922a3d9c6c437 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Wed, 5 Oct 2016 14:34:40 +0530 Subject: mfd: ti_am335x_tscadc: store physical address store the physical address of the device in its priv to use it for DMA addressing in the client drivers. Signed-off-by: Mugunthan V N Acked-for-MFD-by: Lee Jones Signed-off-by: Jonathan Cameron --- drivers/mfd/ti_am335x_tscadc.c | 1 + include/linux/mfd/ti_am335x_tscadc.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index c8f027b4ea4c..0f3fab47fe48 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -183,6 +183,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) tscadc->irq = err; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tscadc->tscadc_phys_base = res->start; tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(tscadc->tscadc_base)) return PTR_ERR(tscadc->tscadc_base); diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index 7f55b8b41032..e45a208d9944 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -155,6 +155,7 @@ struct ti_tscadc_dev { struct device *dev; struct regmap *regmap; void __iomem *tscadc_base; + phys_addr_t tscadc_phys_base; int irq; int used_cells; /* 1-2 */ int tsc_wires; -- cgit v1.2.3-70-g09d2 From f438b9da75eb80eb6c4095a5b75324cc9a7f0570 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Wed, 5 Oct 2016 14:34:41 +0530 Subject: drivers: iio: ti_am335x_adc: add dma support This patch adds the required pieces to ti_am335x_adc driver for DMA support Signed-off-by: Mugunthan V N Reviewed-by: Peter Ujfalusi Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++- include/linux/mfd/ti_am335x_tscadc.h | 7 ++ 2 files changed, 152 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index c3cfacca2541..ad9dec30bb30 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -30,10 +30,28 @@ #include #include +#include +#include + +#define DMA_BUFFER_SIZE SZ_2K + +struct tiadc_dma { + struct dma_slave_config conf; + struct dma_chan *chan; + dma_addr_t addr; + dma_cookie_t cookie; + u8 *buf; + int current_period; + int period_size; + u8 fifo_thresh; +}; + struct tiadc_device { struct ti_tscadc_dev *mfd_tscadc; + struct tiadc_dma dma; struct mutex fifo1_lock; /* to protect fifo access */ int channels; + int total_ch_enabled; u8 channel_line[8]; u8 channel_step[8]; int buffer_en_ch_steps; @@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private) return IRQ_HANDLED; } +static void tiadc_dma_rx_complete(void *param) +{ + struct iio_dev *indio_dev = param; + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + u8 *data; + int i; + + data = dma->buf + dma->current_period * dma->period_size; + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */ + + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) { + iio_push_to_buffers(indio_dev, data); + data += indio_dev->scan_bytes; + } +} + +static int tiadc_start_dma(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + struct dma_async_tx_descriptor *desc; + + dma->current_period = 0; /* We start to fill period 0 */ + /* + * Make the fifo thresh as the multiple of total number of + * channels enabled, so make sure that cyclic DMA period + * length is also a multiple of total number of channels + * enabled. This ensures that no invalid data is reported + * to the stack via iio_push_to_buffers(). + */ + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1, + adc_dev->total_ch_enabled) - 1; + /* Make sure that period length is multiple of fifo thresh level */ + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2, + (dma->fifo_thresh + 1) * sizeof(u16)); + + dma->conf.src_maxburst = dma->fifo_thresh + 1; + dmaengine_slave_config(dma->chan, &dma->conf); + + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr, + dma->period_size * 2, + dma->period_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = tiadc_dma_rx_complete; + desc->callback_param = indio_dev; + + dma->cookie = dmaengine_submit(desc); + + dma_async_issue_pending(dma->chan); + + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh); + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh); + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1); + + return 0; +} + static int tiadc_buffer_preenable(struct iio_dev *indio_dev) { struct tiadc_device *adc_dev = iio_priv(indio_dev); @@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev) static int tiadc_buffer_postenable(struct iio_dev *indio_dev) { struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + unsigned int irq_enable; unsigned int enb = 0; u8 bit; tiadc_step_config(indio_dev); - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) { enb |= (get_adc_step_bit(adc_dev, bit) << 1); + adc_dev->total_ch_enabled++; + } adc_dev->buffer_en_ch_steps = enb; + if (dma->chan) + tiadc_start_dma(indio_dev); + am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb); tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW); - tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES - | IRQENB_FIFO1OVRRUN); + + irq_enable = IRQENB_FIFO1OVRRUN; + if (!dma->chan) + irq_enable |= IRQENB_FIFO1THRES; + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable); return 0; } @@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev) static int tiadc_buffer_predisable(struct iio_dev *indio_dev) { struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; int fifo1count, i, read; tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW)); am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps); adc_dev->buffer_en_ch_steps = 0; + adc_dev->total_ch_enabled = 0; + if (dma->chan) { + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2); + dmaengine_terminate_async(dma->chan); + } /* Flush FIFO of leftover data in the time it takes to disable adc */ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); @@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = { .driver_module = THIS_MODULE, }; +static int tiadc_request_dma(struct platform_device *pdev, + struct tiadc_device *adc_dev) +{ + struct tiadc_dma *dma = &adc_dev->dma; + dma_cap_mask_t mask; + + /* Default slave configuration parameters */ + dma->conf.direction = DMA_DEV_TO_MEM; + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1; + + dma_cap_zero(mask); + dma_cap_set(DMA_CYCLIC, mask); + + /* Get a channel for RX */ + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1"); + if (IS_ERR(dma->chan)) { + int ret = PTR_ERR(dma->chan); + + dma->chan = NULL; + return ret; + } + + /* RX buffer */ + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + &dma->addr, GFP_KERNEL); + if (!dma->buf) + goto err; + + return 0; +err: + dma_release_channel(dma->chan); + return -ENOMEM; +} + static int tiadc_parse_dt(struct platform_device *pdev, struct tiadc_device *adc_dev) { @@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, indio_dev); + err = tiadc_request_dma(pdev, adc_dev); + if (err && err == -EPROBE_DEFER) + goto err_dma; + return 0; +err_dma: + iio_device_unregister(indio_dev); err_buffer_unregister: tiadc_iio_buffered_hardware_remove(indio_dev); err_free_channels: @@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; u32 step_en; + if (dma->chan) { + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + dma->buf, dma->addr); + dma_release_channel(dma->chan); + } iio_device_unregister(indio_dev); tiadc_iio_buffered_hardware_remove(indio_dev); tiadc_channels_remove(indio_dev); diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index e45a208d9944..b9a53e013bff 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -23,6 +23,8 @@ #define REG_IRQENABLE 0x02C #define REG_IRQCLR 0x030 #define REG_IRQWAKEUP 0x034 +#define REG_DMAENABLE_SET 0x038 +#define REG_DMAENABLE_CLEAR 0x03c #define REG_CTRL 0x040 #define REG_ADCFSM 0x044 #define REG_CLKDIV 0x04C @@ -36,6 +38,7 @@ #define REG_FIFO0THR 0xE8 #define REG_FIFO1CNT 0xF0 #define REG_FIFO1THR 0xF4 +#define REG_DMA1REQ 0xF8 #define REG_FIFO0 0x100 #define REG_FIFO1 0x200 @@ -126,6 +129,10 @@ #define FIFOREAD_DATA_MASK (0xfff << 0) #define FIFOREAD_CHNLID_MASK (0xf << 16) +/* DMA ENABLE/CLEAR Register */ +#define DMA_FIFO0 BIT(0) +#define DMA_FIFO1 BIT(1) + /* Sequencer Status */ #define SEQ_STATUS BIT(5) #define CHARGE_STEP 0x11 -- cgit v1.2.3-70-g09d2 From 51239600074bc9979b0a0e83b72c726d7dcc3132 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 8 Nov 2016 12:58:51 +0100 Subject: iio:core: add a callback to allow drivers to provide _available attributes A large number of attributes can only take a limited range of values. Currently in IIO this is handled by directly registering additional *_available attributes thus providing this information to userspace. It is desirable to provide this information via the core for much the same reason this was done for the actual channel information attributes in the first place. If it isn't there, then it can only really be accessed from userspace. Other in kernel IIO consumers have no access to what valid parameters are. Two forms are currently supported: * list of values in one particular IIO_VAL_* format. e.g. 1.300000 1.500000 1.730000 * range specification with a step size: e.g. [1.000000 0.500000 2.500000] equivalent to 1.000000 1.5000000 2.000000 2.500000 An addition set of masks are used to allow different sharing rules for the *_available attributes generated. This allows for example: in_accel_x_offset in_accel_y_offset in_accel_offset_available. We could have gone with having a specification for each and every info_mask element but that would have meant changing the existing userspace ABI. This approach does not. Signed-off-by: Jonathan Cameron [forward ported, added some docs and fixed buffer overflows /peda] Acked-by: Daniel Baluta Signed-off-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 259 +++++++++++++++++++++++++++++++++++----- include/linux/iio/iio.h | 29 +++++ include/linux/iio/types.h | 5 + 3 files changed, 260 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 649725bc15c1..aaca42862389 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -577,66 +577,82 @@ int of_iio_read_mount_matrix(const struct device *dev, #endif EXPORT_SYMBOL(of_iio_read_mount_matrix); -/** - * iio_format_value() - Formats a IIO value into its string representation - * @buf: The buffer to which the formatted value gets written - * @type: One of the IIO_VAL_... constants. This decides how the val - * and val2 parameters are formatted. - * @size: Number of IIO value entries contained in vals - * @vals: Pointer to the values, exact meaning depends on the - * type parameter. - * - * Return: 0 by default, a negative number on failure or the - * total number of characters written for a type that belongs - * to the IIO_VAL_... constant. - */ -ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) +static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, + int size, const int *vals) { unsigned long long tmp; + int tmp0, tmp1; bool scale_db = false; switch (type) { case IIO_VAL_INT: - return sprintf(buf, "%d\n", vals[0]); + return snprintf(buf, len, "%d", vals[0]); case IIO_VAL_INT_PLUS_MICRO_DB: scale_db = true; case IIO_VAL_INT_PLUS_MICRO: if (vals[1] < 0) - return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]), - -vals[1], scale_db ? " dB" : ""); + return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]), + -vals[1], scale_db ? " dB" : ""); else - return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1], - scale_db ? " dB" : ""); + return snprintf(buf, len, "%d.%06u%s", vals[0], vals[1], + scale_db ? " dB" : ""); case IIO_VAL_INT_PLUS_NANO: if (vals[1] < 0) - return sprintf(buf, "-%d.%09u\n", abs(vals[0]), - -vals[1]); + return snprintf(buf, len, "-%d.%09u", abs(vals[0]), + -vals[1]); else - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); + return snprintf(buf, len, "%d.%09u", vals[0], vals[1]); case IIO_VAL_FRACTIONAL: tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]); - vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]); - return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1])); + tmp1 = vals[1]; + tmp0 = (int)div_s64_rem(tmp, 1000000000, &tmp1); + return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); case IIO_VAL_FRACTIONAL_LOG2: tmp = (s64)vals[0] * 1000000000LL >> vals[1]; - vals[1] = do_div(tmp, 1000000000LL); - vals[0] = tmp; - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); + tmp1 = do_div(tmp, 1000000000LL); + tmp0 = tmp; + return snprintf(buf, len, "%d.%09u", tmp0, tmp1); case IIO_VAL_INT_MULTIPLE: { int i; - int len = 0; + int l = 0; - for (i = 0; i < size; ++i) - len += snprintf(&buf[len], PAGE_SIZE - len, "%d ", - vals[i]); - len += snprintf(&buf[len], PAGE_SIZE - len, "\n"); - return len; + for (i = 0; i < size; ++i) { + l += snprintf(&buf[l], len - l, "%d ", vals[i]); + if (l >= len) + break; + } + return l; } default: return 0; } } + +/** + * iio_format_value() - Formats a IIO value into its string representation + * @buf: The buffer to which the formatted value gets written + * which is assumed to be big enough (i.e. PAGE_SIZE). + * @type: One of the IIO_VAL_... constants. This decides how the val + * and val2 parameters are formatted. + * @size: Number of IIO value entries contained in vals + * @vals: Pointer to the values, exact meaning depends on the + * type parameter. + * + * Return: 0 by default, a negative number on failure or the + * total number of characters written for a type that belongs + * to the IIO_VAL_... constant. + */ +ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) +{ + ssize_t len; + + len = __iio_format_value(buf, PAGE_SIZE, type, size, vals); + if (len >= PAGE_SIZE - 1) + return -EFBIG; + + return len + sprintf(buf + len, "\n"); +} EXPORT_SYMBOL_GPL(iio_format_value); static ssize_t iio_read_channel_info(struct device *dev, @@ -664,6 +680,119 @@ static ssize_t iio_read_channel_info(struct device *dev, return iio_format_value(buf, ret, val_len, vals); } +static ssize_t iio_format_avail_list(char *buf, const int *vals, + int type, int length) +{ + int i; + ssize_t len = 0; + + switch (type) { + case IIO_VAL_INT: + for (i = 0; i < length; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 1, &vals[i]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < length - 1) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + break; + default: + for (i = 0; i < length / 2; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 2, &vals[i * 2]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < length / 2 - 1) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + } + + return len; +} + +static ssize_t iio_format_avail_range(char *buf, const int *vals, int type) +{ + int i; + ssize_t len; + + len = snprintf(buf, PAGE_SIZE, "["); + switch (type) { + case IIO_VAL_INT: + for (i = 0; i < 3; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 1, &vals[i]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < 2) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "]\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + break; + default: + for (i = 0; i < 3; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 2, &vals[i * 2]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < 2) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "]\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + } + + return len; +} + +static ssize_t iio_read_channel_info_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + const int *vals; + int ret; + int length; + int type; + + ret = indio_dev->info->read_avail(indio_dev, this_attr->c, + &vals, &type, &length, + this_attr->address); + + if (ret < 0) + return ret; + switch (ret) { + case IIO_AVAIL_LIST: + return iio_format_avail_list(buf, vals, type, length); + case IIO_AVAIL_RANGE: + return iio_format_avail_range(buf, vals, type); + default: + return -EINVAL; + } +} + /** * iio_str_to_fixpoint() - Parse a fixed-point number from a string * @str: The string to parse @@ -980,6 +1109,40 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, return attrcount; } +static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum iio_shared_by shared_by, + const long *infomask) +{ + int i, ret, attrcount = 0; + char *avail_postfix; + + for_each_set_bit(i, infomask, sizeof(infomask) * 8) { + avail_postfix = kasprintf(GFP_KERNEL, + "%s_available", + iio_chan_info_postfix[i]); + if (!avail_postfix) + return -ENOMEM; + + ret = __iio_add_chan_devattr(avail_postfix, + chan, + &iio_read_channel_info_avail, + NULL, + i, + shared_by, + &indio_dev->dev, + &indio_dev->channel_attr_list); + kfree(avail_postfix); + if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) + continue; + else if (ret < 0) + return ret; + attrcount++; + } + + return attrcount; +} + static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, struct iio_chan_spec const *chan) { @@ -995,6 +1158,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SEPARATE, + &chan-> + info_mask_separate_available); + if (ret < 0) + return ret; + attrcount += ret; + ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_TYPE, &chan->info_mask_shared_by_type); @@ -1002,6 +1173,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SHARED_BY_TYPE, + &chan-> + info_mask_shared_by_type_available); + if (ret < 0) + return ret; + attrcount += ret; + ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_DIR, &chan->info_mask_shared_by_dir); @@ -1009,6 +1188,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SHARED_BY_DIR, + &chan->info_mask_shared_by_dir_available); + if (ret < 0) + return ret; + attrcount += ret; + ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_ALL, &chan->info_mask_shared_by_all); @@ -1016,6 +1202,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SHARED_BY_ALL, + &chan->info_mask_shared_by_all_available); + if (ret < 0) + return ret; + attrcount += ret; + if (chan->ext_info) { unsigned int i = 0; for (ext_info = chan->ext_info; ext_info->name; ext_info++) { diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 4591d8ea41bd..849d524645e8 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -225,12 +225,22 @@ struct iio_event_spec { * endianness: little or big endian * @info_mask_separate: What information is to be exported that is specific to * this channel. + * @info_mask_separate_available: What availability information is to be + * exported that is specific to this channel. * @info_mask_shared_by_type: What information is to be exported that is shared * by all channels of the same type. + * @info_mask_shared_by_type_available: What availability information is to be + * exported that is shared by all channels of the same + * type. * @info_mask_shared_by_dir: What information is to be exported that is shared * by all channels of the same direction. + * @info_mask_shared_by_dir_available: What availability information is to be + * exported that is shared by all channels of the same + * direction. * @info_mask_shared_by_all: What information is to be exported that is shared * by all channels. + * @info_mask_shared_by_all_available: What availability information is to be + * exported that is shared by all channels. * @event_spec: Array of events which should be registered for this * channel. * @num_event_specs: Size of the event_spec array. @@ -269,9 +279,13 @@ struct iio_chan_spec { enum iio_endian endianness; } scan_type; long info_mask_separate; + long info_mask_separate_available; long info_mask_shared_by_type; + long info_mask_shared_by_type_available; long info_mask_shared_by_dir; + long info_mask_shared_by_dir_available; long info_mask_shared_by_all; + long info_mask_shared_by_all_available; const struct iio_event_spec *event_spec; unsigned int num_event_specs; const struct iio_chan_spec_ext_info *ext_info; @@ -349,6 +363,14 @@ struct iio_dev; * max_len specifies maximum number of elements * vals pointer can contain. val_len is used to return * length of valid elements in vals. + * @read_avail: function to return the available values from the device. + * mask specifies which value. Note 0 means the available + * values for the channel in question. Return value + * specifies if a IIO_AVAIL_LIST or a IIO_AVAIL_RANGE is + * returned in vals. The type of the vals are returned in + * type and the number of vals is returned in length. For + * ranges, there are always three vals returned; min, step + * and max. For lists, all possible values are enumerated. * @write_raw: function to write a value to the device. * Parameters are the same as for read_raw. * @write_raw_get_fmt: callback function to query the expected @@ -397,6 +419,13 @@ struct iio_info { int *val_len, long mask); + int (*read_avail)(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, + int *length, + long mask); + int (*write_raw)(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 32b579525004..2aa7b6384d64 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -29,4 +29,9 @@ enum iio_event_info { #define IIO_VAL_FRACTIONAL 10 #define IIO_VAL_FRACTIONAL_LOG2 11 +enum iio_available_type { + IIO_AVAIL_LIST, + IIO_AVAIL_RANGE, +}; + #endif /* _IIO_TYPES_H_ */ -- cgit v1.2.3-70-g09d2 From 00c5f80c2fad5368cd5bfa6c9d90e75a9041ac16 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Tue, 8 Nov 2016 12:58:52 +0100 Subject: iio: inkern: add helpers to query available values from channels Specifically a helper for reading the available maximum raw value of a channel and a helper for forwarding read_avail requests for raw values from one iio driver to an iio channel that is consumed. These rather specific helpers are in turn built with generic helpers making it easy to build more helpers for available values as needed. Signed-off-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 104 +++++++++++++++++++++++++++++++++++++++++++ include/linux/iio/consumer.h | 28 ++++++++++++ include/linux/iio/iio.h | 17 +++++++ 3 files changed, 149 insertions(+) (limited to 'include') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 29df11572858..b0f4630a163f 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -716,6 +716,110 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) } EXPORT_SYMBOL_GPL(iio_read_channel_scale); +static int iio_channel_read_avail(struct iio_channel *chan, + const int **vals, int *type, int *length, + enum iio_chan_info_enum info) +{ + if (!iio_channel_has_available(chan->channel, info)) + return -EINVAL; + + return chan->indio_dev->info->read_avail(chan->indio_dev, chan->channel, + vals, type, length, info); +} + +int iio_read_avail_channel_raw(struct iio_channel *chan, + const int **vals, int *length) +{ + int ret; + int type; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (!chan->indio_dev->info) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_read_avail(chan, + vals, &type, length, IIO_CHAN_INFO_RAW); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + if (ret >= 0 && type != IIO_VAL_INT) { + /* raw values are assumed to be IIO_VAL_INT */ + ret = -EINVAL; + goto err_unlock; + } + + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); + +static int iio_channel_read_max(struct iio_channel *chan, + int *val, int *val2, int *type, + enum iio_chan_info_enum info) +{ + int unused; + const int *vals; + int length; + int ret; + + if (!val2) + val2 = &unused; + + ret = iio_channel_read_avail(chan, &vals, type, &length, info); + switch (ret) { + case IIO_AVAIL_RANGE: + switch (*type) { + case IIO_VAL_INT: + *val = vals[2]; + break; + default: + *val = vals[4]; + *val2 = vals[5]; + } + return 0; + + case IIO_AVAIL_LIST: + if (length <= 0) + return -EINVAL; + switch (*type) { + case IIO_VAL_INT: + *val = vals[--length]; + while (length) { + if (vals[--length] > *val) + *val = vals[length]; + } + break; + default: + /* FIXME: learn about max for other iio values */ + return -EINVAL; + } + return 0; + + default: + return ret; + } +} + +int iio_read_max_channel_raw(struct iio_channel *chan, int *val) +{ + int ret; + int type; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (!chan->indio_dev->info) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); + int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { int ret = 0; diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 638157234357..47eeec3218b5 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -225,6 +225,34 @@ int iio_read_channel_processed(struct iio_channel *chan, int *val); */ int iio_write_channel_raw(struct iio_channel *chan, int val); +/** + * iio_read_max_channel_raw() - read maximum available raw value from a given + * channel, i.e. the maximum possible value. + * @chan: The channel being queried. + * @val: Value read back. + * + * Note raw reads from iio channels are in adc counts and hence + * scale will need to be applied if standard units are required. + */ +int iio_read_max_channel_raw(struct iio_channel *chan, int *val); + +/** + * iio_read_avail_channel_raw() - read available raw values from a given channel + * @chan: The channel being queried. + * @vals: Available values read back. + * @length: Number of entries in vals. + * + * Returns an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST. + * + * For ranges, three vals are always returned; min, step and max. + * For lists, all the possible values are enumerated. + * + * Note raw available values from iio channels are in adc counts and + * hence scale will need to be applied if standard units are required. + */ +int iio_read_avail_channel_raw(struct iio_channel *chan, + const int **vals, int *length); + /** * iio_get_channel_type() - get the type of a channel * @channel: The channel being queried. diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 849d524645e8..3f5ea2e9a39e 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -315,6 +315,23 @@ static inline bool iio_channel_has_info(const struct iio_chan_spec *chan, (chan->info_mask_shared_by_all & BIT(type)); } +/** + * iio_channel_has_available() - Checks if a channel has an available attribute + * @chan: The channel to be queried + * @type: Type of the available attribute to be checked + * + * Returns true if the channel supports reporting available values for the + * given attribute type, false otherwise. + */ +static inline bool iio_channel_has_available(const struct iio_chan_spec *chan, + enum iio_chan_info_enum type) +{ + return (chan->info_mask_separate_available & BIT(type)) | + (chan->info_mask_shared_by_type_available & BIT(type)) | + (chan->info_mask_shared_by_dir_available & BIT(type)) | + (chan->info_mask_shared_by_all_available & BIT(type)); +} + #define IIO_CHAN_SOFT_TIMESTAMP(_si) { \ .type = IIO_TIMESTAMP, \ .channel = -1, \ -- cgit v1.2.3-70-g09d2