Devicetree
 help / color / mirror / Atom feed
* [PATCH v5 0/7] ARM: ASoC: drm: sun8i: Add DE2 HDMI audio and video
From: Jean-Francois Moine @ 2016-10-22 13:28 UTC (permalink / raw)
  To: Dave Airlie, Liam Girdwood, Mark Brown, Maxime Ripard,
	Rob Herring
  Cc: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

This patchset series adds HDMI audio and video support to the Allwinner
sun8i SoCs which include the display engine 2 (DE2).

A first submission in January for video on the H3 could not enter into
the mainline kernel due to the lack of license headers in Allwinner's
sources.

Recently, an announce about Tina OS for the R series
	https://www.youtube.com/watch?v=h7KD-6HblAU
was followed by the upload of a new linux-3.4 source tree
	https://github.com/tinalinux/linux-3.4
with files containing GPL headers.

Well, I don't know if these sources are really from Allwinner, but
anyway, this is the opportunity to propose a new version of my DRM
HDMI driver.

v5:
	- add overlay plane
	- add audio support
	- add support for the A83T
	- add back the HDMI driver
	- many bug fixes
v4: 
	- drivers/clk/sunxi/Makefile was missing (Emil Velikov)
v3:
	- add the hardware cursor
	- simplify and fix the DE2 init sequences
	- generation for all SUNXI SoCs (Andre Przywara)
v2:
	- remove the HDMI driver
	- remarks from Chen-Yu Tsai and Russell King
	- DT documentation added

Jean-Francois Moine (7):
  drm: sunxi: Add a basic DRM driver for Allwinner DE2
  ASoC: sunxi: Add a simple HDMI CODEC
  drm: sunxi: add DE2 HDMI support
  ASoC: sunxi: Add sun8i I2S driver
  ARM: dts: sun8i-h3: add HDMI audio and video nodes
  ARM: dts: sun8i-h3: Add HDMI audio and video to the Banana Pi M2+
  ARM: dts: sun8i-h3: Add HDMI audio and video to the Orange PI 2

 .../devicetree/bindings/display/sunxi/hdmi.txt     |  52 ++
 .../bindings/display/sunxi/sunxi-de2.txt           |  83 ++
 .../devicetree/bindings/sound/sun4i-i2s.txt        |  38 +-
 arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts    |  17 +
 arch/arm/boot/dts/sun8i-h3-orangepi-2.dts          |  17 +
 arch/arm/boot/dts/sun8i-h3.dtsi                    |  67 ++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/sunxi/Kconfig                      |  29 +
 drivers/gpu/drm/sunxi/Makefile                     |   9 +
 drivers/gpu/drm/sunxi/de2_crtc.c                   | 475 +++++++++++
 drivers/gpu/drm/sunxi/de2_crtc.h                   |  63 ++
 drivers/gpu/drm/sunxi/de2_de.c                     | 591 +++++++++++++
 drivers/gpu/drm/sunxi/de2_drm.h                    |  47 ++
 drivers/gpu/drm/sunxi/de2_drv.c                    | 378 +++++++++
 drivers/gpu/drm/sunxi/de2_hdmi.c                   | 396 +++++++++
 drivers/gpu/drm/sunxi/de2_hdmi.h                   |  40 +
 drivers/gpu/drm/sunxi/de2_hdmi_io.c                | 927 +++++++++++++++++++++
 drivers/gpu/drm/sunxi/de2_hdmi_io.h                |  25 +
 drivers/gpu/drm/sunxi/de2_plane.c                  | 119 +++
 include/sound/sunxi_hdmi.h                         |  23 +
 sound/soc/codecs/Kconfig                           |   9 +
 sound/soc/codecs/Makefile                          |   2 +
 sound/soc/codecs/sunxi-hdmi.c                      | 106 +++
 sound/soc/sunxi/Kconfig                            |   8 +
 sound/soc/sunxi/Makefile                           |   3 +
 sound/soc/sunxi/sun8i-i2s.c                        | 700 ++++++++++++++++
 27 files changed, 4222 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
 create mode 100644 drivers/gpu/drm/sunxi/Kconfig
 create mode 100644 drivers/gpu/drm/sunxi/Makefile
 create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.c
 create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.h
 create mode 100644 drivers/gpu/drm/sunxi/de2_de.c
 create mode 100644 drivers/gpu/drm/sunxi/de2_drm.h
 create mode 100644 drivers/gpu/drm/sunxi/de2_drv.c
 create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.c
 create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.h
 create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.c
 create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.h
 create mode 100644 drivers/gpu/drm/sunxi/de2_plane.c
 create mode 100644 include/sound/sunxi_hdmi.h
 create mode 100644 sound/soc/codecs/sunxi-hdmi.c
 create mode 100644 sound/soc/sunxi/sun8i-i2s.c

-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 0/4] IIO wrapper drivers, dpot-dac and envelope-detector
From: Jonathan Cameron @ 2016-10-22 14:26 UTC (permalink / raw)
  To: Peter Rosin
  Cc: Jonathan Cameron, Lars-Peter Clausen,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Hartmut Knaack,
	Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-owner-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <58b9c20d-6c3c-4693-b073-e5a8f9c4cb94-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 21/10/16 23:58, Peter Rosin wrote:
> On 2016-10-21 09:17, jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org wrote:
>> On 20.10.2016 19:17, Peter Rosin wrote:
>>> On 2016-10-20 19:37, Jonathan Cameron wrote:
>>>> On 20 October 2016 18:30:19 BST, Jonathan Cameron 
>>>> <jic23-tko9wxEg+fIOOJlXag/Snyp2UmYkHbXO@public.gmane.org> wrote:
>>>>> On 20 October 2016 13:55:12 BST, Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org> 
>>>>> wrote:
>>>>>> On 10/20/2016 11:25 AM, Peter Rosin wrote:
>>>>>>> Also, is there some agreed-upon way to dig out the maximum value 
>>>>>>> from
>>>>>>> an iio channel? If so, "dpot-dac,max-ohms" can be eliminated from 
>>>>>>> the
>>>>>>> dt bindings, which would have been nice...
>>>>>>
>>>>>> Yes, this is something we could really use. In a sense it exists for
>>>>>> the
>>>>>> devices with buffer-capable channels where there is the real_bits 
>>>>>> field
>>>>>> which tells us the data width of the channel. But a dedicated 
>>>>>> mechanism
>>>>>> for
>>>>>> querying the maximum (and minimum) valid code seems like a useful
>>>>>> feature.
>>>>>> Not only for in-kernel clients, but also for userspace.
>>>>>
>>>>> This was something that was addressed by the rather ancient patch
>>>>> series i posted that added
>>>>> an available call back which provided info on range and values for 
>>>>> all info mask elements.
>>>>> Series got buried by there being a lot of precursors but quite a few of
>>>>> those have merged since.
>>>>>
>>>>> Hmm Google won't let me find it on my phone. Was a while back now. 
>>>>> Will
>>>>> try to get on pc with
>>>>> decent email archive later and dig out a reference.
>>>> http://marc.info/?l=linux-iio&m=138469765309868&w=2 I think...
>>>
>>> Interesting, one issue with that is that it is all in real world
>>> units, while I'd rather have the raw value.
>> Um.. It's been a while, but the principle was (IIRC) that every
>> _available would match the units fo the associated info mask element.
>> Thus if you have a _raw element it would be in adc counts (most likely).
>>
>> _input would be in relevant real world units, scale etc in the whatever
>> units the value itself is in.
> 
> Ok, so I forward ported that patch and added code so that the relevant
> channels provide what is available. I also added code to turn the
> rest of the parameter style devicetree properties into iio device/channel
> attributes. So, it is now much neater from a bindings point of view.
> 
> Before I post the updated patches, I'm wondering what the status is
> on that ancient patch? It didn't forward port without issues, but there
> were no real difficulties that I noticed. Should I just start off my v2
> series with that patch? I tend to think that that's the best option,
> because I suspect that adding a "max-ohms" devicetree property as a
> stop-gap pending some new infrastructure is pretty unrealistic...
> 
> Basically, my question is if that ancient patch as any chance of living
> at all in a form close to what it is, or if should start looking for
> an alternative right away?
The stoppers (IIRC) were that at the time we had a lot of drivers not
making full use off the info_mask stuff so there were a whole load
of precursor patches.  A lot of those have been done since, so we are
probably much more ready for it.

The controversial bit was the question of how to describe ranges, but
I don't think that got all that much attention back then.

If you are happy to take on looking after that series I'd certainly
be very happy!   3 years kind of implies I'm not going to get to
it particularly soon myself :(

Will be interesting to see what reviews we get of it when you post it
though.   Perhaps we deliberately push it into drivers only slowly
initially so that if we decide it was a horrible mistake (or that we
need to make changes to the ABI) we only end up supporting obsolete
ABI in a few drivers...

So a slowly but surely one perhaps rather than mass adoption.

Jonathan
> 
> Cheers,
> Peter
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 0/4] IIO wrapper drivers, dpot-dac and envelope-detector
From: Jonathan Cameron @ 2016-10-22 14:27 UTC (permalink / raw)
  To: Lars-Peter Clausen, Peter Rosin,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <30a4b562-f553-2620-e961-c2904bce9c51-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>

On 20/10/16 16:29, Lars-Peter Clausen wrote:
> On 10/20/2016 04:53 PM, Peter Rosin wrote:
> [...]
>> Good idea! Then the "envelope-detector,inverted" bool can go, and be
>> implied by the compatible string. If some way to rebind the irq trigger
>> is later discovered that can be added as a channel attr without
>> deprecating any dt bindings stuff. While at it, the other properties
>> ("envelope-detector,dac-max" and "envelope-detector,comp-interval-ms")
>> could also be implied from the compatible string. Would that be better?
>> I think so.
>>
>> But, the compatible string is one thing and the driver name is another.
>> "axentia,tse850-envelope-detector" doesn't seem like the best of driver
>> names...
> 
> The driver name is not that important we can still change that later if we
> have to, the DT compatible string on the other hand is fixed.
> 
>>
>> Are there any existing examples of drivers for (generic) things built
>> with discrete components like this that could perhaps provide guidance?
> 
> Not that I'm aware of.
> 
Me neither.  Always interesting to break new ground ;)

J

^ permalink raw reply

* Re: [PATCH 0/4] IIO wrapper drivers, dpot-dac and envelope-detector
From: Jonathan Cameron @ 2016-10-22 14:31 UTC (permalink / raw)
  To: Peter Rosin, Lars-Peter Clausen, linux-kernel
  Cc: Hartmut Knaack, Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
	linux-iio, devicetree
In-Reply-To: <7472292a-a509-c791-4eff-5a5037ead837@axentia.se>

On 20/10/16 15:53, Peter Rosin wrote:
> On 2016-10-20 14:55, Lars-Peter Clausen wrote:
>> On 10/20/2016 11:25 AM, Peter Rosin wrote:
>>> Also, is there some agreed-upon way to dig out the maximum value from
>>> an iio channel? If so, "dpot-dac,max-ohms" can be eliminated from the
>>> dt bindings, which would have been nice...
>>
>> Yes, this is something we could really use. In a sense it exists for the
>> devices with buffer-capable channels where there is the real_bits field
>> which tells us the data width of the channel. But a dedicated mechanism for
>> querying the maximum (and minimum) valid code seems like a useful feature.
>> Not only for in-kernel clients, but also for userspace.
> 
> For the dpot I have, real_bits (if provided) would not be too great since
> the maximum value is 256 (i.e. 257 possible wiper positions). I doesn't
> feel like I'm the most qualified person to add these new min/max attributes
> though, as I'm not familiar with most parts of the iio code. I'll happily
> jump on board if they are somehow magically available, of course :-)
> 
>>> I'm also wondering if I'm somehow abusing the regulator? I only added
>>> it to get rid of a "dpot-dac,max-voltage" thing from the dt bindings.
>>> It feels right though, but maybe I should do more with it than check
>>> its voltage? What?
>>
>> Enable the regulator when it is in use?
> 
> Right, I didn't express myself all that clearly, I do in fact already
> enable the regulator in ->probe and disable it in ->remove. Anything
> else?
Nope.  This is the same thing we do with ADCs that take a reference voltage.
Sometimes we even query the voltage ever time or handle notifiers that
tell use when it changes... (only example I can immediately think of for
that is the sht15 driver in hwmon - my fault a long time ago ;)
> 
>>> There are a couple of things to be said about the envelope detector,
>>> one question is where it should live? I placed it in the adc directory,
>>> but maybe it deserves an iio directory of its own? I'm also a bit
>>> worried that the name is a wee bit too generic. But what is a good
>>> name? I don't want it to be too long like dac-comp-envelope-detector
>>> and something like dac-comp-env-det is just unreadable. Naming is
>>> difficult... And suggestions?
>>
>> Yeah, it is a bit tricky. It is a envelope detector built from discrete
>> components, but of course there are many more ways to build one. If you have
>> a codename for your platform you could use this for the DT compatible
>> string, like 'vendor,foobar-envelope-detector'.
> 
> Good idea! Then the "envelope-detector,inverted" bool can go, and be
> implied by the compatible string. If some way to rebind the irq trigger
> is later discovered that can be added as a channel attr without
> deprecating any dt bindings stuff. While at it, the other properties
> ("envelope-detector,dac-max" and "envelope-detector,comp-interval-ms")
> could also be implied from the compatible string. Would that be better?
> I think so.
> 
> But, the compatible string is one thing and the driver name is another.
> "axentia,tse850-envelope-detector" doesn't seem like the best of driver
> names...
> 
> Are there any existing examples of drivers for (generic) things built
> with discrete components like this that could perhaps provide guidance?
> 
>>> Anyway, despite all the above questions and remarks, this works for
>>> me. Please consider applying.
>>
>> In general this series looks really good, good and clear implementation as
>> well as documentation. A few minor bits here and there, but that is normal.
> 
> Thanks, appreciated!
> 
> Cheers,
> Peter
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [PATCH v4 1/2] iio: humidity: add support to hts221 rh/temp combo device
From: Jonathan Cameron @ 2016-10-22 14:35 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.bianconi-qxv4g6HH51o
In-Reply-To: <1476389165-13542-2-git-send-email-lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>

On 13/10/16 21:06, Lorenzo Bianconi wrote:
> Add support to STM HTS221 humidity + temperature sensor
> 
> http://www.st.com/resource/en/datasheet/hts221.pdf
> 
> - continuous mode support
> - i2c support
> - spi support
> - trigger mode support
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
Couple of really really trivial things inline. I've fixed up.

Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.  This certainly ended up
much simpler than it started after you chased down how the hardware
was actually working!

Thanks,

Jonathan

p.s. Just discovered I didn't send this last weekend when I actually applied the patch!
Sorry about that.
> ---
>  drivers/iio/humidity/Kconfig         |  22 ++
>  drivers/iio/humidity/Makefile        |   7 +
>  drivers/iio/humidity/hts221.h        |  73 ++++
>  drivers/iio/humidity/hts221_buffer.c | 168 +++++++++
>  drivers/iio/humidity/hts221_core.c   | 687 +++++++++++++++++++++++++++++++++++
>  drivers/iio/humidity/hts221_i2c.c    | 110 ++++++
>  drivers/iio/humidity/hts221_spi.c    | 125 +++++++
>  7 files changed, 1192 insertions(+)
>  create mode 100644 drivers/iio/humidity/hts221.h
>  create mode 100644 drivers/iio/humidity/hts221_buffer.c
>  create mode 100644 drivers/iio/humidity/hts221_core.c
>  create mode 100644 drivers/iio/humidity/hts221_i2c.c
>  create mode 100644 drivers/iio/humidity/hts221_spi.c
> 
> diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
> index b17e2e2..6fd11d0 100644
> --- a/drivers/iio/humidity/Kconfig
> +++ b/drivers/iio/humidity/Kconfig
> @@ -34,6 +34,28 @@ config HDC100X
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called hdc100x.
>  
> +config HTS221
> +	tristate "STMicroelectronics HTS221 sensor Driver"
> +	depends on (I2C || SPI)
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	select HTS221_I2C if (I2C)
> +	select HTS221_SPI if (SPI_MASTER)
> +	help
> +	  Say yes here to build support for STMicroelectronics HTS221
> +	  temperature-humidity sensor
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called hts221.
> +
> +config HTS221_I2C
> +	tristate
> +	depends on HTS221
> +
> +config HTS221_SPI
> +	tristate
> +	depends on HTS221
> +
>  config HTU21
>  	tristate "Measurement Specialties HTU21 humidity & temperature sensor"
>  	depends on I2C
> diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile
> index 4a73442..a6850e4 100644
> --- a/drivers/iio/humidity/Makefile
> +++ b/drivers/iio/humidity/Makefile
> @@ -5,6 +5,13 @@
>  obj-$(CONFIG_AM2315) += am2315.o
>  obj-$(CONFIG_DHT11) += dht11.o
>  obj-$(CONFIG_HDC100X) += hdc100x.o
> +
> +hts221-y := hts221_core.o \
> +	    hts221_buffer.o
> +obj-$(CONFIG_HTS221) += hts221.o
> +obj-$(CONFIG_HTS221_I2C) += hts221_i2c.o
> +obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
> +
>  obj-$(CONFIG_HTU21) += htu21.o
>  obj-$(CONFIG_SI7005) += si7005.o
>  obj-$(CONFIG_SI7020) += si7020.o
> diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h
> new file mode 100644
> index 0000000..0d2993c
> --- /dev/null
> +++ b/drivers/iio/humidity/hts221.h
> @@ -0,0 +1,73 @@
> +/*
> + * STMicroelectronics hts221 sensor driver
> + *
> + * Copyright 2016 STMicroelectronics Inc.
> + *
> + * Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef HTS221_H
> +#define HTS221_H
> +
> +#define HTS221_DEV_NAME		"hts221"
> +
> +#include <linux/iio/iio.h>
> +
> +#define HTS221_RX_MAX_LENGTH	8
> +#define HTS221_TX_MAX_LENGTH	8
> +
> +#define HTS221_DATA_SIZE	2
> +
> +struct hts221_transfer_buffer {
> +	u8 rx_buf[HTS221_RX_MAX_LENGTH];
> +	u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
> +};
> +
> +struct hts221_transfer_function {
> +	int (*read)(struct device *dev, u8 addr, int len, u8 *data);
> +	int (*write)(struct device *dev, u8 addr, int len, u8 *data);
> +};
> +
> +#define HTS221_AVG_DEPTH	8
> +struct hts221_avg_avl {
> +	u16 avg;
> +	u8 val;
> +};
> +
> +enum hts221_sensor_type {
> +	HTS221_SENSOR_H,
> +	HTS221_SENSOR_T,
> +	HTS221_SENSOR_MAX,
> +};
> +
> +struct hts221_sensor {
> +	u8 cur_avg_idx;
> +	int slope, b_gen;
> +};
> +
> +struct hts221_hw {
> +	const char *name;
> +	struct device *dev;
> +
> +	struct mutex lock;
> +	struct iio_trigger *trig;
> +	int irq;
> +
> +	struct hts221_sensor sensors[HTS221_SENSOR_MAX];
> +
> +	u8 odr;
> +
> +	const struct hts221_transfer_function *tf;
> +	struct hts221_transfer_buffer tb;
> +};
> +
> +int hts221_config_drdy(struct hts221_hw *hw, bool enable);
> +int hts221_probe(struct iio_dev *iio_dev);
> +int hts221_power_on(struct hts221_hw *hw);
> +int hts221_power_off(struct hts221_hw *hw);
> +int hts221_allocate_buffers(struct hts221_hw *hw);
> +int hts221_allocate_triggers(struct hts221_hw *hw);
> +
> +#endif /* HTS221_H */
> diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c
> new file mode 100644
> index 0000000..6b174fd
> --- /dev/null
> +++ b/drivers/iio/humidity/hts221_buffer.c
> @@ -0,0 +1,168 @@
> +/*
> + * STMicroelectronics hts221 sensor driver
> + *
> + * Copyright 2016 STMicroelectronics Inc.
> + *
> + * Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
> + *
> + * Licensed under the GPL-2.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqreturn.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/events.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include "hts221.h"
> +
> +#define HTS221_REG_STATUS_ADDR		0x27
> +#define HTS221_RH_DRDY_MASK		BIT(1)
> +#define HTS221_TEMP_DRDY_MASK		BIT(0)
> +
> +static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +
> +	return hts221_config_drdy(hw, state);
> +}
> +
> +static const struct iio_trigger_ops hts221_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = hts221_trig_set_state,
> +};
> +
> +static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
> +{
> +	struct hts221_hw *hw = (struct hts221_hw *)private;
> +	u8 status;
> +	int err;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
> +			   &status);
> +	if (err < 0)
> +		return IRQ_HANDLED;
> +
Not kernel style multi line comment. I'll fix it up if I take this version.
> +	/* H_DA bit (humidity data available) is routed to DRDY line.
> +	 * Humidity sample is computed after temperature one.
> +	 * Here we can assume data channels are both available if H_DA bit
> +	 * is set in status register
> +	 */
> +	if (!(status & HTS221_RH_DRDY_MASK))
> +		return IRQ_NONE;
> +
> +	iio_trigger_poll_chained(hw->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +int hts221_allocate_triggers(struct hts221_hw *hw)
Nitpick of the day - there is now only one ;)
> +{
> +	struct iio_dev *iio_dev = iio_priv_to_dev(hw);
> +	unsigned long irq_type;
> +	int err;
> +
> +	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
> +
> +	switch (irq_type) {
> +	case IRQF_TRIGGER_HIGH:
> +	case IRQF_TRIGGER_RISING:
> +		break;
> +	default:
> +		dev_info(hw->dev,
> +			 "mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
> +			 irq_type);
> +		irq_type = IRQF_TRIGGER_RISING;
> +		break;
> +	}
> +
> +	err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
> +					hts221_trigger_handler_thread,
> +					irq_type | IRQF_ONESHOT,
> +					hw->name, hw);
> +	if (err) {
> +		dev_err(hw->dev, "failed to request trigger irq %d\n",
> +			hw->irq);
> +		return err;
> +	}
> +
> +	hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
> +					  iio_dev->name);
> +	if (!hw->trig)
> +		return -ENOMEM;
> +
> +	iio_trigger_set_drvdata(hw->trig, iio_dev);
> +	hw->trig->ops = &hts221_trigger_ops;
> +	hw->trig->dev.parent = hw->dev;
> +	iio_dev->trig = iio_trigger_get(hw->trig);
> +
> +	return devm_iio_trigger_register(hw->dev, hw->trig);
> +}
> +
> +static int hts221_buffer_preenable(struct iio_dev *iio_dev)
> +{
> +	return hts221_power_on(iio_priv(iio_dev));
> +}
> +
> +static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
> +{
> +	return hts221_power_off(iio_priv(iio_dev));
> +}
> +
> +static const struct iio_buffer_setup_ops hts221_buffer_ops = {
> +	.preenable = hts221_buffer_preenable,
> +	.postenable = iio_triggered_buffer_postenable,
> +	.predisable = iio_triggered_buffer_predisable,
> +	.postdisable = hts221_buffer_postdisable,
> +};
> +
> +static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
> +{
> +	u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *iio_dev = pf->indio_dev;
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +	struct iio_chan_spec const *ch;
> +	int err;
> +
> +	/* humidity data */
> +	ch = &iio_dev->channels[HTS221_SENSOR_H];
> +	err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
> +			   buffer);
> +	if (err < 0)
> +		goto out;
> +
> +	/* temperature data */
> +	ch = &iio_dev->channels[HTS221_SENSOR_T];
> +	err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
> +			   buffer + HTS221_DATA_SIZE);
> +	if (err < 0)
> +		goto out;
> +
> +	iio_push_to_buffers_with_timestamp(iio_dev, buffer,
> +					   iio_get_time_ns(iio_dev));
> +
> +out:
> +	iio_trigger_notify_done(hw->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +int hts221_allocate_buffers(struct hts221_hw *hw)
> +{
> +	return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
> +					NULL, hts221_buffer_handler_thread,
> +					&hts221_buffer_ops);
> +}
> +
> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
> new file mode 100644
> index 0000000..5a099dd
> --- /dev/null
> +++ b/drivers/iio/humidity/hts221_core.c
> @@ -0,0 +1,687 @@
> +/*
> + * STMicroelectronics hts221 sensor driver
> + *
> + * Copyright 2016 STMicroelectronics Inc.
> + *
> + * Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/delay.h>
> +#include <asm/unaligned.h>
> +
> +#include "hts221.h"
> +
> +#define HTS221_REG_WHOAMI_ADDR		0x0f
> +#define HTS221_REG_WHOAMI_VAL		0xbc
> +
> +#define HTS221_REG_CNTRL1_ADDR		0x20
> +#define HTS221_REG_CNTRL2_ADDR		0x21
> +#define HTS221_REG_CNTRL3_ADDR		0x22
> +
> +#define HTS221_REG_AVG_ADDR		0x10
> +#define HTS221_REG_H_OUT_L		0x28
> +#define HTS221_REG_T_OUT_L		0x2a
> +
> +#define HTS221_HUMIDITY_AVG_MASK	0x07
> +#define HTS221_TEMP_AVG_MASK		0x38
> +
> +#define HTS221_ODR_MASK			0x87
> +#define HTS221_BDU_MASK			BIT(2)
> +
> +#define HTS221_DRDY_MASK		BIT(2)
> +
> +#define HTS221_ENABLE_SENSOR		BIT(7)
> +
> +#define HTS221_HUMIDITY_AVG_4		0x00 /* 0.4 %RH */
> +#define HTS221_HUMIDITY_AVG_8		0x01 /* 0.3 %RH */
> +#define HTS221_HUMIDITY_AVG_16		0x02 /* 0.2 %RH */
> +#define HTS221_HUMIDITY_AVG_32		0x03 /* 0.15 %RH */
> +#define HTS221_HUMIDITY_AVG_64		0x04 /* 0.1 %RH */
> +#define HTS221_HUMIDITY_AVG_128		0x05 /* 0.07 %RH */
> +#define HTS221_HUMIDITY_AVG_256		0x06 /* 0.05 %RH */
> +#define HTS221_HUMIDITY_AVG_512		0x07 /* 0.03 %RH */
> +
> +#define HTS221_TEMP_AVG_2		0x00 /* 0.08 degC */
> +#define HTS221_TEMP_AVG_4		0x08 /* 0.05 degC */
> +#define HTS221_TEMP_AVG_8		0x10 /* 0.04 degC */
> +#define HTS221_TEMP_AVG_16		0x18 /* 0.03 degC */
> +#define HTS221_TEMP_AVG_32		0x20 /* 0.02 degC */
> +#define HTS221_TEMP_AVG_64		0x28 /* 0.015 degC */
> +#define HTS221_TEMP_AVG_128		0x30 /* 0.01 degC */
> +#define HTS221_TEMP_AVG_256		0x38 /* 0.007 degC */
> +
> +/* calibration registers */
> +#define HTS221_REG_0RH_CAL_X_H		0x36
> +#define HTS221_REG_1RH_CAL_X_H		0x3a
> +#define HTS221_REG_0RH_CAL_Y_H		0x30
> +#define HTS221_REG_1RH_CAL_Y_H		0x31
> +#define HTS221_REG_0T_CAL_X_L		0x3c
> +#define HTS221_REG_1T_CAL_X_L		0x3e
> +#define HTS221_REG_0T_CAL_Y_H		0x32
> +#define HTS221_REG_1T_CAL_Y_H		0x33
> +#define HTS221_REG_T1_T0_CAL_Y_H	0x35
> +
> +struct hts221_odr {
> +	u8 hz;
> +	u8 val;
> +};
> +
> +struct hts221_avg {
> +	u8 addr;
> +	u8 mask;
> +	struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
> +};
> +
> +static const struct hts221_odr hts221_odr_table[] = {
> +	{  1, 0x01 },	/* 1Hz */
> +	{  7, 0x02 },	/* 7Hz */
> +	{ 13, 0x03 },	/* 12.5Hz */
> +};
> +
> +static const struct hts221_avg hts221_avg_list[] = {
> +	{
> +		.addr = HTS221_REG_AVG_ADDR,
> +		.mask = HTS221_HUMIDITY_AVG_MASK,
> +		.avg_avl = {
> +			{ 4, HTS221_HUMIDITY_AVG_4 },
> +			{ 8, HTS221_HUMIDITY_AVG_8 },
> +			{ 16, HTS221_HUMIDITY_AVG_16 },
> +			{ 32, HTS221_HUMIDITY_AVG_32 },
> +			{ 64, HTS221_HUMIDITY_AVG_64 },
> +			{ 128, HTS221_HUMIDITY_AVG_128 },
> +			{ 256, HTS221_HUMIDITY_AVG_256 },
> +			{ 512, HTS221_HUMIDITY_AVG_512 },
> +		},
> +	},
> +	{
> +		.addr = HTS221_REG_AVG_ADDR,
> +		.mask = HTS221_TEMP_AVG_MASK,
> +		.avg_avl = {
> +			{ 2, HTS221_TEMP_AVG_2 },
> +			{ 4, HTS221_TEMP_AVG_4 },
> +			{ 8, HTS221_TEMP_AVG_8 },
> +			{ 16, HTS221_TEMP_AVG_16 },
> +			{ 32, HTS221_TEMP_AVG_32 },
> +			{ 64, HTS221_TEMP_AVG_64 },
> +			{ 128, HTS221_TEMP_AVG_128 },
> +			{ 256, HTS221_TEMP_AVG_256 },
> +		},
> +	},
> +};
> +
> +static const struct iio_chan_spec hts221_channels[] = {
> +	{
> +		.type = IIO_HUMIDITYRELATIVE,
> +		.address = HTS221_REG_H_OUT_L,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_OFFSET) |
> +				      BIT(IIO_CHAN_INFO_SCALE) |
> +				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +		.scan_index = 0,
> +		.scan_type = {
> +			.sign = 's',
> +			.realbits = 16,
> +			.storagebits = 16,
> +			.endianness = IIO_LE,
> +		},
> +	},
> +	{
> +		.type = IIO_TEMP,
> +		.address = HTS221_REG_T_OUT_L,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_OFFSET) |
> +				      BIT(IIO_CHAN_INFO_SCALE) |
> +				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +		.scan_index = 1,
> +		.scan_type = {
> +			.sign = 's',
> +			.realbits = 16,
> +			.storagebits = 16,
> +			.endianness = IIO_LE,
> +		},
> +	},
> +	IIO_CHAN_SOFT_TIMESTAMP(2),
> +};
> +
> +static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
> +				  u8 val)
> +{
> +	u8 data;
> +	int err;
> +
> +	mutex_lock(&hw->lock);
> +
> +	err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
> +	if (err < 0) {
> +		dev_err(hw->dev, "failed to read %02x register\n", addr);
> +		goto unlock;
> +	}
> +
> +	data = (data & ~mask) | (val & mask);
> +
> +	err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
> +	if (err < 0)
> +		dev_err(hw->dev, "failed to write %02x register\n", addr);
> +
> +unlock:
> +	mutex_unlock(&hw->lock);
> +
> +	return err;
> +}
> +
> +static int hts221_check_whoami(struct hts221_hw *hw)
> +{
> +	u8 data;
> +	int err;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
> +			   &data);
> +	if (err < 0) {
> +		dev_err(hw->dev, "failed to read whoami register\n");
> +		return err;
> +	}
> +
> +	if (data != HTS221_REG_WHOAMI_VAL) {
> +		dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
> +			data, HTS221_REG_WHOAMI_VAL);
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +int hts221_config_drdy(struct hts221_hw *hw, bool enable)
> +{
> +	u8 val = enable ? BIT(2) : 0;
> +	int err;
> +
> +	err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
> +				     HTS221_DRDY_MASK, val);
> +
> +	return err < 0 ? err : 0;
> +}
> +
> +static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
> +{
> +	int i, err;
> +	u8 val;
> +
> +	for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
> +		if (hts221_odr_table[i].hz == odr)
> +			break;
> +
> +	if (i == ARRAY_SIZE(hts221_odr_table))
> +		return -EINVAL;
> +
> +	val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
> +	err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
> +				     HTS221_ODR_MASK, val);
> +	if (err < 0)
> +		return err;
> +
> +	hw->odr = odr;
> +
> +	return 0;
> +}
> +
> +static int hts221_update_avg(struct hts221_hw *hw,
> +			     enum hts221_sensor_type type,
> +			     u16 val)
> +{
> +	int i, err;
> +	const struct hts221_avg *avg = &hts221_avg_list[type];
> +
> +	for (i = 0; i < HTS221_AVG_DEPTH; i++)
> +		if (avg->avg_avl[i].avg == val)
> +			break;
> +
> +	if (i == HTS221_AVG_DEPTH)
> +		return -EINVAL;
> +
> +	err = hts221_write_with_mask(hw, avg->addr, avg->mask,
> +				     avg->avg_avl[i].val);
> +	if (err < 0)
> +		return err;
> +
> +	hw->sensors[type].cur_avg_idx = i;
> +
> +	return 0;
> +}
> +
> +static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	int i;
> +	ssize_t len = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
> +		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
> +				 hts221_odr_table[i].hz);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +
> +static ssize_t
> +hts221_sysfs_rh_oversampling_avail(struct device *dev,
> +				   struct device_attribute *attr,
> +				   char *buf)
> +{
> +	const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
> +	ssize_t len = 0;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
> +		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
> +				 avg->avg_avl[i].avg);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +
> +static ssize_t
> +hts221_sysfs_temp_oversampling_avail(struct device *dev,
> +				     struct device_attribute *attr,
> +				     char *buf)
> +{
> +	const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
> +	ssize_t len = 0;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
> +		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
> +				 avg->avg_avl[i].avg);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +
> +int hts221_power_on(struct hts221_hw *hw)
> +{
> +	return hts221_update_odr(hw, hw->odr);
> +}
> +
> +int hts221_power_off(struct hts221_hw *hw)
> +{
> +	u8 data[] = {0x00, 0x00};
> +
> +	return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
> +			     data);
> +}
> +
> +static int hts221_parse_temp_caldata(struct hts221_hw *hw)
> +{
> +	int err, *slope, *b_gen;
> +	s16 cal_x0, cal_x1, cal_y0, cal_y1;
> +	u8 cal0, cal1;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
> +			   sizeof(cal0), &cal0);
> +	if (err < 0)
> +		return err;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
> +			   sizeof(cal1), &cal1);
> +	if (err < 0)
> +		return err;
> +	cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
> +			   sizeof(cal0), &cal0);
> +	if (err < 0)
> +		return err;
> +	cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
> +			   (u8 *)&cal_x0);
> +	if (err < 0)
> +		return err;
> +	cal_x0 = le16_to_cpu(cal_x0);
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
> +			   (u8 *)&cal_x1);
> +	if (err < 0)
> +		return err;
> +	cal_x1 = le16_to_cpu(cal_x1);
> +
> +	slope = &hw->sensors[HTS221_SENSOR_T].slope;
> +	b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
> +
> +	*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
> +	*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
> +		 (cal_x1 - cal_x0);
> +	*b_gen *= 8;
> +
> +	return 0;
> +}
> +
> +static int hts221_parse_rh_caldata(struct hts221_hw *hw)
> +{
> +	int err, *slope, *b_gen;
> +	s16 cal_x0, cal_x1, cal_y0, cal_y1;
> +	u8 data;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
> +			   &data);
> +	if (err < 0)
> +		return err;
> +	cal_y0 = data;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
> +			   &data);
> +	if (err < 0)
> +		return err;
> +	cal_y1 = data;
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
> +			   (u8 *)&cal_x0);
> +	if (err < 0)
> +		return err;
> +	cal_x0 = le16_to_cpu(cal_x0);
> +
> +	err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
> +			   (u8 *)&cal_x1);
> +	if (err < 0)
> +		return err;
> +	cal_x1 = le16_to_cpu(cal_x1);
> +
> +	slope = &hw->sensors[HTS221_SENSOR_H].slope;
> +	b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
> +
> +	*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
> +	*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
> +		 (cal_x1 - cal_x0);
> +	*b_gen *= 8;
> +
> +	return 0;
> +}
> +
> +static int hts221_get_sensor_scale(struct hts221_hw *hw,
> +				   enum iio_chan_type ch_type,
> +				   int *val, int *val2)
> +{
> +	s64 tmp;
> +	s32 rem, div, data;
> +
> +	switch (ch_type) {
> +	case IIO_HUMIDITYRELATIVE:
> +		data = hw->sensors[HTS221_SENSOR_H].slope;
> +		div = (1 << 4) * 1000;
> +		break;
> +	case IIO_TEMP:
> +		data = hw->sensors[HTS221_SENSOR_T].slope;
> +		div = (1 << 6) * 1000;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	tmp = div_s64(data * 1000000000LL, div);
> +	tmp = div_s64_rem(tmp, 1000000000LL, &rem);
> +
> +	*val = tmp;
> +	*val2 = rem;
> +
> +	return IIO_VAL_INT_PLUS_NANO;
> +}
> +
> +static int hts221_get_sensor_offset(struct hts221_hw *hw,
> +				    enum iio_chan_type ch_type,
> +				    int *val, int *val2)
> +{
> +	s64 tmp;
> +	s32 rem, div, data;
> +
> +	switch (ch_type) {
> +	case IIO_HUMIDITYRELATIVE:
> +		data = hw->sensors[HTS221_SENSOR_H].b_gen;
> +		div = hw->sensors[HTS221_SENSOR_H].slope;
> +		break;
> +	case IIO_TEMP:
> +		data = hw->sensors[HTS221_SENSOR_T].b_gen;
> +		div = hw->sensors[HTS221_SENSOR_T].slope;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	tmp = div_s64(data * 1000000000LL, div);
> +	tmp = div_s64_rem(tmp, 1000000000LL, &rem);
> +
> +	*val = tmp;
> +	*val2 = rem;
> +
> +	return IIO_VAL_INT_PLUS_NANO;
> +}
> +
> +static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
> +{
> +	u8 data[HTS221_DATA_SIZE];
> +	int err;
> +
> +	err = hts221_power_on(hw);
> +	if (err < 0)
> +		return err;
> +
> +	msleep(50);
> +
> +	err = hw->tf->read(hw->dev, addr, sizeof(data), data);
> +	if (err < 0)
> +		return err;
> +
> +	hts221_power_off(hw);
> +
> +	*val = (s16)get_unaligned_le16(data);
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int hts221_read_raw(struct iio_dev *iio_dev,
> +			   struct iio_chan_spec const *ch,
> +			   int *val, int *val2, long mask)
> +{
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +	int ret;
> +
> +	ret = iio_device_claim_direct_mode(iio_dev);
> +	if (ret)
> +		return ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = hts221_read_oneshot(hw, ch->address, val);
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
> +		break;
> +	case IIO_CHAN_INFO_OFFSET:
> +		ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
> +		break;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = hw->odr;
> +		ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
> +		u8 idx;
> +		const struct hts221_avg *avg;
> +
> +		switch (ch->type) {
> +		case IIO_HUMIDITYRELATIVE:
> +			avg = &hts221_avg_list[HTS221_SENSOR_H];
> +			idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
> +			*val = avg->avg_avl[idx].avg;
> +			ret = IIO_VAL_INT;
> +			break;
> +		case IIO_TEMP:
> +			avg = &hts221_avg_list[HTS221_SENSOR_T];
> +			idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
> +			*val = avg->avg_avl[idx].avg;
> +			ret = IIO_VAL_INT;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +		break;
> +	}
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	iio_device_release_direct_mode(iio_dev);
> +
> +	return ret;
> +}
> +
> +static int hts221_write_raw(struct iio_dev *iio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int val, int val2, long mask)
> +{
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +	int ret;
> +
> +	ret = iio_device_claim_direct_mode(iio_dev);
> +	if (ret)
> +		return ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = hts221_update_odr(hw, val);
> +		break;
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		switch (chan->type) {
> +		case IIO_HUMIDITYRELATIVE:
> +			ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
> +			break;
> +		case IIO_TEMP:
> +			ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	iio_device_release_direct_mode(iio_dev);
> +
> +	return ret;
> +}
> +
> +static int hts221_validate_trigger(struct iio_dev *iio_dev,
> +				   struct iio_trigger *trig)
> +{
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +
> +	return hw->trig == trig ? 0 : -EINVAL;
> +}
> +
> +static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
> +		       hts221_sysfs_rh_oversampling_avail, NULL, 0);
> +static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
> +		       hts221_sysfs_temp_oversampling_avail, NULL, 0);
> +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
> +
> +static struct attribute *hts221_attributes[] = {
> +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +	&iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
> +	&iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group hts221_attribute_group = {
> +	.attrs = hts221_attributes,
> +};
> +
> +static const struct iio_info hts221_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &hts221_attribute_group,
> +	.read_raw = hts221_read_raw,
> +	.write_raw = hts221_write_raw,
> +	.validate_trigger = hts221_validate_trigger,
> +};
> +
> +static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
> +
> +int hts221_probe(struct iio_dev *iio_dev)
> +{
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +	int err;
> +	u8 data;
> +
> +	mutex_init(&hw->lock);
> +
> +	err = hts221_check_whoami(hw);
> +	if (err < 0)
> +		return err;
> +
> +	hw->odr = hts221_odr_table[0].hz;
> +
> +	iio_dev->modes = INDIO_DIRECT_MODE;
> +	iio_dev->dev.parent = hw->dev;
> +	iio_dev->available_scan_masks = hts221_scan_masks;
> +	iio_dev->channels = hts221_channels;
> +	iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
> +	iio_dev->name = HTS221_DEV_NAME;
> +	iio_dev->info = &hts221_info;
> +
> +	/* configure humidity sensor */
> +	err = hts221_parse_rh_caldata(hw);
> +	if (err < 0) {
> +		dev_err(hw->dev, "failed to get rh calibration data\n");
> +		return err;
> +	}
> +
> +	data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
> +	err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
> +	if (err < 0) {
> +		dev_err(hw->dev, "failed to set rh oversampling ratio\n");
> +		return err;
> +	}
> +
> +	/* configure temperature sensor */
> +	err = hts221_parse_temp_caldata(hw);
> +	if (err < 0) {
> +		dev_err(hw->dev,
> +			"failed to get temperature calibration data\n");
> +		return err;
> +	}
> +
> +	data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
> +	err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
> +	if (err < 0) {
> +		dev_err(hw->dev,
> +			"failed to set temperature oversampling ratio\n");
> +		return err;
> +	}
> +
> +	if (hw->irq > 0) {
> +		err = hts221_allocate_buffers(hw);
> +		if (err < 0)
> +			return err;
> +
> +		err = hts221_allocate_triggers(hw);
> +		if (err)
> +			return err;
> +	}
> +
> +	return devm_iio_device_register(hw->dev, iio_dev);
> +}
> +EXPORT_SYMBOL(hts221_probe);
> +
> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c
> new file mode 100644
> index 0000000..367ecd5
> --- /dev/null
> +++ b/drivers/iio/humidity/hts221_i2c.c
> @@ -0,0 +1,110 @@
> +/*
> + * STMicroelectronics hts221 i2c driver
> + *
> + * Copyright 2016 STMicroelectronics Inc.
> + *
> + * Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include "hts221.h"
> +
> +#define I2C_AUTO_INCREMENT	0x80
> +
> +static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
> +{
> +	struct i2c_msg msg[2];
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	if (len > 1)
> +		addr |= I2C_AUTO_INCREMENT;
> +
> +	msg[0].addr = client->addr;
> +	msg[0].flags = client->flags;
> +	msg[0].len = 1;
> +	msg[0].buf = &addr;
> +
> +	msg[1].addr = client->addr;
> +	msg[1].flags = client->flags | I2C_M_RD;
> +	msg[1].len = len;
> +	msg[1].buf = data;
> +
> +	return i2c_transfer(client->adapter, msg, 2);
> +}
> +
> +static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
> +{
> +	u8 send[len + 1];
> +	struct i2c_msg msg;
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	if (len > 1)
> +		addr |= I2C_AUTO_INCREMENT;
> +
> +	send[0] = addr;
> +	memcpy(&send[1], data, len * sizeof(u8));
> +
> +	msg.addr = client->addr;
> +	msg.flags = client->flags;
> +	msg.len = len + 1;
> +	msg.buf = send;
> +
> +	return i2c_transfer(client->adapter, &msg, 1);
> +}
> +
> +static const struct hts221_transfer_function hts221_transfer_fn = {
> +	.read = hts221_i2c_read,
> +	.write = hts221_i2c_write,
> +};
> +
> +static int hts221_i2c_probe(struct i2c_client *client,
> +			    const struct i2c_device_id *id)
> +{
> +	struct hts221_hw *hw;
> +	struct iio_dev *iio_dev;
> +
> +	iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*hw));
> +	if (!iio_dev)
> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(client, iio_dev);
> +
> +	hw = iio_priv(iio_dev);
> +	hw->name = client->name;
> +	hw->dev = &client->dev;
> +	hw->irq = client->irq;
> +	hw->tf = &hts221_transfer_fn;
> +
> +	return hts221_probe(iio_dev);
> +}
> +
> +static const struct of_device_id hts221_i2c_of_match[] = {
> +	{ .compatible = "st,hts221", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hts221_i2c_of_match);
> +
> +static const struct i2c_device_id hts221_i2c_id_table[] = {
> +	{ HTS221_DEV_NAME },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table);
> +
> +static struct i2c_driver hts221_driver = {
> +	.driver = {
> +		.name = "hts221_i2c",
> +		.of_match_table = of_match_ptr(hts221_i2c_of_match),
> +	},
> +	.probe = hts221_i2c_probe,
> +	.id_table = hts221_i2c_id_table,
> +};
> +module_i2c_driver(hts221_driver);
> +
> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics hts221 i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c
> new file mode 100644
> index 0000000..70df5e7
> --- /dev/null
> +++ b/drivers/iio/humidity/hts221_spi.c
> @@ -0,0 +1,125 @@
> +/*
> + * STMicroelectronics hts221 spi driver
> + *
> + * Copyright 2016 STMicroelectronics Inc.
> + *
> + * Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include "hts221.h"
> +
> +#define SENSORS_SPI_READ	0x80
> +#define SPI_AUTO_INCREMENT	0x40
> +
> +static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
> +{
> +	int err;
> +	struct spi_device *spi = to_spi_device(dev);
> +	struct iio_dev *iio_dev = spi_get_drvdata(spi);
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = hw->tb.tx_buf,
> +			.bits_per_word = 8,
> +			.len = 1,
> +		},
> +		{
> +			.rx_buf = hw->tb.rx_buf,
> +			.bits_per_word = 8,
> +			.len = len,
> +		}
> +	};
> +
> +	if (len > 1)
> +		addr |= SPI_AUTO_INCREMENT;
> +	hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
> +
> +	err = spi_sync_transfer(spi, xfers,  ARRAY_SIZE(xfers));
> +	if (err < 0)
> +		return err;
> +
> +	memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
> +
> +	return len;
> +}
> +
> +static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +	struct iio_dev *iio_dev = spi_get_drvdata(spi);
> +	struct hts221_hw *hw = iio_priv(iio_dev);
> +
> +	struct spi_transfer xfers = {
> +		.tx_buf = hw->tb.tx_buf,
> +		.bits_per_word = 8,
> +		.len = len + 1,
> +	};
> +
> +	if (len >= HTS221_TX_MAX_LENGTH)
> +		return -ENOMEM;
> +
> +	if (len > 1)
> +		addr |= SPI_AUTO_INCREMENT;
> +	hw->tb.tx_buf[0] = addr;
> +	memcpy(&hw->tb.tx_buf[1], data, len);
> +
> +	return spi_sync_transfer(spi, &xfers, 1);
> +}
> +
> +static const struct hts221_transfer_function hts221_transfer_fn = {
> +	.read = hts221_spi_read,
> +	.write = hts221_spi_write,
> +};
> +
> +static int hts221_spi_probe(struct spi_device *spi)
> +{
> +	struct hts221_hw *hw;
> +	struct iio_dev *iio_dev;
> +
> +	iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*hw));
> +	if (!iio_dev)
> +		return -ENOMEM;
> +
> +	spi_set_drvdata(spi, iio_dev);
> +
> +	hw = iio_priv(iio_dev);
> +	hw->name = spi->modalias;
> +	hw->dev = &spi->dev;
> +	hw->irq = spi->irq;
> +	hw->tf = &hts221_transfer_fn;
> +
> +	return hts221_probe(iio_dev);
> +}
> +
> +static const struct of_device_id hts221_spi_of_match[] = {
> +	{ .compatible = "st,hts221", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
> +
> +static const struct spi_device_id hts221_spi_id_table[] = {
> +	{ HTS221_DEV_NAME },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
> +
> +static struct spi_driver hts221_driver = {
> +	.driver = {
> +		.name = "hts221_spi",
> +		.of_match_table = of_match_ptr(hts221_spi_of_match),
> +	},
> +	.probe = hts221_spi_probe,
> +	.id_table = hts221_spi_id_table,
> +};
> +module_spi_driver(hts221_driver);
> +
> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics hts221 spi driver");
> +MODULE_LICENSE("GPL v2");
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/4] dt-bindings: iio: document dpot-dac bindings
From: Jonathan Cameron @ 2016-10-22 14:36 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1476955562-13673-2-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 20/10/16 10:25, Peter Rosin wrote:
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> ---
I like this, but will need some device tree maintainer input on it.

To my mind we are explicitly describing hardware connectivity here so it should
be fine.  No where near as controversial as a certain other IIO binding ;)


>  .../devicetree/bindings/iio/dac/dpot-dac.txt       | 43 ++++++++++++++++++++++
>  MAINTAINERS                                        |  6 +++
>  2 files changed, 49 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> new file mode 100644
> index 000000000000..a381188676c3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> @@ -0,0 +1,43 @@
> +Bindings for DAC emulation using a digital potentiometer
> +
> +It is assumed the that the dpot is used as a voltage divider between the
> +current dpot wiper setting and the maximum resistance of the dpot. The
> +divided voltage is provided by a vref regulator.
> +
> +                  .------.
> +   .-----------.  |      |
> +   | Vref      |--'    .---.
> +   | regulator |--.    |   |
> +   '-----------'  |    | d |
> +                  |    | p |
> +                  |    | o |  wiper
> +                  |    | t |<---------+
> +                  |    |   |
> +                  |    '---'       dac output voltage
> +                  |      |
> +                  '------+------------+
> +
> +Required properties:
> +- compatible: Should be "dpot-dac"
> +- vref-supply: The regulator supplying the voltage divider.
> +- io-channels: Channel node of the dpot to be used for the voltage division.
> +- io-channel-names: Should be "dpot".
> +- dpot-dac,max-ohms: The maximum resistance of the dpot.
> +
> +Example:
> +
> +	&i2c {
> +		dpot: mcp4651-503@28 {
> +			compatible = "microchip,mcp4651-503";
> +			reg = <0x28>;
> +			#io-channel-cells = <1>;
> +		};
> +	};
> +
> +	dac {
> +		compatible = "dpot-dac";
> +		vref-supply = <&reg_3v3>;
> +		io-channels = <&dpot 0>;
> +		io-channel-names = "dpot";
> +		dpot-dac,max-ohms = <50000>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1cd38a7e0064..c68b72088945 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6111,6 +6111,12 @@ L:	linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>  S:	Maintained
>  F:	drivers/media/rc/iguanair.c
>  
> +IIO DIGITAL POTENTIOMETER DAC
> +M:	Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> +L:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> +
>  IIO SUBSYSTEM AND DRIVERS
>  M:	Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>  R:	Hartmut Knaack <knaack.h-Mmb7MZpHnFY@public.gmane.org>
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v4 2/2] Documentation: dt: iio: humidity: add hts221 sensor device binding
From: Jonathan Cameron @ 2016-10-22 14:37 UTC (permalink / raw)
  To: Rob Herring, Lorenzo Bianconi
  Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.bianconi-qxv4g6HH51o
In-Reply-To: <20161018140000.mecaaavnvveme6ba@rob-hp-laptop>

On 18/10/16 15:00, Rob Herring wrote:
> On Thu, Oct 13, 2016 at 10:06:05PM +0200, Lorenzo Bianconi wrote:
>> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
>> ---
>>  .../devicetree/bindings/iio/humidity/hts221.txt    | 22 ++++++++++++++++++++++
>>  1 file changed, 22 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/humidity/hts221.txt
> 
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> 
Applied. Thanks.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 2/4] iio: dpot-dac: DAC driver based on a digital potentiometer
From: Jonathan Cameron @ 2016-10-22 14:47 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1476955562-13673-3-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 20/10/16 10:26, Peter Rosin wrote:
> It is assumed the that the dpot is used as a voltage divider between the
> current dpot wiper setting and the maximum resistance of the dpot. The
> divided voltage is provided by a vref regulator.
> 
>                   .------.
>    .-----------.  |      |
>    | Vref      |--'    .---.
>    | regulator |--.    |   |
>    '-----------'  |    | d |
>                   |    | p |
>                   |    | o |  wiper
>                   |    | t |<---------+
>                   |    |   |
>                   |    '---'       dac output voltage
>                   |      |
>                   '------+------------+
The world needs more Asci art ;)
> 
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
I've added various comments, but not actual changes.  Peter covered the few
bits I noticed already.  Very nice.

J
> ---
>  MAINTAINERS                |   1 +
>  drivers/iio/dac/Kconfig    |  10 +++
>  drivers/iio/dac/Makefile   |   1 +
>  drivers/iio/dac/dpot-dac.c | 219 +++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 231 insertions(+)
>  create mode 100644 drivers/iio/dac/dpot-dac.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c68b72088945..8c8aae24b96b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6116,6 +6116,7 @@ M:	Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
>  L:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> +F:	drivers/iio/dac/dpot-dac.c
>  
>  IIO SUBSYSTEM AND DRIVERS
>  M:	Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> index 120b24478469..934d4138fcdb 100644
> --- a/drivers/iio/dac/Kconfig
> +++ b/drivers/iio/dac/Kconfig
> @@ -200,6 +200,16 @@ config AD8801
>  	  To compile this driver as a module choose M here: the module will be called
>  	  ad8801.
>  
> +config DPOT_DAC
> +	tristate "DAC emulation using a DPOT"
> +	depends on OF
> +	help
> +	  Say yes here to build support for DAC emulation using a digital
> +	  potentiometer.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called iio-dpot-dac.
> +
>  config LPC18XX_DAC
>  	tristate "NXP LPC18xx DAC driver"
>  	depends on ARCH_LPC18XX || COMPILE_TEST
> diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> index 27642bbf75f2..f01bf4a99867 100644
> --- a/drivers/iio/dac/Makefile
> +++ b/drivers/iio/dac/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_AD5686) += ad5686.o
>  obj-$(CONFIG_AD7303) += ad7303.o
>  obj-$(CONFIG_AD8801) += ad8801.o
>  obj-$(CONFIG_CIO_DAC) += cio-dac.o
> +obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
>  obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
>  obj-$(CONFIG_M62332) += m62332.o
>  obj-$(CONFIG_MAX517) += max517.o
> diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c
> new file mode 100644
> index 000000000000..94fbb4b27287
> --- /dev/null
> +++ b/drivers/iio/dac/dpot-dac.c
> @@ -0,0 +1,219 @@
> +/*
> + * IIO DAC emulation driver using a digital potentiometer
> + *
> + * Copyright (C) 2016 Axentia Technologies AB
> + *
> + * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> + *
> + * 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.
> + */
> +
> +/*
> + * It is assumed the that the dpot is used as a voltage divider between the
> + * current dpot wiper setting and the maximum resistance of the dpot. The
> + * divided voltage is provided by a vref regulator.
> + *
> + *                   .------.
> + *    .-----------.  |      |
> + *    | Vref      |--'    .---.
> + *    | regulator |--.    |   |
> + *    '-----------'  |    | d |
> + *                   |    | p |
> + *                   |    | o |  wiper
> + *                   |    | t |<---------+
> + *                   |    |   |
> + *                   |    '---'       dac output voltage
> + *                   |      |
> + *                   '------+------------+
> + */
> +
> +#include <linux/err.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +
> +struct dpot_dac {
> +	struct regulator *vref;
> +	struct iio_channel *dpot;
> +	u32 max_ohms;
> +};
> +
> +static const struct iio_chan_spec dpot_dac_iio_channel = {
> +	.type = IIO_VOLTAGE,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
> +			    | BIT(IIO_CHAN_INFO_SCALE),
> +	.output = 1,
> +};
> +
> +static int dpot_dac_read_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val, int *val2, long mask)
> +{
> +	struct dpot_dac *dac = iio_priv(indio_dev);
> +	int ret;
> +	unsigned long long tmp;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		return iio_read_channel_raw(dac->dpot, val);
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = iio_read_channel_scale(dac->dpot, val, val2);
> +		switch (ret) {
> +		case IIO_VAL_FRACTIONAL_LOG2:
> +			tmp = (s64)*val * 1000000000LL;
> +			do_div(tmp, dac->max_ohms);
> +			tmp *= regulator_get_voltage(dac->vref) / 1000;
> +			do_div(tmp, 1000000000LL);
> +			*val = tmp;
> +			return ret;
> +		case IIO_VAL_INT:
> +			*val2 = 1;
> +			ret = IIO_VAL_FRACTIONAL;
> +			break;
> +		}
> +		*val *= regulator_get_voltage(dac->vref) / 1000;
> +		*val2 *= dac->max_ohms;
> +
> +		return ret;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int dpot_dac_write_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int val, int val2, long mask)
> +{
> +	struct dpot_dac *dac = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		return iio_write_channel_raw(dac->dpot, val);
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info dpot_dac_info = {
> +	.read_raw = dpot_dac_read_raw,
> +	.write_raw = dpot_dac_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int dpot_dac_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct iio_dev *indio_dev;
> +	struct dpot_dac *dac;
> +	enum iio_chan_type type;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +	dac = iio_priv(indio_dev);
> +
> +	indio_dev->name = dev_name(dev);
> +	indio_dev->dev.parent = dev;
> +	indio_dev->info = &dpot_dac_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = &dpot_dac_iio_channel;
> +	indio_dev->num_channels = 1;
> +
> +	dac->vref = devm_regulator_get(dev, "vref");
> +	if (IS_ERR(dac->vref)) {
> +		if (PTR_ERR(dac->dpot) != -EPROBE_DEFER)
> +			dev_err(&pdev->dev, "failed to get vref regulator\n");
> +		return PTR_ERR(dac->vref);
> +	}
> +
> +	dac->dpot = devm_iio_channel_get(dev, "dpot");
> +	if (IS_ERR(dac->dpot)) {
> +		if (PTR_ERR(dac->dpot) != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get dpot input channel\n");
> +		return PTR_ERR(dac->dpot);
> +	}
> +
> +	switch (iio_read_channel_scale(dac->dpot, &ret, NULL)) {
> +	case IIO_VAL_INT:
> +	case IIO_VAL_FRACTIONAL:
> +	case IIO_VAL_FRACTIONAL_LOG2:
> +		break;
> +	default:
> +		dev_err(dev, "dpot has a scale that is too weird\n");
int plus micro is kind of the default, so should probably handle that
one as well if possible.  Can be added later when someone needs it though.
> +		return -EINVAL;
> +	}
> +
> +	ret = iio_get_channel_type(dac->dpot, &type);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (type != IIO_RESISTANCE) {
> +		dev_err(dev, "dpot is of the wrong type\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "dpot-dac,max-ohms",
> +				   &dac->max_ohms);
> +	if (ret) {
> +		dev_err(dev, "the dpot-dac,max-ohms property is missing\n");
> +		return ret;
> +	}
Now I know why you wanted that range ;)  Was wondering a bit when reading
your cover letter...  All makes sense now.
> +
> +	ret = regulator_enable(dac->vref);
> +	if (ret) {
> +		dev_err(dev, "failed to enable the vref regulator\n");
> +		return ret;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device\n");
> +		goto disable_reg;
> +	}
> +
> +	return 0;
> +
> +disable_reg:
> +	regulator_disable(dac->vref);
> +	return ret;
> +}
> +
> +static int dpot_dac_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct dpot_dac *dac = iio_priv(indio_dev);
> +
> +	iio_device_unregister(indio_dev);
> +	regulator_disable(dac->vref);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id dpot_dac_match[] = {
> +	{ .compatible = "dpot-dac" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, dpot_dac_match);
> +
> +static struct platform_driver dpot_dac_driver = {
> +	.probe = dpot_dac_probe,
> +	.remove = dpot_dac_remove,
> +	.driver = {
> +		.name = "iio-dpot-dac",
> +		.of_match_table = dpot_dac_match,
> +	},
> +};
> +module_platform_driver(dpot_dac_driver);
> +
> +MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 3/4] dt-bindings: iio: document envelope-detector bindings
From: Jonathan Cameron @ 2016-10-22 14:53 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1476955562-13673-4-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 20/10/16 10:26, Peter Rosin wrote:
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Definitely going to need device tree maintainer input!  (though as Lars
and you discussed some mods to this in the cover letter discussion,
perhaps they should wait for V2).
> ---
>  .../bindings/iio/adc/envelope-detector.txt         | 65 ++++++++++++++++++++++
>  MAINTAINERS                                        |  6 ++
>  2 files changed, 71 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> new file mode 100644
> index 000000000000..0e26299516fd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> @@ -0,0 +1,65 @@
> +Bindings for ADC envelope detector using a DAC and a comparator
> +
> +The DAC is used to find the peak level of an alternating voltage input
> +signal by a binary search using the output of a comparator wired to
> +an interrupt pin. Like so:
> +                          _
> +                         | \
> +    input +------>-------|+ \
> +                         |   \
> +           .-------.     |    }---.
> +           |       |     |   /    |
> +           |    dac|-->--|- /     |
> +           |       |     |_/      |
> +           |       |              |
> +           |       |              |
> +           |    irq|------<-------'
> +           |       |
> +           '-------'
> +
> +Required properties:
> +- compatible: Should be "envelope-detector"
Agreed with the discussion in the cover letter responses.  Needs
to be more specific as lots of ways of skinning this cat.
Hehe, just occurred to me that your Maintainer entry below
makes you responsible for all of them.. Acked!

> +- io-channels: Channel node of the dac to be used for comparator input.
> +- io-channel-names: Should be "dac".
> +- interrupt specification for one client interrupt,
> +  see ../../interrupt-controller/interrupts.txt for details.
> +- interrupt-names: Should be "comp".
> +- envelope-detector,dac-max: The maximum raw input value for the dac.
I guess the available stuff you have brought up to date should deal with this
one without needing another param.

> +- envelope-detector,comp-interval-ms: How long to wait for the comparator
> +  to trigger before moving on to another DAC level. This interval needs to
> +  relate to the lowest possible frequency of the above input signal.
This is an odd one and definitely a case for the device tree guys.

It's not generic enough to not have a 'manufacturer' in the param name,
but there isn't really a manufacturer as anyone with a few chips can build
one of these (using the 'wiring' diagram above ;)

So what should the prefix be?

> +
> +Optional properties:
> +- envelope-detector,inverted: If the input signal is centered around the
> +  dac-max voltage (instead of zero), this property causes the detector to
> +  search for the lowest DAC value that does not triggers the comparator
> +  (instead of the highest). The result is also inverted so that a lower DAC
> +  reading yields a higher voltage value.
As discussed, this is fundamental enough that it should probably
be based on the compatible string rather than a separate item.
> +
> +
> +Example:
> +
> +	&spi {
> +		dac: ad7303@4 {
> +			compatible = "adi,ad7303";
> +			reg = <4>;
> +			spi-max-frequency = <10000000>;
> +			Vdd-supply = <&vdd_supply>;
> +			adi,use-external-reference;
> +			REF-supply = <&vref_supply>;
> +			#io-channel-cells = <1>;
> +		};
> +	};
> +
> +	envelope-detector {
> +		compatible = "envelope-detector";
> +		io-channels = <&dac 0>;
> +		io-channel-names = "dac";
> +
> +		interrupt-parent = <&gpio>;
> +		interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
> +		interrupt-names = "comp";
> +
> +		envelope-detector,dac-max = <255>;
> +		envelope-detector,comp-interval-ms = <50>;
> +	};
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8c8aae24b96b..4b6f6ec1b703 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6118,6 +6118,12 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
>  F:	drivers/iio/dac/dpot-dac.c
>  
> +IIO ENVELOPE DETECTOR
> +M:	Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> +L:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> +
>  IIO SUBSYSTEM AND DRIVERS
>  M:	Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>  R:	Hartmut Knaack <knaack.h-Mmb7MZpHnFY@public.gmane.org>
> 

^ permalink raw reply

* [PATCH 1/1] arm: dts: imx6ul: 14x14-evk: add the optee node
From: Jason Liu @ 2016-10-22 14:57 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: devicetree, shawnguo, linux-kernel

This patch adds the optee node for the i.MX6UL 14x14 evk board

Signed-off-by: Jason Liu <jason.hui.liu@nxp.com>
---
 arch/arm/boot/dts/imx6ul-14x14-evk.dts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dts b/arch/arm/boot/dts/imx6ul-14x14-evk.dts
index e281d50..e68dc19 100644
--- a/arch/arm/boot/dts/imx6ul-14x14-evk.dts
+++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dts
@@ -30,6 +30,13 @@
 		status = "okay";
 	};
 
+	firmware {
+		optee {
+			compatible = "linaro,optee-tz";
+			method = "smc";
+		};
+	};
+
 	regulators {
 		compatible = "simple-bus";
 		#address-cells = <1>;
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH 4/4] iio: envelope-detector: ADC driver based on a DAC and a comparator
From: Jonathan Cameron @ 2016-10-22 15:03 UTC (permalink / raw)
  To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1476955562-13673-5-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 20/10/16 10:26, Peter Rosin wrote:
> The DAC is used to find the peak level of an alternating voltage input
> signal by a binary search using the output of a comparator wired to
> an interrupt pin. Like so:
>                           _
>                          | \
>     input +------>-------|+ \
>                          |   \
>            .-------.     |    }---.
>            |       |     |   /    |
>            |    dac|-->--|- /     |
>            |       |     |_/      |
>            |       |              |
>            |       |              |
>            |    irq|------<-------'
>            |       |
>            '-------'
> 
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Some really trivial bits inline that might have made it into your V2.

Jonathan
> ---
>  MAINTAINERS                         |   1 +
>  drivers/iio/adc/Kconfig             |  10 ++
>  drivers/iio/adc/Makefile            |   1 +
>  drivers/iio/adc/envelope-detector.c | 305 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 317 insertions(+)
>  create mode 100644 drivers/iio/adc/envelope-detector.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 4b6f6ec1b703..d9a58b5f2b6f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6123,6 +6123,7 @@ M:	Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
>  L:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> +F:	drivers/iio/adc/envelope-detector.c
>  
>  IIO SUBSYSTEM AND DRIVERS
>  M:	Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 7edcf3238620..d5c4a95855c2 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -195,6 +195,16 @@ config DA9150_GPADC
>  	  To compile this driver as a module, choose M here: the module will be
>  	  called berlin2-adc.
>  
> +config ENVELOPE_DETECTOR
> +	tristate "Envelope detector using a DAC and a comparator"
> +	depends on OF
> +	help
> +	  Say yes here to build support for an envelope detector using a DAC
> +	  and a comparator.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called iio-envelope-detector.
> +
>  config EXYNOS_ADC
>  	tristate "Exynos ADC driver support"
>  	depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04c311f..0d773c6a0578 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
>  obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> +obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>  obj-$(CONFIG_HI8435) += hi8435.o
> diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
> new file mode 100644
> index 000000000000..837646107beb
> --- /dev/null
> +++ b/drivers/iio/adc/envelope-detector.c
> @@ -0,0 +1,305 @@
> +/*
> + * Driver for an envelope detector using a DAC and a comparator
> + *
> + * Copyright (C) 2016 Axentia Technologies AB
> + *
> + * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> + *
> + * 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.
> + */
> +
> +/*
> + * The DAC is used to find the peak level of an alternating voltage input
> + * signal by a binary search using the output of a comparator wired to
> + * an interrupt pin. Like so:
> + *                           _
> + *                          | \
> + *     input +------>-------|+ \
> + *                          |   \
> + *            .-------.     |    }---.
> + *            |       |     |   /    |
> + *            |    dac|-->--|- /     |
> + *            |       |     |_/      |
> + *            |       |              |
> + *            |       |              |
> + *            |    irq|------<-------'
> + *            |       |
> + *            '-------'
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/interrupt.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/workqueue.h>
> +
> +struct envelope {
> +	struct iio_channel *dac;
> +	struct delayed_work comp_timeout;
> +	int comp_irq;
> +
> +	spinlock_t comp_lock; /* protects comp */
> +	int comp;
> +
> +	struct mutex read_lock; /* protects everything below */
> +
> +	u32 dac_max;
> +	u32 comp_interval;
> +	bool invert;
> +
> +	int high;
> +	int level;
> +	int low;
> +
> +	struct completion done;
> +};
> +
Perhaps a comment explaining what this function is doing..
The naming isn't 100% clear...
> +static int envelope_detector_latch(struct envelope *env)
> +{
> +	int comp;
> +
> +	spin_lock_irq(&env->comp_lock);
> +	comp = env->comp;
> +	env->comp = 0;
> +	spin_unlock_irq(&env->comp_lock);
> +
> +	if (comp)
> +		enable_irq(env->comp_irq);
> +
> +	return comp;
> +}
> +
> +static irqreturn_t envelope_detector_isr(int irq, void *ctx)
> +{
> +	struct envelope *env = ctx;
> +
> +	spin_lock(&env->comp_lock);
> +	env->comp = 1;
> +	disable_irq_nosync(env->comp_irq);
> +	spin_unlock(&env->comp_lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void envelope_detector_setup_compare(struct envelope *env)
> +{
> +	int ret;
> +
> +	/*
> +	 * Do a binary search for the peak input level, and stop
> +	 * when that level is "trapped" between two adjacent DAC
> +	 * values.
> +	 * When invert is active, use the midpoint floor so that
> +	 * env->level ends up as env->low when the termination
> +	 * criteria below is fulfilled, and use the midpoint
> +	 * ceiling when invert is not active so that env->level
> +	 * ends up as env->high in that case.
> +	 */
> +	env->level = (env->high + env->low + !env->invert) / 2;
> +
> +	if (env->high == env->low + 1) {
> +		complete(&env->done);
> +		return;
> +	}
> +
> +	/* Set a "safe" DAC level (if there is such a thing)... */
> +	ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max);
> +	if (ret < 0)
> +		goto err;
> +
> +	/* ...clear the comparison result... */
> +	envelope_detector_latch(env);
> +
> +	/* ...set the real DAC level... */
> +	ret = iio_write_channel_raw(env->dac, env->level);
> +	if (ret < 0)
> +		goto err;
> +
> +	/* ...and wait for a bit to see if the latch catches anything. */
> +	schedule_delayed_work(&env->comp_timeout,
> +			      msecs_to_jiffies(env->comp_interval));
> +	return;
> +
> +err:
> +	env->level = ret;
> +	complete(&env->done);
> +}
> +
> +static void envelope_detector_timeout(struct work_struct *work)
> +{
> +	struct envelope *env = container_of(work, struct envelope,
> +					    comp_timeout.work);
> +
> +	/* Adjust low/high depending on the latch content... */
> +	if (!envelope_detector_latch(env) ^ !env->invert)
> +		env->low = env->level;
> +	else
> +		env->high = env->level;
> +
> +	/* ...and continue the search. */
> +	envelope_detector_setup_compare(env);
> +}
> +
> +static int envelope_detector_read_raw(struct iio_dev *indio_dev,
> +				      struct iio_chan_spec const *chan,
> +				      int *val, int *val2, long mask)
> +{
> +	struct envelope *env = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		/*
> +		 * When invert is active, start with high=max+1 and low=0
> +		 * since we will end up with the low value when the
> +		 * termination criteria is fulfilled (rounding down). And
> +		 * start with high=max and low=-1 when invert is not active
> +		 * since we will end up with the high value in that case.
> +		 * This ensures that we in both cases return a value in the
> +		 * same range as the DAC and that as not triggered the
has

Impressive level of detail. I'd probably have missed the need for this!
> +		 * comparator.
> +		 */
> +		mutex_lock(&env->read_lock);
> +		env->high = env->dac_max + 1 - !env->invert;
> +		env->low = 0 - !env->invert;
> +		envelope_detector_setup_compare(env);
> +		wait_for_completion(&env->done);
> +		if (env->level < 0) {
> +			ret = env->level;
> +			goto err_unlock;
> +		}
> +		*val = env->invert ? env->dac_max - env->level : env->level;
> +		mutex_unlock(&env->read_lock);
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		return iio_read_channel_scale(env->dac, val, val2);
> +	}
> +
> +	return -EINVAL;
> +
> +err_unlock:
> +	mutex_unlock(&env->read_lock);
> +	return ret;
> +}
> +
> +static const struct iio_chan_spec envelope_detector_iio_channel = {
> +	.type = IIO_ALTVOLTAGE,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
> +			    | BIT(IIO_CHAN_INFO_SCALE),
> +	.output = 1,
> +};
> +
> +static const struct iio_info envelope_detector_info = {
> +	.read_raw = &envelope_detector_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int envelope_detector_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct iio_dev *indio_dev;
> +	struct envelope *env;
> +	enum iio_chan_type type;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*env));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +	env = iio_priv(indio_dev);
> +
> +	spin_lock_init(&env->comp_lock);
> +	mutex_init(&env->read_lock);
> +	init_completion(&env->done);
> +	INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout);
> +
> +	indio_dev->name = dev_name(dev);
> +	indio_dev->dev.parent = dev;
> +	indio_dev->dev.of_node = dev->of_node;
> +	indio_dev->info = &envelope_detector_info;
> +	indio_dev->channels = &envelope_detector_iio_channel;
> +	indio_dev->num_channels = 1;
> +
> +	env->dac = devm_iio_channel_get(dev, "dac");
> +	if (IS_ERR(env->dac)) {
> +		if (PTR_ERR(env->dac) != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get dac input channel\n");
> +		return PTR_ERR(env->dac);
> +	}
> +
> +	env->comp_irq = platform_get_irq_byname(pdev, "comp");
> +	if (env->comp_irq < 0) {
> +		if (env->comp_irq != -EPROBE_DEFER)
> +			dev_err(dev, "failed to get compare interrupt\n");
> +		return env->comp_irq;
> +	}
> +
> +	ret = devm_request_irq(dev, env->comp_irq, envelope_detector_isr, 0,
> +			       "env-env-dac-comp", env);
> +	if (ret) {
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "failed to request interrupt\n");
> +		return ret;
> +	}
> +
> +	ret = iio_get_channel_type(env->dac, &type);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (type != IIO_VOLTAGE) {
> +		dev_err(dev, "dac is of the wrong type\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "envelope-detector,dac-max",
> +				   &env->dac_max);
> +	if (ret) {
> +		dev_err(dev, "the dac-max property is missing\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node,
> +				   "envelope-detector,comp-interval-ms",
> +				   &env->comp_interval);
> +	if (ret) {
> +		dev_err(dev, "the comp-interval-ms property is missing\n");
> +		return ret;
> +	}
> +
> +	env->invert = of_property_read_bool(dev->of_node,
> +					    "envelope-detector,inverted");
> +
> +	return devm_iio_device_register(dev, indio_dev);
> +}
> +
> +static const struct of_device_id envelope_detector_match[] = {
> +	{ .compatible = "envelope-detector" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, envelope_detector_match);
> +
> +static struct platform_driver envelope_detector_driver = {
> +	.probe = envelope_detector_probe,
> +	.driver = {
> +		.name = "iio-envelope-detector",
> +		.of_match_table = envelope_detector_match,
> +	},
> +};
> +module_platform_driver(envelope_detector_driver);
> +
> +MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 1/3] mtd: s3c2410: make ecc mode configurable via platform data
From: Krzysztof Kozlowski @ 2016-10-22 15:32 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Krzysztof Kozlowski, Sergio Prado, kgene, javier, linux, linux,
	richard, dwmw2, computersforpeace, linux-arm-kernel,
	linux-samsung-soc, linux-kernel, linux-mtd, robh+dt, mark.rutland,
	devicetree
In-Reply-To: <20161022142907.08a3eedf@bbrezillon>

On Sat, Oct 22, 2016 at 02:29:07PM +0200, Boris Brezillon wrote:
> Hi Krzysztof,
> 
> On Fri, 21 Oct 2016 21:27:10 +0300
> Krzysztof Kozlowski <krzk@kernel.org> wrote:
> 
> > On Thu, Oct 20, 2016 at 07:42:44PM -0200, Sergio Prado wrote:
> > > Removing CONFIG_MTD_NAND_S3C2410_HWECC option and adding a ecc_mode
> > > field in the drivers's platform data structure so it can be selectable
> > > via platform data.
> > > 
> > > Also setting this field to NAND_ECC_SOFT in all boards using this
> > > driver since none of them had CONFIG_MTD_NAND_S3C2410_HWECC enabled.
> > > 
> > > Signed-off-by: Sergio Prado <sergio.prado@e-labworks.com>
> > > ---
> > >  arch/arm/mach-s3c24xx/common-smdk.c            |   1 +
> > >  arch/arm/mach-s3c24xx/mach-anubis.c            |   1 +
> > >  arch/arm/mach-s3c24xx/mach-at2440evb.c         |   1 +
> > >  arch/arm/mach-s3c24xx/mach-bast.c              |   1 +
> > >  arch/arm/mach-s3c24xx/mach-gta02.c             |   1 +
> > >  arch/arm/mach-s3c24xx/mach-jive.c              |   1 +
> > >  arch/arm/mach-s3c24xx/mach-mini2440.c          |   1 +
> > >  arch/arm/mach-s3c24xx/mach-osiris.c            |   1 +
> > >  arch/arm/mach-s3c24xx/mach-qt2410.c            |   1 +
> > >  arch/arm/mach-s3c24xx/mach-rx1950.c            |   1 +
> > >  arch/arm/mach-s3c24xx/mach-rx3715.c            |   1 +
> > >  arch/arm/mach-s3c24xx/mach-vstms.c             |   1 +
> > >  arch/arm/mach-s3c64xx/mach-hmt.c               |   1 +
> > >  arch/arm/mach-s3c64xx/mach-mini6410.c          |   1 +
> > >  arch/arm/mach-s3c64xx/mach-real6410.c          |   1 +
> > >  drivers/mtd/nand/Kconfig                       |   9 --
> > >  drivers/mtd/nand/s3c2410.c                     | 119 +++++++++++++------------
> > >  include/linux/platform_data/mtd-nand-s3c2410.h |   6 +-
> > >  18 files changed, 79 insertions(+), 70 deletions(-)
> > >  
> > 
> > I acked this twice (v1 and v2)... and still you are ignoring them. I am
> > sorry, I am not gonna to ack this third time!
> > 
> > For v2 I acked also other patches but it it is not there as well...
> 
> I'll collect your acks (and Rob ones) when applying the patches. BTW,
> how should I proceed with patch 1? Do you want an topic branch
> containing this patch?

I don't expect much work around these files so the risk of conflicts is
rather small. You could put it on a topic branch just in case (so I
could pull in if needed) but I am fine with applying these to your tree
as is.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH 1/1] arm: dts: imx6ul: 14x14-evk: add the optee node
From: Fabio Estevam @ 2016-10-22 17:24 UTC (permalink / raw)
  To: Jason Liu
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Shawn Guo,
	linux-kernel
In-Reply-To: <1477148229-6940-1-git-send-email-jason.hui.liu-3arQi8VN3Tc@public.gmane.org>

Hi Jason,

On Sat, Oct 22, 2016 at 12:57 PM, Jason Liu <jason.hui.liu-3arQi8VN3Tc@public.gmane.org> wrote:
> This patch adds the optee node for the i.MX6UL 14x14 evk board
>
> Signed-off-by: Jason Liu <jason.hui.liu-3arQi8VN3Tc@public.gmane.org>
> ---
>  arch/arm/boot/dts/imx6ul-14x14-evk.dts | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dts b/arch/arm/boot/dts/imx6ul-14x14-evk.dts
> index e281d50..e68dc19 100644
> --- a/arch/arm/boot/dts/imx6ul-14x14-evk.dts
> +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dts
> @@ -30,6 +30,13 @@
>                 status = "okay";
>         };
>
> +       firmware {
> +               optee {
> +                       compatible = "linaro,optee-tz";
> +                       method = "smc";

It seems we need to wait until the property lands in mainline.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support
From: Jonathan Cameron @ 2016-10-22 17:47 UTC (permalink / raw)
  To: Mugunthan V N, linux-iio-u79uwXL29TY76Z2rM5mHXA
  Cc: Tony Lindgren, Rob Herring, Mark Rutland, Russell King,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Lee Jones, Vignesh R, Andrew F . Davis,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sekhar Nori, Peter Ujfalusi,
	John Syne
In-Reply-To: <d7dd9a21-51d9-e8f5-a6fe-34653afc9240-l0cyMroinI0@public.gmane.org>

On 19/10/16 09:52, Mugunthan V N wrote:
> On Sunday 09 October 2016 02:00 PM, Jonathan Cameron wrote:
>> On 05/10/16 10:04, Mugunthan V N wrote:
>>> This patch adds the required pieces to ti_am335x_adc driver for
>>> DMA support
>>>
>>> Signed-off-by: Mugunthan V N <mugunthanvnm-l0cyMroinI0@public.gmane.org>
>> Hi,
>>
>> Just the one question inline.  I'll also need an Ack from Lee as
>> this touches code in mfd (as does the previous patch).
>> We have obviously missed this merge window, so no particular rush.
Question answered below.  So just need that Ack from Lee.


>>
>> Otherwise, looking very nice indeed.  Got to get my BBB out and play
>> with this now ;)
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/ti_am335x_adc.c      | 148 ++++++++++++++++++++++++++++++++++-
>>>  include/linux/mfd/ti_am335x_tscadc.h |   7 ++
>>>  2 files changed, 152 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
>>> index c3cfacca..ad9dec3 100644
>>> --- a/drivers/iio/adc/ti_am335x_adc.c
>>> +++ b/drivers/iio/adc/ti_am335x_adc.c
>>> @@ -30,10 +30,28 @@
>>>  #include <linux/iio/buffer.h>
>>>  #include <linux/iio/kfifo_buf.h>
>>>  
>>> +#include <linux/dmaengine.h>
>>> +#include <linux/dma-mapping.h>
>>> +
>>> +#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;
>>> +	}
>> Hmm. Another case for the mooted iio_push_to_buffers_multi. Guess
>> we should move on with that one ;)
>>> +}
>>> +
>>> +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;
>> This changes the non dma behaviour as we no longer set IRQENB_FIFO1THRES.
>> Why?  Was it wrong before?
> 
> In non DMA case, dms->chan will be NULL and IRQENB_FIFO1THRES interrupt
> will be enabled to pop the ADC samples.
> 
oops.  Thanks for pointing out how I'd misread this!
So just after the ack from Lee now.
Jonathan
> Regards
> Mugunthan V N
> 
>>> +	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 e45a208..b9a53e0 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
>>>
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [PATCH v3 1/5] iio: dac: mcp4725: use regulator framework
From: Jonathan Cameron @ 2016-10-22 18:01 UTC (permalink / raw)
  To: Tomas Novotny, devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Mark Rutland, Akinobu Mita, Yong Li,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Tomas Novotny
In-Reply-To: <1476812589-18333-2-git-send-email-tomas.novotny-MzfF5RY65ECbtNqbA8UwFdBPR1lH4CV8@public.gmane.org>

On 18/10/16 18:43, Tomas Novotny wrote:
> From: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
> 
> 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 <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
I've already applied this from V2.

Thanks,

Jonathan
> ---
>  drivers/iio/dac/mcp4725.c       | 45 ++++++++++++++++++++++++++++++++---------
>  include/linux/iio/dac/mcp4725.h |  1 -
>  2 files changed, 36 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
> index cca935c..1f55d1b 100644
> --- a/drivers/iio/dac/mcp4725.c
> +++ b/drivers/iio/dac/mcp4725.c
> @@ -18,6 +18,7 @@
>  #include <linux/i2c.h>
>  #include <linux/err.h>
>  #include <linux/delay.h>
> +#include <linux/regulator/consumer.h>
>  
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
> @@ -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,38 @@ 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 91530e6..7c062e8 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_ */
> 

^ permalink raw reply

* Re: [PATCH v3 2/5] iio: dac: mcp4725: fix incorrect comment
From: Jonathan Cameron @ 2016-10-22 18:02 UTC (permalink / raw)
  To: Tomas Novotny, devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Mark Rutland, Akinobu Mita, Yong Li,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Tomas Novotny
In-Reply-To: <1476812589-18333-3-git-send-email-tomas.novotny-MzfF5RY65ECbtNqbA8UwFdBPR1lH4CV8@public.gmane.org>

On 18/10/16 18:43, Tomas Novotny wrote:
> From: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
> 
> Number 2 is referencing to the settings with the largest available
> resistor.
> 
> No functional change.
> 
> Signed-off-by: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
Thanks,

Applied to the togreg branch of iio.git and pushed out as testing.

Jonathan
> ---
>  drivers/iio/dac/mcp4725.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
> index 1f55d1b..e481f38 100644
> --- a/drivers/iio/dac/mcp4725.c
> +++ b/drivers/iio/dac/mcp4725.c
> @@ -374,7 +374,7 @@ static int mcp4725_probe(struct i2c_client *client,
>  	}
>  	pd = (inbuf[0] >> 1) & 0x3;
>  	data->powerdown = pd > 0 ? true : false;
> -	data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
> +	data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
>  	data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
>  
>  	err = iio_device_register(indio_dev);
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 3/5] Documentation: dt: iio: add mcp4725/6 dac device binding
From: Jonathan Cameron @ 2016-10-22 18:02 UTC (permalink / raw)
  To: Tomas Novotny, devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Mark Rutland, Akinobu Mita, Yong Li,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Tomas Novotny
In-Reply-To: <1476812589-18333-4-git-send-email-tomas.novotny-MzfF5RY65ECbtNqbA8UwFdBPR1lH4CV8@public.gmane.org>

On 18/10/16 18:43, Tomas Novotny wrote:
> From: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
> 
> Signed-off-by: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Applied. Thanks,

Jonathan
> ---
>  .../devicetree/bindings/iio/dac/mcp4725.txt        | 35 ++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/dac/mcp4725.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/dac/mcp4725.txt b/Documentation/devicetree/bindings/iio/dac/mcp4725.txt
> new file mode 100644
> index 0000000..1bc6c09
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/mcp4725.txt
> @@ -0,0 +1,35 @@
> +Microchip mcp4725 and mcp4726 DAC device driver
> +
> +Required properties:
> +	- compatible: Must be "microchip,mcp4725" or "microchip,mcp4726"
> +	- reg: Should contain the DAC I2C address
> +	- vdd-supply: Phandle to the Vdd power supply. This supply is used as a
> +	  voltage reference on mcp4725. It is used as a voltage reference on
> +	  mcp4726 if there is no vref-supply specified.
> +
> +Optional properties (valid only for mcp4726):
> +	- vref-supply: Optional phandle to the Vref power supply. Vref pin is
> +	  used as a voltage reference when this supply is specified.
> +	- microchip,vref-buffered: Boolean to enable buffering of the external
> +	  Vref pin. This boolean is not valid without the vref-supply. Quoting
> +	  the datasheet: This is offered in cases where the reference voltage
> +	  does not have the current capability not to drop its voltage when
> +	  connected to the internal resistor ladder circuit.
> +
> +Examples:
> +
> +	/* simple mcp4725 */
> +	mcp4725@60 {
> +		compatible = "microchip,mcp4725";
> +		reg = <0x60>;
> +		vdd-supply = <&vdac_vdd>;
> +	};
> +
> +	/* mcp4726 with the buffered external reference voltage */
> +	mcp4726@60 {
> +		compatible = "microchip,mcp4726";
> +		reg = <0x60>;
> +		vdd-supply = <&vdac_vdd>;
> +		vref-supply = <&vdac_vref>;
> +		microchip,vref-buffered;
> +	};
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 4/5] iio: dac: mcp4725: support voltage reference selection
From: Jonathan Cameron @ 2016-10-22 18:18 UTC (permalink / raw)
  To: Tomas Novotny, devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Mark Rutland, Akinobu Mita, Yong Li,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Tomas Novotny
In-Reply-To: <1476812589-18333-5-git-send-email-tomas.novotny-MzfF5RY65ECbtNqbA8UwFdBPR1lH4CV8@public.gmane.org>

On 18/10/16 18:43, Tomas Novotny wrote:
> From: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
> 
> 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 <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
Something odd happened and this patch wouldn't apply without some manual
intervention.

Please check the result in the testing branch of iio.git.

Thanks,

Jonathan
> ---
>  drivers/iio/dac/mcp4725.c       | 97 ++++++++++++++++++++++++++++++++++++++---
>  include/linux/iio/dac/mcp4725.h | 11 +++++
>  2 files changed, 102 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
> index e481f38..83d431f 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);
>  
> @@ -396,6 +479,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 7c062e8..628b2cf 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_ */
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 5/5] iio: dac: mcp4725: add devicetree support
From: Jonathan Cameron @ 2016-10-22 18:26 UTC (permalink / raw)
  To: Tomas Novotny, devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Mark Rutland, Akinobu Mita, Yong Li,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Tomas Novotny
In-Reply-To: <1476812589-18333-6-git-send-email-tomas.novotny-MzfF5RY65ECbtNqbA8UwFdBPR1lH4CV8@public.gmane.org>

On 18/10/16 18:43, Tomas Novotny wrote:
> From: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
> 
> Signed-off-by: Tomas Novotny <tomas-P46umIhNmdHrBKCeMvbIDA@public.gmane.org>
Applied to the togreg branch of iio.git. Initially pushed out as
testing for the autobuilders to play with it.

thanks,

Jonathan
> ---
>  drivers/iio/dac/mcp4725.c | 43 +++++++++++++++++++++++++++++++++++++------
>  1 file changed, 37 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
> index 83d431f..05f7f4d 100644
> --- a/drivers/iio/dac/mcp4725.c
> +++ b/drivers/iio/dac/mcp4725.c
> @@ -19,6 +19,7 @@
>  #include <linux/err.h>
>  #include <linux/delay.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/of.h>
>  
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
> @@ -364,22 +365,41 @@ static const struct iio_info mcp4725_info = {
>  	.driver_module = THIS_MODULE,
>  };
>  
> +#ifdef CONFIG_OF
> +static int mcp4725_probe_dt(struct device *dev,
> +			    struct mcp4725_platform_data *pdata)
> +{
> +	struct device_node *np = dev->of_node;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	/* check if is the vref-supply defined */
> +	pdata->use_vref = of_property_read_bool(np, "vref-supply");
> +	pdata->vref_buffered =
> +		of_property_read_bool(np, "microchip,vref-buffered");
> +
> +	return 0;
> +}
> +#else
> +static int mcp4725_probe_dt(struct device *dev,
> +			    struct mcp4725_platform_data *platform_data)
> +{
> +	return -ENODEV;
> +}
> +#endif
> +
>  static int mcp4725_probe(struct i2c_client *client,
>  			 const struct i2c_device_id *id)
>  {
>  	struct mcp4725_data *data;
>  	struct iio_dev *indio_dev;
> -	struct mcp4725_platform_data *pdata = dev_get_platdata(&client->dev);
> +	struct mcp4725_platform_data *pdata, pdata_dt;
>  	u8 inbuf[4];
>  	u8 pd;
>  	u8 ref;
>  	int err;
>  
> -	if (!pdata) {
> -		dev_err(&client->dev, "invalid platform data");
> -		return -EINVAL;
> -	}
> -
>  	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
>  	if (indio_dev == NULL)
>  		return -ENOMEM;
> @@ -387,6 +407,17 @@ static int mcp4725_probe(struct i2c_client *client,
>  	i2c_set_clientdata(client, indio_dev);
>  	data->client = client;
>  	data->id = id->driver_data;
> +	pdata = dev_get_platdata(&client->dev);
> +
> +	if (!pdata) {
> +		err = mcp4725_probe_dt(&client->dev, &pdata_dt);
> +		if (err) {
> +			dev_err(&client->dev,
> +				"invalid platform or devicetree data");
> +			return err;
> +		}
> +		pdata = &pdata_dt;
> +	}
>  
>  	if (data->id == MCP4725 && pdata->use_vref) {
>  		dev_err(&client->dev,
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v4 4/8] drivers:input:tsc2007: add iio interface to read external ADC input and temperature
From: Jonathan Cameron @ 2016-10-22 18:33 UTC (permalink / raw)
  To: H. Nikolaus Schaller, Dmitry Torokhov, Rob Herring, Mark Rutland,
	Benoît Cousson, Tony Lindgren, Russell King, Arnd Bergmann,
	Michael Welling, Mika Penttilä, Javier Martinez Canillas,
	Igor Grinberg, Sebastian Reichel, Andrew F. Davis, Mark Brown
  Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	letux-kernel-S0jZdbWzriLCfDggNXIi3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	kernel-Jl6IXVxNIMRxAtABVqVhTwC/G2K4zDHf
In-Reply-To: <4d3c600bf0a6b75a74212a103bf31525505290a0.1476712675.git.hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>

On 17/10/16 14:57, H. Nikolaus Schaller wrote:
> The tsc2007 chip not only has a resistive touch screen controller but
> also an external AUX adc imput which can be used for an ambient
> light sensor, battery voltage monitoring or any general purpose.
> 
> Additionally it can measure the chip temperature.
> 
> This extension provides an iio interface for these adc channels.
> 
> Since it is not wasting much resources and is very straightforward,
> we simply provide all other adc channels as optional iio interfaces
> as weel. This can be used for debugging or special applications.
well
> 
> Signed-off-by: H. Nikolaus Schaller <hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
This could be cleaner done perhaps by factoring out the IIO stuff into a separate
file and using a header with stubs to deal with the no available case.

There will only be a handful of stubs and it'll give you a lot cleaner code
in here.

If def fun in .c files is always harder to deal with than in a header
where stubs are really obvious.

J
> ---
>  drivers/input/touchscreen/tsc2007.c | 151 +++++++++++++++++++++++++++++++++++-
>  1 file changed, 150 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
> index c314331..b4a9504 100644
> --- a/drivers/input/touchscreen/tsc2007.c
> +++ b/drivers/input/touchscreen/tsc2007.c
> @@ -30,6 +30,7 @@
>  #include <linux/of.h>
>  #include <linux/of_gpio.h>
>  #include <linux/input/touchscreen.h>
> +#include <linux/iio/iio.h>
>  
>  #define TSC2007_MEASURE_TEMP0		(0x0 << 4)
>  #define TSC2007_MEASURE_AUX		(0x2 << 4)
> @@ -61,6 +62,16 @@
>  #define READ_X		(ADC_ON_12BIT | TSC2007_MEASURE_X)
>  #define PWRDOWN		(TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
>  
> +#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
> +{ \
> +	.datasheet_name = _name, \
> +	.type = _type, \
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\
> +			BIT(_chan_info), \
> +	.indexed = 1, \
> +	.channel = _chan, \
> +}
> +
>  struct ts_event {
>  	u16	x;
>  	u16	y;
> @@ -69,9 +80,13 @@ struct ts_event {
>  
>  struct tsc2007 {
>  	struct input_dev	*input;
> +#ifdef CONFIG_IIO
> +	struct iio_dev		*indio;
> +#endif
>  	char			phys[32];
>  
>  	struct i2c_client	*client;
> +	struct mutex		mlock;
>  
>  	u16			model;
>  	u16			x_plate_ohms;
> @@ -192,7 +207,10 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
>  	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
>  
>  		/* pen is down, continue with the measurement */
> +
> +		mutex_lock(&ts->mlock);
>  		tsc2007_read_values(ts, &tc);
> +		mutex_unlock(&ts->mlock);
>  
>  		rt = tsc2007_calculate_resistance(ts, &tc);
>  
> @@ -319,6 +337,89 @@ static void tsc2007_close(struct input_dev *input_dev)
>  	tsc2007_stop(ts);
>  }
>  
> +#ifdef CONFIG_IIO
> +
> +static const struct iio_chan_spec tsc2007_iio_channel[] = {
> +	TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> +	TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> +	TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> +	TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> +	TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> +	TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
> +	TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
> +	TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
> +	TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
> +};
> +
> +static int tsc2007_read_raw(struct iio_dev *indio_dev,
> +	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
> +{
> +	struct  tsc2007 *tsc = iio_priv(indio_dev);
> +	int adc_chan = chan->channel;
> +	int ret = 0;
> +
> +	if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
> +		return -EINVAL;
> +
> +	if (mask != IIO_CHAN_INFO_RAW)
> +		return -EINVAL;
> +
> +	mutex_lock(&tsc->mlock);
> +
> +	switch (chan->channel) {
> +	case 0:
> +		*val = tsc2007_xfer(tsc, READ_X);
> +		break;
> +	case 1:
> +		*val = tsc2007_xfer(tsc, READ_Y);
> +		break;
> +	case 2:
> +		*val = tsc2007_xfer(tsc, READ_Z1);
> +		break;
> +	case 3:
> +		*val = tsc2007_xfer(tsc, READ_Z2);
> +		break;
> +	case 4:
> +		*val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
> +		break;
> +	case 5: {
> +		struct ts_event tc;
> +
> +		tc.x = tsc2007_xfer(tsc, READ_X);
> +		tc.z1 = tsc2007_xfer(tsc, READ_Z1);
> +		tc.z2 = tsc2007_xfer(tsc, READ_Z2);
> +		*val = tsc2007_calculate_resistance(tsc, &tc);
> +		break;
> +	}
> +	case 6:
> +		*val = tsc2007_is_pen_down(tsc);
> +		break;
> +	case 7:
> +		*val = tsc2007_xfer(tsc,
> +				    (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
> +		break;
> +	case 8:
> +		*val = tsc2007_xfer(tsc,
> +				    (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
> +		break;
> +	}
> +
> +	/* Prepare for next touch reading - power down ADC, enable PENIRQ */
> +	tsc2007_xfer(tsc, PWRDOWN);
> +
> +	mutex_unlock(&tsc->mlock);
> +
> +	ret = IIO_VAL_INT;
> +
> +	return ret;
> +}
> +
> +static const struct iio_info tsc2007_iio_info = {
> +	.read_raw = tsc2007_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +#endif
> +
>  #ifdef CONFIG_OF
>  static int tsc2007_get_pendown_state_gpio(struct device *dev)
>  {
> @@ -453,26 +554,56 @@ static int tsc2007_probe(struct i2c_client *client,
>  	const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
>  	struct tsc2007 *ts;
>  	struct input_dev *input_dev;
> +#ifdef CONFIG_IIO
> +	struct iio_dev *indio_dev;
> +#endif
>  	int err;
>  
>  	if (!i2c_check_functionality(client->adapter,
>  				     I2C_FUNC_SMBUS_READ_WORD_DATA))
>  		return -EIO;
>  
> +#ifdef CONFIG_IIO
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ts));
> +	if (!indio_dev) {
> +		dev_err(&client->dev, "iio_device_alloc failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	ts = iio_priv(indio_dev);
> +#else
>  	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
>  	if (!ts)
>  		return -ENOMEM;
> -
> +#endif
>  	input_dev = devm_input_allocate_device(&client->dev);
>  	if (!input_dev)
>  		return -ENOMEM;
>  
>  	i2c_set_clientdata(client, ts);
>  
> +#ifdef CONFIG_IIO
> +	indio_dev->name = "tsc2007";
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->info = &tsc2007_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = tsc2007_iio_channel;
> +	indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
> +
> +	err = iio_device_register(indio_dev);
> +	if (err < 0) {
> +		dev_err(&client->dev, "iio_device_register() failed: %d\n",
> +			err);
> +		return err;
> +	}
> +	ts->indio = indio_dev;
> +#endif
> +
>  	ts->client = client;
>  	ts->irq = client->irq;
>  	ts->input = input_dev;
>  	init_waitqueue_head(&ts->wait);
> +	mutex_init(&ts->mlock);
>  
>  	snprintf(ts->phys, sizeof(ts->phys),
>  		 "%s/input0", dev_name(&client->dev));
> @@ -543,7 +674,9 @@ static int tsc2007_probe(struct i2c_client *client,
>  	if (err < 0) {
>  		dev_err(&client->dev,
>  			"Failed to setup chip: %d\n", err);
> +#ifdef CONFIG_IIO
>  		iio_device_unregister(indio_dev);
> +#endif
>  		return err;	/* usually, chip does not respond */
>  	}
>  
> @@ -557,6 +690,21 @@ static int tsc2007_probe(struct i2c_client *client,
>  	return 0;
>  }
>  
> +static int tsc2007_remove(struct i2c_client *client)
> +{
> +	struct tsc2007 *ts = i2c_get_clientdata(client);
> +	struct input_dev *input_dev = ts->input;
> +#ifdef CONFIG_IIO
> +	struct iio_dev *indio_dev = ts->indio;
> +#endif
> +	input_unregister_device(input_dev);
> +
> +#ifdef CONFIG_IIO
> +	iio_device_unregister(indio_dev);
> +#endif
> +	return 0;
> +}
> +
>  static const struct i2c_device_id tsc2007_idtable[] = {
>  	{ "tsc2007", 0 },
>  	{ }
> @@ -579,6 +727,7 @@ static struct i2c_driver tsc2007_driver = {
>  	},
>  	.id_table	= tsc2007_idtable,
>  	.probe		= tsc2007_probe,
> +	.remove		= tsc2007_remove,
>  };
>  
>  module_i2c_driver(tsc2007_driver);
> 

^ permalink raw reply

* Re: [PATCH v4 4/8] drivers:input:tsc2007: add iio interface to read external ADC input and temperature
From: H. Nikolaus Schaller @ 2016-10-22 20:46 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Benoît Cousson,
	Tony Lindgren, Russell King, Arnd Bergmann, Michael Welling,
	Mika Penttilä, Javier Martinez Canillas, Igor Grinberg,
	Sebastian Reichel, Andrew F. Davis, Mark Brown, linux-input,
	devicetree, linux-kernel, linux-omap, letux-kernel, linux-iio,
	kernel
In-Reply-To: <07bb4d52-a439-f9a9-8593-ddde8e93e6ee@kernel.org>

Hi Jonathan,

> Am 22.10.2016 um 20:33 schrieb Jonathan Cameron <jic23@kernel.org>:
> 
> On 17/10/16 14:57, H. Nikolaus Schaller wrote:
>> The tsc2007 chip not only has a resistive touch screen controller but
>> also an external AUX adc imput which can be used for an ambient
>> light sensor, battery voltage monitoring or any general purpose.
>> 
>> Additionally it can measure the chip temperature.
>> 
>> This extension provides an iio interface for these adc channels.
>> 
>> Since it is not wasting much resources and is very straightforward,
>> we simply provide all other adc channels as optional iio interfaces
>> as weel. This can be used for debugging or special applications.
> well
>> 
>> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
> This could be cleaner done perhaps by factoring out the IIO stuff into a separate
> file and using a header with stubs to deal with the no available case.
> 
> There will only be a handful of stubs and it'll give you a lot cleaner code
> in here.
> 
> If def fun in .c files is always harder to deal with than in a header
> where stubs are really obvious.

Yes, it became a lot of #ifdefs spread over the source file.

The easiest thing would be to require IIO to be enabled :)

With your proposal to consider refactoring, I think the crucial part
is the conditional allocation either through devm_iio_device_alloc()
or devm_kzalloc(). This can be refactored into some conditional
tsc2007_alloc().

I have tried some draft (not tested and not tidied up) to check if the
direction is good.

This reduces the number of #ifdef CONFIG_IIO from 7 to 2 without introducing
new files or includes. There are also 2 other #ifdef CONFIG_OF so it doesn't
seem to be very complex now in comparison. And the patch itself has only a
handful of hunks (8).

Moving tsc2007_alloc into a separate file tsc2007_iio.c would only move around
one #ifdef CONFIG_OF from tsc2007.c but IMHO makes it more difficult to understand
because it is not really iio specific and one has to switch between two source
files. And I would have to touch the Makefile as well.

What do you think?

If generally ok, I can include that in [PATCH v5].

BR and thanks,
Nikolaus


diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 5e3c4bf..691e79f 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -30,6 +30,7 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/input/touchscreen.h>
+#include <linux/iio/iio.h>
 
 #define TSC2007_MEASURE_TEMP0          (0x0 << 4)
 #define TSC2007_MEASURE_AUX            (0x2 << 4)
@@ -69,9 +70,13 @@ struct ts_event {
 
 struct tsc2007 {
        struct input_dev        *input;
+#ifdef CONFIG_IIO
+       struct iio_dev          *indio;
+#endif
        char                    phys[32];
 
        struct i2c_client       *client;
+       struct mutex            mlock;
 
        u16                     model;
        u16                     x_plate_ohms;
@@ -192,7 +197,10 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
        while (!ts->stopped && tsc2007_is_pen_down(ts)) {
 
                /* pen is down, continue with the measurement */
+
+               mutex_lock(&ts->mlock);
                tsc2007_read_values(ts, &tc);
+               mutex_unlock(&ts->mlock);
 
                rt = tsc2007_calculate_resistance(ts, &tc);
 
@@ -319,6 +327,162 @@ static void tsc2007_close(struct input_dev *input_dev)
        tsc2007_stop(ts);
 }
 
+#ifdef CONFIG_IIO
+
+#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
+{ \
+       .datasheet_name = _name, \
+       .type = _type, \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+                       BIT(_chan_info), \
+       .indexed = 1, \
+       .channel = _chan, \
+}
+
+static const struct iio_chan_spec tsc2007_iio_channel[] = {
+       TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
+       TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
+};
+
+static int tsc2007_read_raw(struct iio_dev *indio_dev,
+       struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+       struct  tsc2007 *tsc = iio_priv(indio_dev);
+       int adc_chan = chan->channel;
+       int ret = 0;
+
+       if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
+               return -EINVAL;
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       mutex_lock(&tsc->mlock);
+
+       switch (chan->channel) {
+       case 0:
+               *val = tsc2007_xfer(tsc, READ_X);
+               break;
+       case 1:
+               *val = tsc2007_xfer(tsc, READ_Y);
+               break;
+       case 2:
+               *val = tsc2007_xfer(tsc, READ_Z1);
+               break;
+       case 3:
+               *val = tsc2007_xfer(tsc, READ_Z2);
+               break;
+       case 4:
+               *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
+               break;
+       case 5: {
+               struct ts_event tc;
+
+               tc.x = tsc2007_xfer(tsc, READ_X);
+               tc.z1 = tsc2007_xfer(tsc, READ_Z1);
+               tc.z2 = tsc2007_xfer(tsc, READ_Z2);
+               *val = tsc2007_calculate_resistance(tsc, &tc);
+               break;
+       }
+       case 6:
+               *val = tsc2007_is_pen_down(tsc);
+               break;
+       case 7:
+               *val = tsc2007_xfer(tsc,
+                                   (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
+               break;
+       case 8:
+               *val = tsc2007_xfer(tsc,
+                                   (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
+               break;
+       }
+
+       /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+       tsc2007_xfer(tsc, PWRDOWN);
+
+       mutex_unlock(&tsc->mlock);
+
+       ret = IIO_VAL_INT;
+
+       return ret;
+}
+
+static const struct iio_info tsc2007_iio_info = {
+       .read_raw = tsc2007_read_raw,
+       .driver_module = THIS_MODULE,
+};
+
+static int tsc2007_alloc(struct i2c_client *client, struct tsc2007 **ts,
+                          struct input_dev **input_dev)
+{
+       int err;
+       struct iio_dev *indio_dev;
+
+       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ts));
+       if (!indio_dev) {
+               dev_err(&client->dev, "iio_device_alloc failed\n");
+               return -ENOMEM;
+       }
+
+       *ts = iio_priv(indio_dev);
+
+       *input_dev = devm_input_allocate_device(&client->dev);
+       if (!*input_dev)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, *ts);
+       (*ts)->indio = indio_dev;
+
+       indio_dev->name = "tsc2007";
+       indio_dev->dev.parent = &client->dev;
+       indio_dev->info = &tsc2007_iio_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = tsc2007_iio_channel;
+       indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
+
+       err = iio_device_register(indio_dev);
+       if (err < 0) {
+               dev_err(&client->dev, "iio_device_register() failed: %d\n",
+                       err);
+               return err;
+       }
+
+       return 0;
+}
+
+#define tsc2007_iio_device_unregister(ts) iio_device_unregister(ts->indio)
+
+#else /* CONFIG_IIO */
+
+static int tsc2007_alloc(struct i2c_client *client, struct tsc2007 **ts,
+                          struct input_dev **input_dev)
+{
+       int err;
+
+       *ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
+       if (!*ts)
+               return -ENOMEM;
+
+       *input_dev = devm_input_allocate_device(&client->dev);
+       if (!*input_dev)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, *ts);
+
+       return 0;
+}
+
+#define tsc2007_iio_device_unregister(ts) /* not needed */
+
+#endif /* CONFIG_IIO */
+
 #ifdef CONFIG_OF
 static int tsc2007_get_pendown_state_gpio(struct device *dev)
 {
@@ -459,20 +623,15 @@ static int tsc2007_probe(struct i2c_client *client,
                                     I2C_FUNC_SMBUS_READ_WORD_DATA))
                return -EIO;
 
-       ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
-       if (!ts)
-               return -ENOMEM;
-
-       input_dev = devm_input_allocate_device(&client->dev);
-       if (!input_dev)
-               return -ENOMEM;
-
-       i2c_set_clientdata(client, ts);
+       err = tsc2007_alloc(client, &ts, &input_dev);
+       if (err < 0)
+               return err;
 
        ts->client = client;
        ts->irq = client->irq;
        ts->input = input_dev;
        init_waitqueue_head(&ts->wait);
+       mutex_init(&ts->mlock);
 
        snprintf(ts->phys, sizeof(ts->phys),
                 "%s/input0", dev_name(&client->dev));
@@ -543,6 +702,7 @@ static int tsc2007_probe(struct i2c_client *client,
        if (err < 0) {
                dev_err(&client->dev,
                        "Failed to setup chip: %d\n", err);
+               tsc2007_iio_device_unregister(ts);
                return err;     /* usually, chip does not respond */
        }
 
@@ -556,6 +716,14 @@ static int tsc2007_probe(struct i2c_client *client,
        return 0;
 }
 
+static int tsc2007_remove(struct i2c_client *client)
+{
+       struct tsc2007 *ts = i2c_get_clientdata(client);
+       input_unregister_device(ts->input);
+       tsc2007_iio_device_unregister(ts);
+       return 0;
+}
+
 static const struct i2c_device_id tsc2007_idtable[] = {
        { "tsc2007", 0 },
        { }
@@ -578,6 +746,7 @@ static struct i2c_driver tsc2007_driver = {
        },
        .id_table       = tsc2007_idtable,
        .probe          = tsc2007_probe,
+       .remove         = tsc2007_remove,
 };
 
 module_i2c_driver(tsc2007_driver);

^ permalink raw reply related

* Re: [PATCH v2 4/4] cpufreq: pxa: convert to clock API
From: Robert Jarzmik @ 2016-10-22 21:37 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Daniel Mack, Haojian Zhuang, Rob Herring, Mark Rutland,
	Rafael Wysocki, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161018114025.GD11471@vireshk-i7>

Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> writes:

> On 15-10-16, 21:57, Robert Jarzmik wrote:
>> As the clock settings have been introduced into the clock pxa drivers,
>> which are now available to change the CPU clock by themselves, remove
>> the clock handling from this driver, and rely on pxa clock drivers.
>> 
>> Signed-off-by: Robert Jarzmik <robert.jarzmik-GANU6spQydw@public.gmane.org>
>> ---
>> Since v1: added !OF Kconfig dependency
>> ---
>>  drivers/cpufreq/Kconfig.arm      |   2 +-
>>  drivers/cpufreq/pxa2xx-cpufreq.c | 191 ++++++++-------------------------------
>>  2 files changed, 40 insertions(+), 153 deletions(-)
>
> Acked-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Okay, after some more testing, I'd like to remove the !OF for next iteration.
The reason is that I have a usecase where I have a single kernel image with
devictree support (ie CONFIG_OF=y), but with support for both devicetree and
legacy platforms. In this case, a platform such as lubbock can boot :
 - with devicetree
 - with legacy arch/arm/mach-pxa/lubbock.c

In this kernel, the !OF Kconfig prevents the legacy version from working, as
pxa2xx-cpufreq is descarded even if it is needed in the legacy version.

Therefore, I'd like to respin without this !OF.

Cheers.

-- 
Robert
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v2 0/7] IIO wrapper drivers, dpot-dac and envelope-detector
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
  To: linux-kernel
  Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Rob Herring, Mark Rutland, linux-iio,
	devicetree

Hi!

These two drivers share the fact that they wrap another iio channel,
and I use the first in combination with the second, which is why I'm
submitting them as a pair.

The first driver is a simple wrapper converting an iio dpot into an
iio dac. It only changes the unit and scale. It also does not add any
fancy iio buffer support that I don't need. I suppose that can be
added. By someone else :-)

The second driver (the envelope detector) is more involved. It also
explains why I need the dpot-dac driver. I wanted the envelope
detector to be generic and work with any dac, but I had a dpot...

But before those two new drivers, there is some infrastructure added
to provide available values for a channel.

v1 -> v2
- provide out_resistanceX_raw_available channel attribute in mcp4531 dpot

dpot-dac:
- change Vref to vref
- the module will be called dpot-dac (in Kconfig help)
- removed a 'the'
- removed (s64) cast
- make the channel indexed, makes libiio find the channel (tested with 0.5)
- add a comment on how integer scale is converted to fractional scale
  and clarify the code a bit
- dig out max-ohms by looking at scale and maximum available raw value
  from the dpot channel and drop the 'dpot-dac,max-ohms' devicetree property
- provide out_voltageX_raw_available channel attribute

envelope-detector:
- change compatible from envelope-detector to axentia,tse850-envelope-detector
- remove envelope-detector,invert and envelope-detector,comp-interval-ms from
  devicetree and add them as iio device attributes instead
- make the channel indexed, makes libiio find the channel (tested with 0.5)
- reorder struct envelope to better indicate what is covered by read_lock
- add comment on interaction between envelope_detector_comp_latch (renamed
  from envelope_detector_latch) and envelope_detector_comp_isr (renamed
  from envelope_detector_isr)
- fixup a problem in envelope_detector_comp_latch where interrupts pending
  from when the interrupt has been disabled interferes with expected
  operation
- slight rewrite of the initial high/low assignments
- use a better name when requesting the interrupt
- dig out dac_max by looking at scale and maximum available raw value
  from the dac channel and drop the 'envelope-detector,dac-max' devicetree
  property

Ok, so the devicetree compatible string for the envelope-detector gained
the "axentia,tse850-" prefix, but at the same time the driver also gained
generality that in my mind puts it in about the same position as the
dpot-dac driver. The driver really has very little to do with the specifics
of the TSE-850. But at the same time there are more ways to build an
envelope detector compared to the obviousness of building a dac from a dpot...

One thing I don't like is that the irq needs to be changed for cases where
it is only ever interesting with the 'invert' variant, and it may not work
to set up the irq the wrong way first. For the TSE-850, this does not
matter, since we have a mux on the envelope detector input and can make use
of both 'invert' and 'normal' for different signals (we could have gotten
by with only 'invert' since the only signals we are measuring that are
'normal' are also DC signals and can thus be detected both from below and
from above), but it is nice to have it both ways.

Also, perhaps the new device attributes (invert and compare_interval_ms)
should really be channel attributes?

Cheers,
Peter

PS. I have not tried to forward port anything else from the thread with
the old thread with the initial iio:core patch.

Jonathan Cameron (1):
  iio:core: add a callback to allow drivers to provide _available
    attributes

Peter Rosin (6):
  iio: mcp4531: provide range of available raw values
  dt-bindings: add axentia to vendor-prefixes
  dt-bindings: iio: document dpot-dac bindings
  iio: dpot-dac: DAC driver based on a digital potentiometer
  dt-bindings: iio: document envelope-detector bindings
  iio: envelope-detector: ADC driver based on a DAC and a comparator

 .../testing/sysfs-bus-iio-adc-envelope-detector    |  36 ++
 .../bindings/iio/adc/envelope-detector.txt         |  55 +++
 .../devicetree/bindings/iio/dac/dpot-dac.txt       |  41 ++
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 MAINTAINERS                                        |  15 +
 drivers/iio/adc/Kconfig                            |  10 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/envelope-detector.c                | 463 +++++++++++++++++++++
 drivers/iio/dac/Kconfig                            |  10 +
 drivers/iio/dac/Makefile                           |   1 +
 drivers/iio/dac/dpot-dac.c                         | 298 +++++++++++++
 drivers/iio/industrialio-core.c                    | 232 +++++++++--
 drivers/iio/potentiometer/mcp4531.c                | 104 +++--
 include/linux/iio/iio.h                            |  11 +
 include/linux/iio/types.h                          |   5 +
 15 files changed, 1216 insertions(+), 67 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
 create mode 100644 Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
 create mode 100644 Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
 create mode 100644 drivers/iio/adc/envelope-detector.c
 create mode 100644 drivers/iio/dac/dpot-dac.c

-- 
2.1.4

^ permalink raw reply

* [PATCH v2 1/7] iio:core: add a callback to allow drivers to provide _available attributes
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
  To: linux-kernel
  Cc: Jonathan Cameron, Peter Rosin, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Rob Herring, Mark Rutland, linux-iio,
	devicetree
In-Reply-To: <1477176226-10566-1-git-send-email-peda@axentia.se>

From: Jonathan Cameron <jic23@kernel.org>

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 <jic23@kernel.org>
[rather mindlessly forward ported from approx 3 years ago /peda]
Signed-off-by: Peter Rosin <peda@axentia.se>
---
 drivers/iio/industrialio-core.c | 232 +++++++++++++++++++++++++++++++++++-----
 include/linux/iio/iio.h         |  11 ++
 include/linux/iio/types.h       |   5 +
 3 files changed, 223 insertions(+), 25 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index fc340ed3dca1..93c69ebeda1e 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -575,51 +575,41 @@ 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, 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 sprintf(buf, "%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]),
+			return sprintf(buf, "-%d.%06u%s", abs(vals[0]),
 				       -vals[1], scale_db ? " dB" : "");
 		else
-			return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
+			return sprintf(buf, "%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]),
+			return sprintf(buf, "-%d.%09u", abs(vals[0]),
 				       -vals[1]);
 		else
-			return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
+			return sprintf(buf, "%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 sprintf(buf, "%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 sprintf(buf, "%d.%09u", tmp0, tmp1);
 	case IIO_VAL_INT_MULTIPLE:
 	{
 		int i;
@@ -628,13 +618,34 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
 		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;
 	}
 	default:
 		return 0;
 	}
 }
+
+/**
+ * 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)
+{
+	ssize_t len;
+
+	len = __iio_format_value(buf, type, size, vals);
+
+	return len + sprintf(buf + len, "\n");
+}
 EXPORT_SYMBOL_GPL(iio_format_value);
 
 static ssize_t iio_read_channel_info(struct device *dev,
@@ -662,6 +673,113 @@ 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, type, 1, &vals[i]);
+			if (i < length - 1)
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						"\n");
+		}
+		break;
+	default:
+		for (i = 0; i < length / 2; i++) {
+			len += __iio_format_value(buf + len,
+						  type,
+						  2,
+						  &vals[i * 2]);
+			if (i < length / 2 - 1)
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						"\n");
+		}
+	};
+
+	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, type, 1, &vals[i]);
+			if (i < 2)
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						"]\n");
+		}
+		break;
+	default:
+		for (i = 0; i < 3; i++) {
+			len += __iio_format_value(buf + len,
+						  type,
+						  2,
+						  &vals[i * 2]);
+			if (i < 2)
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						" ");
+			else
+				len += snprintf(buf + len,
+						PAGE_SIZE - len,
+						"]\n");
+		}
+	};
+
+	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
@@ -978,6 +1096,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)
 {
@@ -993,6 +1145,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);
@@ -1000,6 +1160,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);
@@ -1007,6 +1175,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);
@@ -1014,6 +1189,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 b4a0679e4a49..c0f897084620 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -269,9 +269,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;
@@ -397,6 +401,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_el);
+
 	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_ */
-- 
2.1.4

^ permalink raw reply related

* [PATCH v2 2/7] iio: mcp4531: provide range of available raw values
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477176226-10566-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
 drivers/iio/potentiometer/mcp4531.c | 104 +++++++++++++++++++++---------------
 1 file changed, 62 insertions(+), 42 deletions(-)

diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c
index 13b6ae2fcf7b..0d1bcf89ae17 100644
--- a/drivers/iio/potentiometer/mcp4531.c
+++ b/drivers/iio/potentiometer/mcp4531.c
@@ -38,7 +38,7 @@
 
 struct mcp4531_cfg {
 	int wipers;
-	int max_pos;
+	int avail[3];
 	int kohms;
 };
 
@@ -78,38 +78,38 @@ enum mcp4531_type {
 };
 
 static const struct mcp4531_cfg mcp4531_cfg[] = {
-	[MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
-	[MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
-	[MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
-	[MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
-	[MCP454x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
-	[MCP454x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
-	[MCP454x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
-	[MCP454x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
-	[MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
-	[MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
-	[MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
-	[MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
-	[MCP456x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
-	[MCP456x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
-	[MCP456x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
-	[MCP456x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
-	[MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
-	[MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
-	[MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
-	[MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
-	[MCP464x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
-	[MCP464x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
-	[MCP464x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
-	[MCP464x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
-	[MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
-	[MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
-	[MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
-	[MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
-	[MCP466x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
-	[MCP466x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
-	[MCP466x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
-	[MCP466x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
+	[MCP453x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP453x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP453x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP453x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP454x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP454x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP454x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP454x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP455x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP455x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP455x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP455x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
+	[MCP456x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP456x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP456x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP456x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
+	[MCP463x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP463x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP463x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP463x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP464x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =   5, },
+	[MCP464x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  10, },
+	[MCP464x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms =  50, },
+	[MCP464x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
+	[MCP465x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP465x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP465x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP465x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
+	[MCP466x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =   5, },
+	[MCP466x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  10, },
+	[MCP466x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms =  50, },
+	[MCP466x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
 };
 
 #define MCP4531_WRITE (0 << 2)
@@ -124,13 +124,14 @@ struct mcp4531_data {
 	const struct mcp4531_cfg *cfg;
 };
 
-#define MCP4531_CHANNEL(ch) {					\
-	.type = IIO_RESISTANCE,					\
-	.indexed = 1,						\
-	.output = 1,						\
-	.channel = (ch),					\
-	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
-	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+#define MCP4531_CHANNEL(ch) {						\
+	.type = IIO_RESISTANCE,						\
+	.indexed = 1,							\
+	.output = 1,							\
+	.channel = (ch),						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),		\
+	.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW),	\
 }
 
 static const struct iio_chan_spec mcp4531_channels[] = {
@@ -156,13 +157,31 @@ static int mcp4531_read_raw(struct iio_dev *indio_dev,
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
 		*val = 1000 * data->cfg->kohms;
-		*val2 = data->cfg->max_pos;
+		*val2 = data->cfg->avail[2];
 		return IIO_VAL_FRACTIONAL;
 	}
 
 	return -EINVAL;
 }
 
+static int mcp4531_read_avail(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      const int **vals, int *type, int *length,
+			      long mask)
+{
+	struct mcp4531_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		*length = ARRAY_SIZE(data->cfg->avail);
+		*vals = data->cfg->avail;
+		*type = IIO_VAL_INT;
+		return IIO_AVAIL_RANGE;
+	}
+
+	return -EINVAL;
+}
+
 static int mcp4531_write_raw(struct iio_dev *indio_dev,
 			     struct iio_chan_spec const *chan,
 			     int val, int val2, long mask)
@@ -172,7 +191,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		if (val > data->cfg->max_pos || val < 0)
+		if (val > data->cfg->avail[2] || val < 0)
 			return -EINVAL;
 		break;
 	default:
@@ -186,6 +205,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
 
 static const struct iio_info mcp4531_info = {
 	.read_raw = mcp4531_read_raw,
+	.read_avail = mcp4531_read_avail,
 	.write_raw = mcp4531_write_raw,
 	.driver_module = THIS_MODULE,
 };
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox