* Re: [PATCH v2 1/2] devicetree: add Garmin vendor prefix
From: Jonathan Cameron @ 2016-12-31 15:19 UTC (permalink / raw)
To: Matt Ranostay
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring
In-Reply-To: <CAJ_EiSTQ6naDiRP6o1UOr33UyKDtXaan2j4UPFNMTRKReuR-PQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 31/12/16 05:55, Matt Ranostay wrote:
> On Fri, Dec 30, 2016 at 12:26 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>> On 29/12/16 05:13, Matt Ranostay wrote:
>>> Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>>> Signed-off-by: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
>> Rob, just to check - is this the right way to go with prefixes?
>> It's awfully ugly in this case ;)
>
> Rob said the stock ticker (likely the US exchanges) is the way to go :)
Fair enough. Both applied to the togreg branch of iio.git and pushed
out as testing.
Thanks,
Jonathan
>
>>
>> Jonathan
>>> ---
>>> Changes from v1:
>>> * switch to stock ticker for Garmin Limited
>>>
>>> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>>> 1 file changed, 1 insertion(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> index 16d3b5e7f5d1..5749bfc5fc5b 100644
>>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> @@ -107,6 +107,7 @@ firefly Firefly
>>> focaltech FocalTech Systems Co.,Ltd
>>> friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
>>> fsl Freescale Semiconductor
>>> +grmn Garmin Limited
>>> ge General Electric Company
>>> geekbuying GeekBuying
>>> gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
>>>
>>
^ permalink raw reply
* Re: [PATCH v5 3/4] clk: rockchip: add new pll-type for rk3328
From: Heiko Stuebner @ 2016-12-31 12:53 UTC (permalink / raw)
To: Elaine Zhang
Cc: mturquette, sboyd, xf, robh+dt, mark.rutland, linux-clk, huangtao,
xxx, cl, linux-rockchip, linux-kernel, devicetree,
linux-arm-kernel
In-Reply-To: <1482979511-6847-4-git-send-email-zhangqing@rock-chips.com>
Am Donnerstag, 29. Dezember 2016, 10:45:10 CET schrieb Elaine Zhang:
> The rk3328's pll and clock are similar with rk3036's,
> it different with pll_mode_mask, the rk3328 soc
> pll mode only one bit(rk3036 soc have two bits)
> so these should be independent and separate from
> the series of rk3328s.
>
> Changes in v4:
> adjust the pacth 3 and 4 order.
> move pll_rk3328 to patch 3.
> Changes in v3:
> fix up the pll type pll_rk3328 description and use
>
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
applied to my clk-branch for 4.11
The clock controller itself also looks good now, I'll just give Rob or someone
else a bit of time for eventual comments after new years :-)
Heiko
^ permalink raw reply
* Re: [PATCH v2 1/2] devicetree: add Garmin vendor prefix
From: Matt Ranostay @ 2016-12-31 5:55 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring
In-Reply-To: <144bd616-1b5a-970a-edcb-13ebf67178f1-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On Fri, Dec 30, 2016 at 12:26 PM, Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On 29/12/16 05:13, Matt Ranostay wrote:
>> Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> Signed-off-by: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
> Rob, just to check - is this the right way to go with prefixes?
> It's awfully ugly in this case ;)
Rob said the stock ticker (likely the US exchanges) is the way to go :)
>
> Jonathan
>> ---
>> Changes from v1:
>> * switch to stock ticker for Garmin Limited
>>
>> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index 16d3b5e7f5d1..5749bfc5fc5b 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -107,6 +107,7 @@ firefly Firefly
>> focaltech FocalTech Systems Co.,Ltd
>> friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
>> fsl Freescale Semiconductor
>> +grmn Garmin Limited
>> ge General Electric Company
>> geekbuying GeekBuying
>> gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
>>
>
--
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] iio: adc: Add Renesas GyroADC driver
From: Marek Vasut @ 2016-12-30 21:49 UTC (permalink / raw)
To: Peter Meerwald-Stadler
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven,
Simon Horman
In-Reply-To: <alpine.DEB.2.02.1612302205210.3977-jW+XmwGofnusTnJN9+BGXg@public.gmane.org>
On 12/30/2016 10:09 PM, Peter Meerwald-Stadler wrote:
>
>>>> Add IIO driver for the Renesas RCar GyroADC block. This block is a
>>>> simple 4/8-channel ADC which samples 12/15/24 bits of data every
>
>>>> + if (iio_buffer_enabled(indio_dev))
>>>> + return -EBUSY;
>>>
>>> use iio_device_claim_direct_mode()
>>
>> Why ? Do I really need the mutex locking here ?
>
> no, not needed, since you do not support buffered mode yet; but neither do
> you need iio_buffer_enabled()
>
> p.
>
So drop the whole thing then ?
--
Best regards,
Marek Vasut
--
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/2] cfg80211: reg: support ieee80211-(min|max)-center-freq DT properties
From: Rafał Miłecki @ 2016-12-30 21:37 UTC (permalink / raw)
To: Arend van Spriel
Cc: Kalle Valo,
linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Martin Blumenstingl, Felix Fietkau, Arnd Bergmann,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Rafał Miłecki
In-Reply-To: <86a22b00-1a04-25e7-9d31-2c1fd9d04e48-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
On 30 December 2016 at 21:20, Arend van Spriel
<arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
> On 29-12-16 10:43, Rafał Miłecki wrote:
>> On 29 December 2016 at 09:57, Arend van Spriel
>> <arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
>>> On 28-12-16 22:30, Rafał Miłecki wrote:
>>>> On 28 December 2016 at 22:28, Rafał Miłecki <zajec5-Re5JQEeQqe8@public.gmane.orgm> wrote:
>>>>> On 28 December 2016 at 22:07, Arend van Spriel
>>>>> <arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
>>>>>> On 28-12-16 16:59, Rafał Miłecki wrote:
>>>>>>> From: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>>>>>>
>>>>>>> They allow specifying hardware limitations of supported channels. This
>>>>>>> may be useful for specifying single band devices or devices that support
>>>>>>> only some part of the whole band.
>>>>>>> E.g. some tri-band routers have separated radios for lower and higher
>>>>>>> part of 5 GHz band.
>>>>>>>
>>>>>>> Signed-off-by: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>>>>>> ---
>>>>>>> net/wireless/reg.c | 34 ++++++++++++++++++++++++++++++++++
>>>>>>> 1 file changed, 34 insertions(+)
>>>>>>>
>>>>>>> diff --git a/net/wireless/reg.c b/net/wireless/reg.c
>>>>>>> index 5dbac37..35ba5c7 100644
>>>>>>> --- a/net/wireless/reg.c
>>>>>>> +++ b/net/wireless/reg.c
>>>>>>> @@ -1123,6 +1123,26 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
>>>>>>> }
>>>>>>> EXPORT_SYMBOL(reg_initiator_name);
>>>>>>>
>>>>>>> +static bool reg_center_freq_of_valid(struct wiphy *wiphy,
>>>>>>> + struct ieee80211_channel *chan)
>>>>>>> +{
>>>>>>> + struct device_node *np = wiphy_dev(wiphy)->of_node;
>>>>>>> + u32 val;
>>>>>>> +
>>>>>>> + if (!np)
>>>>>>> + return true;
>>>>>>> +
>>>>>>> + if (!of_property_read_u32(np, "ieee80211-min-center-freq", &val) &&
>>>>>>> + chan->center_freq < KHZ_TO_MHZ(val))
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + if (!of_property_read_u32(np, "ieee80211-max-center-freq", &val) &&
>>>>>>> + chan->center_freq > KHZ_TO_MHZ(val))
>>>>>>> + return false;
>>>>>>
>>>>>> I suspect these functions rely on CONFIG_OF. So might not build for
>>>>>> !CONFIG_OF.
>>>>>
>>>>> I compiled it with
>>>>> # CONFIG_OF is not set
>>>>>
>>>>> Can you test it and provide a non-working config if you see a
>>>>> compilation error, please?
>>>>
>>>> include/linux/of.h provides a lot of dummy static inline functions if
>>>> CONFIG_OF is not used (they also allow compiler to drop most of the
>>>> code).
>>>
>>> of_propeirty_read_u32 is static inline in of.h calling
>>> of_property_read_u32_array, which has a dummy variant in of.h returning
>>> -ENOSYS so -38. Pretty sure that is not what you want. At least it does
>>> not allow the compiler to drop any code so probably better to do:
>>>
>>> if (!IS_ENABLED(CONFIG_OF) || !np)
>>> return true;
>>
>> Please verify that using a compiler. If there is a problem I'll be
>> happy to work on it, but I need a proof it exists.
>
> I am on vacation right now so not having much more than email and web
> browser to use as review reference.
>
>> If compilers sees a:
>> if (!-ENOSYS && chan->center_freq < KHZ_TO_MHZ(val))
>> condition, it's pretty clear it can be dropped. With both conditional
>> blocks dropped function always returns "true" and... can be dropped.
>>
>> Let me see if I can convince you with the following test:
>
> No need to convince me. I made a mistake reviewing the code. Thanks for
> clarifying it.
>
>> $ grep -m 1 CONFIG_OF .config
>> # CONFIG_OF is not set
>> $ objdump --syms net/wireless/reg.o | grep -c reg_center_freq_of_valid
>> 0
>>
>> $ grep -m 1 CONFIG_OF .config
>> CONFIG_OF=y
>> $ objdump --syms net/wireless/reg.o | grep -c reg_center_freq_of_valid
>> 1
>>
>>
>>> So with this patch you change the channel to DISABLED. I am not very
>>> familiar with reg.c so do you know if this is done before or after
>>> calling regulatory notifier in the driver. brcmfmac will enable channels
>>> querying the device upon regulatory notifier call, which may undo the
>>> behavior introduced by your patch.
>>
>> I'm not regulatory export, so I hope someone will review this patch.
>> So far I can say it works for me after trying it on SR400ac with
>> BCM43602.
>
> But you probably do not have a mapping table for mapping country code
> received in notifier to firmware regulatory code/revision. Only if you
> have that, brcmfmac will update the channels in the bands.
Thanks, I'll redo my tests.
> Giving this some more consideration I am not sure if this is the proper
> place to handle this. ieee80211-(min|max)-center-freq is platform
> specific configuration allowing multiple cards to be used in different
> (sub)bands. This has nothing to do with regulatory. So probably better
> to move it to core.c or chan.c.
I can see point in this, I'll see if I can put this code in a more
proper place. Thanks for your comment!
--
Rafał
--
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 v6 6/8] IIO: add STM32 timer trigger driver
From: Jonathan Cameron @ 2016-12-30 21:12 UTC (permalink / raw)
To: Benjamin Gaignard, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
alexandre.torgue-qxv4g6HH51o, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
linux-pwm-u79uwXL29TY76Z2rM5mHXA, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: fabrice.gasnier-qxv4g6HH51o, gerald.baeza-qxv4g6HH51o,
arnaud.pouliquen-qxv4g6HH51o,
linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
linaro-kernel-cunTk1MwBs8s++Sfvej+rw, Benjamin Gaignard
In-Reply-To: <1481292919-26587-7-git-send-email-benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
On 09/12/16 14:15, Benjamin Gaignard wrote:
> Timers IPs can be used to generate triggers for other IPs like
> DAC, ADC or other timers.
> Each trigger may result of timer internals signals like counter enable,
> reset or edge, this configuration could be done through "master_mode"
> device attribute.
>
> A timer device could be triggered by other timers, we use the trigger
> name and is_stm32_iio_timer_trigger() function to distinguish them
> and configure IP input switch.
>
> Timer may also decide on which event (edge, level) they could
> be activated by a trigger, this configuration is done by writing in
> "slave_mode" device attribute.
>
> Since triggers could also be used by DAC or ADC their names are defined
> in include/ nux/iio/timer/stm32-timer-trigger.h so those IPs will be able
> to configure themselves in valid_trigger function
>
> Trigger have a "sampling_frequency" attribute which allow to configure
> timer sampling frequency without using PWM interface
>
> version 5:
> - simplify tables of triggers
> - only create an IIO device when needed
>
> version 4:
> - get triggers configuration from "reg" in DT
> - add tables of triggers
> - sampling frequency is enable/disable when writing in trigger
> sampling_frequency attribute
> - no more use of interruptions
>
> version 3:
> - change compatible to "st,stm32-timer-trigger"
> - fix attributes access right
> - use string instead of int for master_mode and slave_mode
> - document device attributes in sysfs-bus-iio-timer-stm32
>
> version 2:
> - keep only one compatible
> - use st,input-triggers-names and st,output-triggers-names
> to know which triggers are accepted and/or create by the device
Firstly, sorry it has taken me so long to get back to this.
I'm still not keen on this use of iio_device elements just to act as
glue between triggers. I think we need to work out a more light weight
way to do this. As you are only using them for validation and to provide
somewhere to hang the control attibutes off, there is nothing stopping us
moving that over to the iio_trigger instead which would avoid the messy
duality going on here.
I might still be missing something though!
You would only I think need 3 attributes
parrent_trigger
and something like your master_mode and slave_mode attributes.
The parrent_trigger would need some validation etc, but if we keep it
within this driver initially that won't be hard to do. Checking the device
parent matches will do most of it.
Jonathan
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
> ---
> .../ABI/testing/sysfs-bus-iio-timer-stm32 | 55 +++
> drivers/iio/Kconfig | 2 +-
> drivers/iio/Makefile | 1 +
> drivers/iio/timer/Kconfig | 13 +
> drivers/iio/timer/Makefile | 1 +
> drivers/iio/timer/stm32-timer-trigger.c | 466 +++++++++++++++++++++
> drivers/iio/trigger/Kconfig | 1 -
> include/linux/iio/timer/stm32-timer-trigger.h | 62 +++
> 8 files changed, 599 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
> create mode 100644 drivers/iio/timer/Kconfig
> create mode 100644 drivers/iio/timer/Makefile
> create mode 100644 drivers/iio/timer/stm32-timer-trigger.c
> create mode 100644 include/linux/iio/timer/stm32-timer-trigger.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
> new file mode 100644
> index 0000000..26583dd
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
> @@ -0,0 +1,55 @@
> +What: /sys/bus/iio/devices/iio:deviceX/master_mode_available
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the list possible master modes which are:
> + - "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO).
> + - "enable" : The Counter Enable signal CNT_EN is used as trigger output.
> + - "update" : The update event is selected as trigger output.
> + For instance a master timer can then be used as a prescaler for a slave timer.
> + - "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set.
> + - "OC1REF" : OC1REF signal is used as trigger output.
> + - "OC2REF" : OC2REF signal is used as trigger output.
> + - "OC3REF" : OC3REF signal is used as trigger output.
> + - "OC4REF" : OC4REF signal is used as trigger output.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/master_mode
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the current master modes.
> + Writing set the master mode
> +
> +What: /sys/bus/iio/devices/iio:deviceX/slave_mode_available
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the list possible slave modes which are:
> + - "disabled" : The prescaler is clocked directly by the internal clock.
> + - "encoder_1" : Counter counts up/down on TI2FP1 edge depending on TI1FP2 level.
> + - "encoder_2" : Counter counts up/down on TI1FP2 edge depending on TI2FP1 level.
> + - "encoder_3" : Counter counts up/down on both TI1FP1 and TI2FP2 edges depending
> + on the level of the other input.
> + - "reset" : Rising edge of the selected trigger input reinitializes the counter
> + and generates an update of the registers.
> + - "gated" : The counter clock is enabled when the trigger input is high.
> + The counter stops (but is not reset) as soon as the trigger becomes low.
> + Both start and stop of the counter are controlled.
> + - "trigger" : The counter starts at a rising edge of the trigger TRGI (but it is not
> + reset). Only the start of the counter is controlled.
> + - "external_clock": Rising edges of the selected trigger (TRGI) clock the counter.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/slave_mode
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the current slave mode.
> + Writing set the slave mode
> +
> +What: /sys/bus/iio/devices/triggerX/sampling_frequency
> +KernelVersion: 4.10
> +Contact: benjamin.gaignard-qxv4g6HH51o@public.gmane.org
> +Description:
> + Reading returns the current sampling frequency.
> + Writing an value different of 0 set and start sampling.
> + Writing 0 stop sampling.
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 6743b18..2de2a80 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -90,5 +90,5 @@ source "drivers/iio/potentiometer/Kconfig"
> source "drivers/iio/pressure/Kconfig"
> source "drivers/iio/proximity/Kconfig"
> source "drivers/iio/temperature/Kconfig"
> -
> +source "drivers/iio/timer/Kconfig"
> endif # IIO
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 87e4c43..b797c08 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -32,4 +32,5 @@ obj-y += potentiometer/
> obj-y += pressure/
> obj-y += proximity/
> obj-y += temperature/
> +obj-y += timer/
> obj-y += trigger/
> diff --git a/drivers/iio/timer/Kconfig b/drivers/iio/timer/Kconfig
> new file mode 100644
> index 0000000..e3c21f2
> --- /dev/null
> +++ b/drivers/iio/timer/Kconfig
> @@ -0,0 +1,13 @@
> +#
> +# Timers drivers
> +
> +menu "Timers"
> +
> +config IIO_STM32_TIMER_TRIGGER
> + tristate "STM32 Timer Trigger"
> + depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
> + select IIO_TRIGGERED_EVENT
> + help
> + Select this option to enable STM32 Timer Trigger
> +
> +endmenu
> diff --git a/drivers/iio/timer/Makefile b/drivers/iio/timer/Makefile
> new file mode 100644
> index 0000000..4ad95ec9
> --- /dev/null
> +++ b/drivers/iio/timer/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
> diff --git a/drivers/iio/timer/stm32-timer-trigger.c b/drivers/iio/timer/stm32-timer-trigger.c
> new file mode 100644
> index 0000000..8d16e8f
> --- /dev/null
> +++ b/drivers/iio/timer/stm32-timer-trigger.c
> @@ -0,0 +1,466 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/timer/stm32-timer-trigger.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_event.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/stm32-timers.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#define MAX_TRIGGERS 6
> +#define MAX_VALIDS 5
> +
> +/* List the triggers created by each timer */
> +static const void *triggers_table[][MAX_TRIGGERS] = {
> + { TIM1_TRGO, TIM1_CH1, TIM1_CH2, TIM1_CH3, TIM1_CH4,},
> + { TIM2_TRGO, TIM2_CH1, TIM2_CH2, TIM2_CH3, TIM2_CH4,},
> + { TIM3_TRGO, TIM3_CH1, TIM3_CH2, TIM3_CH3, TIM3_CH4,},
> + { TIM4_TRGO, TIM4_CH1, TIM4_CH2, TIM4_CH3, TIM4_CH4,},
> + { TIM5_TRGO, TIM5_CH1, TIM5_CH2, TIM5_CH3, TIM5_CH4,},
> + { TIM6_TRGO,},
> + { TIM7_TRGO,},
> + { TIM8_TRGO, TIM8_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,},
> + { TIM9_TRGO, TIM9_CH1, TIM9_CH2,},
> + { TIM12_TRGO, TIM12_CH1, TIM12_CH2,},
> +};
> +
> +/* List the triggers accepted by each timer */
> +static const void *valids_table[][MAX_VALIDS] = {
> + { TIM5_TRGO, TIM2_TRGO, TIM4_TRGO, TIM3_TRGO,},
> + { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,},
> + { TIM1_TRGO, TIM8_TRGO, TIM5_TRGO, TIM4_TRGO,},
> + { TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,},
> + { TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,},
> + { }, /* timer 6 */
> + { }, /* timer 7 */
> + { TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,},
> + { TIM2_TRGO, TIM3_TRGO,},
> + { TIM4_TRGO, TIM5_TRGO,},
> +};
> +
> +struct stm32_timer_trigger {
> + struct device *dev;
> + struct regmap *regmap;
> + struct clk *clk;
> + u32 max_arr;
> + const void *triggers;
> + const void *valids;
> +};
> +
> +static int stm32_timer_start(struct stm32_timer_trigger *priv,
> + unsigned int frequency)
> +{
> + unsigned long long prd, div;
> + int prescaler = 0;
> + u32 ccer, cr1;
> +
> + /* Period and prescaler values depends of clock rate */
> + div = (unsigned long long)clk_get_rate(priv->clk);
> +
> + do_div(div, frequency);
> +
> + prd = div;
> +
> + /*
> + * Increase prescaler value until we get a result that fit
> + * with auto reload register maximum value.
> + */
> + while (div > priv->max_arr) {
> + prescaler++;
> + div = prd;
> + do_div(div, (prescaler + 1));
> + }
> + prd = div;
> +
> + if (prescaler > MAX_TIM_PSC) {
> + dev_err(priv->dev, "prescaler exceeds the maximum value\n");
> + return -EINVAL;
> + }
> +
> + /* Check if nobody else use the timer */
> + regmap_read(priv->regmap, TIM_CCER, &ccer);
> + if (ccer & TIM_CCER_CCXE)
> + return -EBUSY;
> +
> + regmap_read(priv->regmap, TIM_CR1, &cr1);
> + if (!(cr1 & TIM_CR1_CEN))
> + clk_enable(priv->clk);
> +
> + regmap_write(priv->regmap, TIM_PSC, prescaler);
> + regmap_write(priv->regmap, TIM_ARR, prd - 1);
> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
> +
> + /* Force master mode to update mode */
> + regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 0x20);
> +
> + /* Make sure that registers are updated */
> + regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
> +
> + /* Enable controller */
> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
> +
> + return 0;
> +}
> +
> +static void stm32_timer_stop(struct stm32_timer_trigger *priv)
> +{
> + u32 ccer, cr1;
> +
> + regmap_read(priv->regmap, TIM_CCER, &ccer);
> + if (ccer & TIM_CCER_CCXE)
> + return;
> +
> + regmap_read(priv->regmap, TIM_CR1, &cr1);
> + if (cr1 & TIM_CR1_CEN)
> + clk_disable(priv->clk);
> +
> + /* Stop timer */
> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
> + regmap_write(priv->regmap, TIM_PSC, 0);
> + regmap_write(priv->regmap, TIM_ARR, 0);
> +}
> +
> +static ssize_t stm32_tt_store_frequency(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_trigger *trig = to_iio_trigger(dev);
> + struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig);
> + unsigned int freq;
> + int ret;
> +
> + ret = kstrtouint(buf, 10, &freq);
> + if (ret)
> + return ret;
> +
> + if (freq == 0) {
> + stm32_timer_stop(priv);
> + } else {
> + ret = stm32_timer_start(priv, freq);
> + if (ret)
> + return ret;
> + }
> +
> + return len;
> +}
> +
> +static ssize_t stm32_tt_read_frequency(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct iio_trigger *trig = to_iio_trigger(dev);
> + struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig);
> + u32 psc, arr, cr1;
> + unsigned long long freq = 0;
> +
> + regmap_read(priv->regmap, TIM_CR1, &cr1);
> + regmap_read(priv->regmap, TIM_PSC, &psc);
> + regmap_read(priv->regmap, TIM_ARR, &arr);
> +
> + if (psc && arr && (cr1 & TIM_CR1_CEN)) {
> + freq = (unsigned long long)clk_get_rate(priv->clk);
> + do_div(freq, psc);
> + do_div(freq, arr);
> + }
> +
> + return sprintf(buf, "%d\n", (unsigned int)freq);
> +}
> +
> +static IIO_DEV_ATTR_SAMP_FREQ(0660,
> + stm32_tt_read_frequency,
> + stm32_tt_store_frequency);
> +
> +static struct attribute *stm32_trigger_attrs[] = {
> + &iio_dev_attr_sampling_frequency.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group stm32_trigger_attr_group = {
> + .attrs = stm32_trigger_attrs,
> +};
> +
> +static const struct attribute_group *stm32_trigger_attr_groups[] = {
> + &stm32_trigger_attr_group,
> + NULL,
> +};
> +
> +static char *master_mode_table[] = {
> + "reset",
> + "enable",
> + "update",
> + "compare_pulse",
> + "OC1REF",
> + "OC2REF",
> + "OC3REF",
> + "OC4REF"
> +};
> +
> +static ssize_t stm32_tt_show_master_mode(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + u32 cr2;
> +
> + regmap_read(priv->regmap, TIM_CR2, &cr2);
> + cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT;
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]);
> +}
> +
> +static ssize_t stm32_tt_store_master_mode(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) {
> + if (!strncmp(master_mode_table[i], buf,
> + strlen(master_mode_table[i]))) {
> + regmap_update_bits(priv->regmap, TIM_CR2,
> + TIM_CR2_MMS, i << TIM_CR2_MMS_SHIFT);
> + return len;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static IIO_CONST_ATTR(master_mode_available,
> + "reset enable update compare_pulse OC1REF OC2REF OC3REF OC4REF");
> +
> +static IIO_DEVICE_ATTR(master_mode, 0660,
> + stm32_tt_show_master_mode,
> + stm32_tt_store_master_mode,
> + 0);
> +
> +static char *slave_mode_table[] = {
> + "disabled",
> + "encoder_1",
> + "encoder_2",
> + "encoder_3",
> + "reset",
> + "gated",
> + "trigger",
> + "external_clock",
> +};
> +
> +static ssize_t stm32_tt_show_slave_mode(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + u32 smcr;
> +
> + regmap_read(priv->regmap, TIM_SMCR, &smcr);
> + smcr &= TIM_SMCR_SMS;
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", slave_mode_table[smcr]);
> +}
> +
> +static ssize_t stm32_tt_store_slave_mode(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(slave_mode_table); i++) {
> + if (!strncmp(slave_mode_table[i], buf,
> + strlen(slave_mode_table[i]))) {
> + regmap_update_bits(priv->regmap,
> + TIM_SMCR, TIM_SMCR_SMS, i);
> + return len;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static IIO_CONST_ATTR(slave_mode_available,
> +"disabled encoder_1 encoder_2 encoder_3 reset gated trigger external_clock");
> +
> +static IIO_DEVICE_ATTR(slave_mode, 0660,
> + stm32_tt_show_slave_mode,
> + stm32_tt_store_slave_mode,
> + 0);
> +
> +static struct attribute *stm32_timer_attrs[] = {
> + &iio_dev_attr_master_mode.dev_attr.attr,
> + &iio_const_attr_master_mode_available.dev_attr.attr,
> + &iio_dev_attr_slave_mode.dev_attr.attr,
> + &iio_const_attr_slave_mode_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group stm32_timer_attr_group = {
> + .attrs = stm32_timer_attrs,
> +};
> +
> +static const struct iio_trigger_ops timer_trigger_ops = {
> + .owner = THIS_MODULE,
> +};
> +
> +static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv)
> +{
> + int ret;
> + const char * const *cur = priv->triggers;
> +
> + while (cur && *cur) {
> + struct iio_trigger *trig;
> +
> + trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur);
> + if (!trig)
> + return -ENOMEM;
> +
> + trig->dev.parent = priv->dev->parent;
> + trig->ops = &timer_trigger_ops;
> + trig->dev.groups = stm32_trigger_attr_groups;
> + iio_trigger_set_drvdata(trig, priv);
> +
> + ret = devm_iio_trigger_register(priv->dev, trig);
> + if (ret)
> + return ret;
> + cur++;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * is_stm32_timer_trigger
> + * @trig: trigger to be checked
> + *
> + * return true if the trigger is a valid stm32 iio timer trigger
> + * either return false
> + */
> +bool is_stm32_timer_trigger(struct iio_trigger *trig)
> +{
> + return (trig->ops == &timer_trigger_ops);
> +}
> +EXPORT_SYMBOL(is_stm32_timer_trigger);
> +
> +static int stm32_validate_trigger(struct iio_dev *indio_dev,
> + struct iio_trigger *trig)
> +{
> + struct stm32_timer_trigger *priv = iio_priv(indio_dev);
> + const char * const *cur = priv->valids;
> + unsigned int i = 0;
> +
> + if (!is_stm32_timer_trigger(trig))
> + return -EINVAL;
> +
> + while (cur && *cur) {
> + if (!strncmp(trig->name, *cur, strlen(trig->name))) {
> + regmap_update_bits(priv->regmap,
> + TIM_SMCR, TIM_SMCR_TS,
> + i << TIM_SMCR_TS_SHIFT);
> + return 0;
> + }
> + cur++;
> + i++;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_trigger_info = {
> + .driver_module = THIS_MODULE,
> + .validate_trigger = stm32_validate_trigger,
> + .attrs = &stm32_timer_attr_group,
> +};
> +
> +static struct stm32_timer_trigger *stm32_setup_iio_device(struct device *dev)
> +{
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev,
> + sizeof(struct stm32_timer_trigger));
> + if (!indio_dev)
> + return NULL;
> +
> + indio_dev->name = dev_name(dev);
> + indio_dev->dev.parent = dev;
> + indio_dev->info = &stm32_trigger_info;
> + indio_dev->modes = INDIO_EVENT_TRIGGERED;
> + indio_dev->num_channels = 0;
> + indio_dev->dev.of_node = dev->of_node;
> +
> + ret = devm_iio_device_register(dev, indio_dev);
> + if (ret)
> + return NULL;
> +
> + return iio_priv(indio_dev);
> +}
> +
> +static int stm32_timer_trigger_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct stm32_timer_trigger *priv;
> + struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
> + unsigned int index;
> + int ret;
> +
> + if (of_property_read_u32(dev->of_node, "reg", &index))
> + return -EINVAL;
> +
> + if (index >= ARRAY_SIZE(triggers_table))
> + return -EINVAL;
> +
> + /* Create an IIO device only if we have triggers to be validated */
> + if (*valids_table[index])
> + priv = stm32_setup_iio_device(dev);
I still don't like this. Really feels like we shouldn't be creating an
iio device with all the bagage that carries just to allow us to do the
trigger trees. We ought to have a much more light weight solution for this
functionality - we aren't typically even using the interrupt tree stuff
that the triggers for devices are all really about.
A simpler approach of allowing each trigger the option of a parent seems like
it would be cleaner. Could be done entirely within this driver in the first
instance. Basically it would just look like your master and slave attributes
but have those under triggerX not iio:deviceX.
We can work out how to make it more generic later - including perhaps the
option to trigger from triggers outside this driver, using some parallel
infrastructure to the device triggering.
> + else
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->dev = dev;
> + priv->regmap = ddata->regmap;
> + priv->clk = ddata->clk;
> + priv->max_arr = ddata->max_arr;
> + priv->triggers = triggers_table[index];
> + priv->valids = valids_table[index];
> +
> + ret = stm32_setup_iio_triggers(priv);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, priv);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id stm32_trig_of_match[] = {
> + { .compatible = "st,stm32-timer-trigger", },
> + { /* end node */ },
> +};
> +MODULE_DEVICE_TABLE(of, stm32_trig_of_match);
> +
> +static struct platform_driver stm32_timer_trigger_driver = {
> + .probe = stm32_timer_trigger_probe,
> + .driver = {
> + .name = "stm32-timer-trigger",
> + .of_match_table = stm32_trig_of_match,
> + },
> +};
> +module_platform_driver(stm32_timer_trigger_driver);
> +
> +MODULE_ALIAS("platform: stm32-timer-trigger");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 Timer Trigger driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
> index 809b2e7..f2af4fe 100644
> --- a/drivers/iio/trigger/Kconfig
> +++ b/drivers/iio/trigger/Kconfig
> @@ -46,5 +46,4 @@ config IIO_SYSFS_TRIGGER
>
> To compile this driver as a module, choose M here: the
> module will be called iio-trig-sysfs.
> -
Clean this up.
> endmenu
> diff --git a/include/linux/iio/timer/stm32-timer-trigger.h b/include/linux/iio/timer/stm32-timer-trigger.h
> new file mode 100644
> index 0000000..55535ae
> --- /dev/null
> +++ b/include/linux/iio/timer/stm32-timer-trigger.h
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STM32_TIMER_TRIGGER_H_
> +#define _STM32_TIMER_TRIGGER_H_
> +
> +#define TIM1_TRGO "tim1_trgo"
> +#define TIM1_CH1 "tim1_ch1"
> +#define TIM1_CH2 "tim1_ch2"
> +#define TIM1_CH3 "tim1_ch3"
> +#define TIM1_CH4 "tim1_ch4"
> +
> +#define TIM2_TRGO "tim2_trgo"
> +#define TIM2_CH1 "tim2_ch1"
> +#define TIM2_CH2 "tim2_ch2"
> +#define TIM2_CH3 "tim2_ch3"
> +#define TIM2_CH4 "tim2_ch4"
> +
> +#define TIM3_TRGO "tim3_trgo"
> +#define TIM3_CH1 "tim3_ch1"
> +#define TIM3_CH2 "tim3_ch2"
> +#define TIM3_CH3 "tim3_ch3"
> +#define TIM3_CH4 "tim3_ch4"
> +
> +#define TIM4_TRGO "tim4_trgo"
> +#define TIM4_CH1 "tim4_ch1"
> +#define TIM4_CH2 "tim4_ch2"
> +#define TIM4_CH3 "tim4_ch3"
> +#define TIM4_CH4 "tim4_ch4"
> +
> +#define TIM5_TRGO "tim5_trgo"
> +#define TIM5_CH1 "tim5_ch1"
> +#define TIM5_CH2 "tim5_ch2"
> +#define TIM5_CH3 "tim5_ch3"
> +#define TIM5_CH4 "tim5_ch4"
> +
> +#define TIM6_TRGO "tim6_trgo"
> +
> +#define TIM7_TRGO "tim7_trgo"
> +
> +#define TIM8_TRGO "tim8_trgo"
> +#define TIM8_CH1 "tim8_ch1"
> +#define TIM8_CH2 "tim8_ch2"
> +#define TIM8_CH3 "tim8_ch3"
> +#define TIM8_CH4 "tim8_ch4"
> +
> +#define TIM9_TRGO "tim9_trgo"
> +#define TIM9_CH1 "tim9_ch1"
> +#define TIM9_CH2 "tim9_ch2"
> +
> +#define TIM12_TRGO "tim12_trgo"
> +#define TIM12_CH1 "tim12_ch1"
> +#define TIM12_CH2 "tim12_ch2"
> +
> +bool is_stm32_timer_trigger(struct iio_trigger *trig);
> +
> +#endif
>
^ permalink raw reply
* Re: [PATCH] iio: adc: Add Renesas GyroADC driver
From: Peter Meerwald-Stadler @ 2016-12-30 21:09 UTC (permalink / raw)
To: Marek Vasut
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven,
Simon Horman
In-Reply-To: <442f8085-110c-659d-d097-add788b8f291-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> >> Add IIO driver for the Renesas RCar GyroADC block. This block is a
> >> simple 4/8-channel ADC which samples 12/15/24 bits of data every
> >> + if (iio_buffer_enabled(indio_dev))
> >> + return -EBUSY;
> >
> > use iio_device_claim_direct_mode()
>
> Why ? Do I really need the mutex locking here ?
no, not needed, since you do not support buffered mode yet; but neither do
you need iio_buffer_enabled()
p.
--
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
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] iio: adc: Add Renesas GyroADC driver
From: Marek Vasut @ 2016-12-30 20:52 UTC (permalink / raw)
To: Peter Meerwald-Stadler
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven,
Simon Horman
In-Reply-To: <alpine.DEB.2.02.1612302027480.3977-jW+XmwGofnusTnJN9+BGXg@public.gmane.org>
On 12/30/2016 08:50 PM, Peter Meerwald-Stadler wrote:
>
>> Add IIO driver for the Renesas RCar GyroADC block. This block is a
>> simple 4/8-channel ADC which samples 12/15/24 bits of data every
>> cycle from all channels.
>
> comments below
>
>> Signed-off-by: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> Cc: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
>> Cc: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
>> ---
>> .../bindings/iio/adc/renesas,gyroadc.txt | 38 +++
>> MAINTAINERS | 6 +
>> drivers/iio/adc/Kconfig | 10 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/rcar_gyro_adc.c | 379 +++++++++++++++++++++
>> 5 files changed, 434 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> create mode 100644 drivers/iio/adc/rcar_gyro_adc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> new file mode 100644
>> index 0000000..3fd5f57
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> @@ -0,0 +1,38 @@
>> +* Renesas RCar GyroADC device driver
>> +
>> +Required properties:
>> +- compatible: Should be "renesas,rcar-gyroadc" for regular GyroADC or
>> + "renesas,rcar-gyroadc-r8a7792" for GyroADC without interrupt
>> + block found in R8A7792.
>> +- reg: Address and length of the register set for the device
>> +- clocks: References to all the clocks specified in the clock-names
>> + property as specified in
>> + Documentation/devicetree/bindings/clock/clock-bindings.txt.
>> +- clock-names: Shall contain "fck" and "if". The "fck" are the GyroADC block
>
> "fck" is...
>
>> + clock, the "if" are the interface clock.
>
> "if" is ...
>
>> + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
>
> this is just an example and not appropriate here?
Oh, copy-paste error, thanks :)
>> +- power-domains: Must contain a reference to the PM domain, if available.
>> +- renesas,gyroadc-mode: GyroADC mode of operation, must be either of:
>> + 1 - MB88101A mode, 12bit sampling, 4 channels
>> + 2 - ADCS7476 mode, 15bit sampling, 8 channels
>> + 3 - MAX1162 mode, 16bit sampling, 8 channels
>> +- renesas,gyroadc-vref: Array of reference voltage values for each input to
>> + the GyroADC, in uV. Array must have 4 elemenets for
>
> elements
All spelling fixed.
[...]
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 99c0514..4a4cac7 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -408,6 +408,16 @@ config QCOM_SPMI_VADC
>> To compile this driver as a module, choose M here: the module will
>> be called qcom-spmi-vadc.
>>
>> +config RCAR_GYRO_ADC
>> + tristate "Renesas RCAR GyroADC driver"
>> + depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
>> + help
>> + Say yes here to build support for the GyroADC found in Renesas
>> + RCar Gen2 SoCs.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called rcar-gyroadc.
>
> called rcar_gyro_adc?
Why so ? The driver is really named rcar-gyroadc , I guess I should
rename either the file or the driver to keep things consistent. So
probably the file .
>> +
>> config ROCKCHIP_SARADC
>> tristate "Rockchip SARADC driver"
>> depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
[...]
>> +
>> +#define RCAR_GYROADC_CLOCK_LENGTH 0x08
>> +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
>> +
>> +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
>> +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
>> +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
>> +
>> +#define RCAR_GYROADC_FIFO_STATUS 0x70
>> +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
>
> FIFO_STATUS_... is not used (yet)
Is it a problem to have a complete register layout here ?
> 4*ch looks suspicious for ch==8??
Well yes, channel is in range 0..7 :)
>> +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
>> +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
>> +
>> +#define RCAR_GYROADC_INTR 0x74
>> +#define RCAR_GYROADC_INTR_INT BIT(0)
>> +
>> +#define RCAR_GYROADC_INTENR 0x78
>> +#define RCAR_GYROADC_INTENR_INTEN BIT(0)
>> +
>> +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
[...]
>> +#define RCAR_GYROADC_CHAN(_idx, _chan_type, _realbits) { \
>> + .type = (_chan_type), \
>
> _chan_type is IIO_VOLTAGE always?
Yep, fixed.
>> + .indexed = 1, \
>> + .channel = (_idx), \
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
>> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
>> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
>> + .scan_index = (_idx), \
>
> no buffered mode yet, so strictly no need for a scan_index and scan_type
OK
>> + .scan_type = { \
>> + .sign = 'u', \
>> + .realbits = (_realbits), \
>> + .storagebits = 16, \
>> + }, \
>> +}
>> +
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
>> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 12),
>> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 12),
>> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 12),
>> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 12),
>> +};
>> +
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
>> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 15),
>> + RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 15),
>> +};
>> +
>> +/*
>> + * NOTE: The data we receive in mode 3 from MAX1162 have MSByte = 0,
>> + * therefore we only use 16bit realbits here instead of 24.
>> + */
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
>> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 16),
>> + RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 16),
>> +};
>> +
>> +static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + if (chan->type != IIO_VOLTAGE)
>> + return -EINVAL;
>> +
>> + if (iio_buffer_enabled(indio_dev))
>> + return -EBUSY;
>
> use iio_device_claim_direct_mode()
Why ? Do I really need the mutex locking here ?
>> +
>> + *val = readl(priv->regs + datareg);
>> + *val &= BIT(chan->scan_type.realbits) - 1;
>> +
>> + return IIO_VAL_INT;
>> + case IIO_CHAN_INFO_SCALE:
>> + *val = 0;
>> + *val2 = (priv->vref_uv[chan->channel] * 1000) / 0x10000;
>> + return IIO_VAL_INT_PLUS_NANO;
>> + case IIO_CHAN_INFO_SAMP_FREQ:
>> + *val = RCAR_GYROADC_SAMPLE_RATE;
>> + *val2 = 0;
>
> *val2 = 0 not needed
OK
[...]
>> + indio_dev->name = dev_name(dev);
>> + indio_dev->dev.parent = dev;
>> + indio_dev->dev.of_node = pdev->dev.of_node;
>> + indio_dev->info = &rcar_gyroadc_iio_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> + if (mode == 1) {
>
> maybe do the mode differentiation only once, any with a switch?
Done
[...]
--
Best regards,
Marek Vasut
^ permalink raw reply
* Re: [PATCH v6 2/8] MFD: add STM32 Timers driver
From: Jonathan Cameron @ 2016-12-30 20:38 UTC (permalink / raw)
To: Benjamin Gaignard, lee.jones, robh+dt, mark.rutland,
alexandre.torgue, devicetree, linux-kernel, thierry.reding,
linux-pwm, knaack.h, lars, pmeerw, linux-iio, linux-arm-kernel
Cc: fabrice.gasnier, gerald.baeza, arnaud.pouliquen, linus.walleij,
linaro-kernel, Benjamin Gaignard
In-Reply-To: <1481292919-26587-3-git-send-email-benjamin.gaignard@st.com>
On 09/12/16 14:15, Benjamin Gaignard wrote:
> This hardware block could at used at same time for PWM generation
> and IIO timers.
> PWM and IIO timer configuration are mixed in the same registers
> so we need a multi fonction driver to be able to share those registers.
fonction -> function
>
> version 6:
> - rename files to stm32-timers
> - rename functions to stm32_timers_xxx
>
> version 5:
> - fix Lee comments about detect function
> - add missing dependency on REGMAP_MMIO
>
> version 4:
> - add a function to detect Auto Reload Register (ARR) size
> - rename the structure shared with other drivers
>
> version 2:
> - rename driver "stm32-gptimer" to be align with SoC documentation
> - only keep one compatible
> - use of_platform_populate() instead of devm_mfd_add_devices()
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
> ---
> drivers/mfd/Kconfig | 11 ++++++
> drivers/mfd/Makefile | 2 +
> drivers/mfd/stm32-timers.c | 80 ++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/stm32-timers.h | 71 +++++++++++++++++++++++++++++++++++
> 4 files changed, 164 insertions(+)
> create mode 100644 drivers/mfd/stm32-timers.c
> create mode 100644 include/linux/mfd/stm32-timers.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4ec1906 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> in various ST Microelectronics and ST-Ericsson embedded
> Nomadik series.
>
> +config MFD_STM32_TIMERS
> + tristate "Support for STM32 Timers"
> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
> + select MFD_CORE
> + select REGMAP
> + select REGMAP_MMIO
> + help
> + Select this option to enable STM32 timers driver used
> + for PWM and IIO Timer. This driver allow to share the
> + registers between the others drivers.
> +
> menu "Multimedia Capabilities Port drivers"
> depends on ARCH_SA1100
>
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 9834e66..11a52f8 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
> obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
>
> obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
> +
> +obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
> diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c
> new file mode 100644
> index 0000000..68d115e
> --- /dev/null
> +++ b/drivers/mfd/stm32-timers.c
> @@ -0,0 +1,80 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/mfd/stm32-timers.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/reset.h>
> +
> +static const struct regmap_config stm32_timers_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = sizeof(u32),
> + .max_register = 0x400,
> +};
> +
> +static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
> +{
> + /*
> + * Only the available bits will be written so when readback
> + * we get the maximum value of auto reload register
> + */
> + regmap_write(ddata->regmap, TIM_ARR, ~0L);
> + regmap_read(ddata->regmap, TIM_ARR, &ddata->max_arr);
> + regmap_write(ddata->regmap, TIM_ARR, 0x0);
> +}
> +
> +static int stm32_timers_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct stm32_timers *ddata;
> + struct resource *res;
> + void __iomem *mmio;
> +
> + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
> + if (!ddata)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + mmio = devm_ioremap_resource(dev, res);
> + if (IS_ERR(mmio))
> + return PTR_ERR(mmio);
> +
> + ddata->regmap = devm_regmap_init_mmio_clk(dev, "clk_int", mmio,
> + &stm32_timers_regmap_cfg);
> + if (IS_ERR(ddata->regmap))
> + return PTR_ERR(ddata->regmap);
> +
> + ddata->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(ddata->clk))
> + return PTR_ERR(ddata->clk);
> +
> + stm32_timers_get_arr_size(ddata);
> +
> + platform_set_drvdata(pdev, ddata);
> +
> + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
> +}
> +
> +static const struct of_device_id stm32_timers_of_match[] = {
> + { .compatible = "st,stm32-timers", },
> + { /* end node */ },
> +};
> +MODULE_DEVICE_TABLE(of, stm32_timers_of_match);
> +
> +static struct platform_driver stm32_timers_driver = {
> + .probe = stm32_timers_probe,
> + .driver = {
> + .name = "stm32-timers",
> + .of_match_table = stm32_timers_of_match,
> + },
> +};
> +module_platform_driver(stm32_timers_driver);
> +
> +MODULE_DESCRIPTION("STMicroelectronics STM32 Timers");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h
> new file mode 100644
> index 0000000..d030004
> --- /dev/null
> +++ b/include/linux/mfd/stm32-timers.h
> @@ -0,0 +1,71 @@
> +/*
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _LINUX_STM32_GPTIMER_H_
> +#define _LINUX_STM32_GPTIMER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +
> +#define TIM_CR1 0x00 /* Control Register 1 */
> +#define TIM_CR2 0x04 /* Control Register 2 */
> +#define TIM_SMCR 0x08 /* Slave mode control reg */
> +#define TIM_DIER 0x0C /* DMA/interrupt register */
> +#define TIM_SR 0x10 /* Status register */
> +#define TIM_EGR 0x14 /* Event Generation Reg */
> +#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */
> +#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */
> +#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */
> +#define TIM_PSC 0x28 /* Prescaler */
> +#define TIM_ARR 0x2c /* Auto-Reload Register */
> +#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */
> +#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */
> +#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */
> +#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */
> +#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
> +
> +#define TIM_CR1_CEN BIT(0) /* Counter Enable */
> +#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */
> +#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */
> +#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
> +#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */
> +#define TIM_DIER_UIE BIT(0) /* Update interrupt */
> +#define TIM_SR_UIF BIT(0) /* Update interrupt flag */
> +#define TIM_EGR_UG BIT(0) /* Update Generation */
> +#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */
> +#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */
> +#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */
> +#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */
> +#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */
> +#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */
> +#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */
> +#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */
> +#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */
> +#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12))
> +#define TIM_BDTR_BKE BIT(12) /* Break input enable */
> +#define TIM_BDTR_BKP BIT(13) /* Break input polarity */
> +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */
> +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
> +#define TIM_BDTR_BKF (BIT(16) | BIT(17) | BIT(18) | BIT(19))
> +#define TIM_BDTR_BK2F (BIT(20) | BIT(21) | BIT(22) | BIT(23))
> +#define TIM_BDTR_BK2E BIT(24) /* Break 2 input enable */
> +#define TIM_BDTR_BK2P BIT(25) /* Break 2 input polarity */
> +
> +#define MAX_TIM_PSC 0xFFFF
> +#define TIM_CR2_MMS_SHIFT 4
> +#define TIM_SMCR_TS_SHIFT 4
> +#define TIM_BDTR_BKF_MASK 0xF
> +#define TIM_BDTR_BKF_SHIFT 16
> +#define TIM_BDTR_BK2F_SHIFT 20
> +
> +struct stm32_timers {
> + struct clk *clk;
> + struct regmap *regmap;
> + u32 max_arr;
> +};
> +#endif
>
^ permalink raw reply
* Re: [PATCH v9 8/8] drivers:input:tsc2007: add iio interface to read external ADC input and temperature
From: Jonathan Cameron @ 2016-12-30 20:36 UTC (permalink / raw)
To: H. Nikolaus Schaller, Sebastian Reichel, Dmitry Torokhov,
Mark Rutland, Benoît Cousson, Tony Lindgren, Russell King,
Arnd Bergmann, Michael Welling, Mika Penttilä,
Javier Martinez Canillas, Igor Grinberg, Andrew F. Davis,
Mark Brown, Rob Herring, Alexander Stein, Eric Engestrom,
Hans de Goede
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA,
letux-kernel-S0jZdbWzriLCfDggNXIi3w,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
kernel-Jl6IXVxNIMRxAtABVqVhTwC/G2K4zDHf
In-Reply-To: <17a94568ffb91abedc9d12896b602022abb5f7e8.1482936802.git.hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
On 28/12/16 14:53, 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.
>
> This patch also splits the tsc2007 driver in several source files:
> tsc2007.h -- constants, structs and stubs
> tsc2007_core.c -- functional parts of the original driver
> tsc2007_iio.c -- the optional iio stuff
>
> Makefile magic allows to conditionally link the iio stuff
> if CONFIG_IIO=y or =m in a way that it works with
> CONFIG_TOUCHSCREEN_TSC2007=m.
>
> Signed-off-by: H. Nikolaus Schaller <hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
> Reviewed-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Just for the record, I'm happy with the few changes from earlier versions.
Jonathan
> ---
> drivers/input/touchscreen/Kconfig | 10 ++
> drivers/input/touchscreen/Makefile | 2 +
> drivers/input/touchscreen/tsc2007.h | 116 ++++++++++++++++
> .../touchscreen/{tsc2007.c => tsc2007_core.c} | 95 +++----------
> drivers/input/touchscreen/tsc2007_iio.c | 150 +++++++++++++++++++++
> 5 files changed, 299 insertions(+), 74 deletions(-)
> create mode 100644 drivers/input/touchscreen/tsc2007.h
> rename drivers/input/touchscreen/{tsc2007.c => tsc2007_core.c} (86%)
> create mode 100644 drivers/input/touchscreen/tsc2007_iio.c
>
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index efca013..1616a8d 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -1035,6 +1035,16 @@ config TOUCHSCREEN_TSC2007
> To compile this driver as a module, choose M here: the
> module will be called tsc2007.
>
> +config TOUCHSCREEN_TSC2007_IIO
> + bool "IIO interface for external ADC input and temperature"
> + depends on TOUCHSCREEN_TSC2007
> + depends on IIO=y || IIO=TOUCHSCREEN_TSC2007
> + help
> + Saying Y here adds an iio interface to the tsc2007 which
> + provides values for the AUX input (used for e.g. battery
> + or ambient light monitoring), temperature and raw input
> + values.
> +
> config TOUCHSCREEN_W90X900
> tristate "W90P910 touchscreen driver"
> depends on ARCH_W90X900
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 81b8645..05d1cc8 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -80,6 +80,8 @@ obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
> obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
> +tsc2007-y := tsc2007_core.o
> +tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO) += tsc2007_iio.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
> obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
> obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
> diff --git a/drivers/input/touchscreen/tsc2007.h b/drivers/input/touchscreen/tsc2007.h
> new file mode 100644
> index 0000000..16efb60
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2007.h
> @@ -0,0 +1,116 @@
> +/*
> + * Copyright (c) 2008 MtekVision Co., Ltd.
> + * Kwangwoo Lee <kwlee-ec7hoAtq5SbSUeElwK9/Pw@public.gmane.org>
> + *
> + * Using code from:
> + * - ads7846.c
> + * Copyright (c) 2005 David Brownell
> + * Copyright (c) 2006 Nokia Corporation
> + * - corgi_ts.c
> + * Copyright (C) 2004-2005 Richard Purdie
> + * - omap_ts.[hc], ads7846.h, ts_osk.c
> + * Copyright (C) 2002 MontaVista Software
> + * Copyright (C) 2004 Texas Instruments
> + * Copyright (C) 2005 Dirk Behme
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/input/touchscreen.h>
> +
> +#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
> +#define TSC2007_MEASURE_AUX (0x2 << 4)
> +#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
> +#define TSC2007_ACTIVATE_XN (0x8 << 4)
> +#define TSC2007_ACTIVATE_YN (0x9 << 4)
> +#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
> +#define TSC2007_SETUP (0xb << 4)
> +#define TSC2007_MEASURE_X (0xc << 4)
> +#define TSC2007_MEASURE_Y (0xd << 4)
> +#define TSC2007_MEASURE_Z1 (0xe << 4)
> +#define TSC2007_MEASURE_Z2 (0xf << 4)
> +
> +#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
> +#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
> +#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
> +#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
> +
> +#define TSC2007_12BIT (0x0 << 1)
> +#define TSC2007_8BIT (0x1 << 1)
> +
> +#define MAX_12BIT ((1 << 12) - 1)
> +
> +#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
> +
> +#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
> +#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
> +#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
> +#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
> +#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
> +
> +struct ts_event {
> + u16 x;
> + u16 y;
> + u16 z1, z2;
> +};
> +
> +struct tsc2007 {
> + struct input_dev *input;
> + char phys[32];
> +
> + struct i2c_client *client;
> +
> + u16 model;
> + u16 x_plate_ohms;
> +
> + struct touchscreen_properties prop;
> +
> + bool report_resistance;
> + u16 min_x;
> + u16 min_y;
> + u16 max_x;
> + u16 max_y;
> + u16 max_rt;
> + unsigned long poll_period; /* in jiffies */
> + int fuzzx;
> + int fuzzy;
> + int fuzzz;
> +
> + unsigned int gpio;
> + int irq;
> +
> + wait_queue_head_t wait;
> + bool stopped;
> + bool pendown;
> +
> + int (*get_pendown_state)(struct device *);
> + void (*clear_penirq)(void);
> +
> + struct mutex mlock;
> + struct iio_dev *iio_dev; /* optional */
> +};
> +
> +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd);
> +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> + struct ts_event *tc);
> +bool tsc2007_is_pen_down(struct tsc2007 *ts);
> +
> +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2007_IIO)
> +
> +/* defined in tsc2007_iio.c */
> +int tsc2007_iio_configure(struct tsc2007 *ts);
> +void tsc2007_iio_unconfigure(struct tsc2007 *ts);
> +
> +#else /* CONFIG_TOUCHSCREEN_TSC2007_IIO */
> +
> +static inline int tsc2007_iio_configure(struct tsc2007 *ts)
> +{
> + return 0;
> +}
> +static inline void tsc2007_iio_unconfigure(struct tsc2007 *ts)
> +{
> +}
> +
> +#endif /* CONFIG_TOUCHSCREEN_TSC2007_IIO */
> diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007_core.c
> similarity index 86%
> rename from drivers/input/touchscreen/tsc2007.c
> rename to drivers/input/touchscreen/tsc2007_core.c
> index 76b462b..812ded8 100644
> --- a/drivers/input/touchscreen/tsc2007.c
> +++ b/drivers/input/touchscreen/tsc2007_core.c
> @@ -27,79 +27,11 @@
> #include <linux/i2c.h>
> #include <linux/i2c/tsc2007.h>
> #include <linux/of_device.h>
> -#include <linux/of.h>
> #include <linux/of_gpio.h>
> -#include <linux/input/touchscreen.h>
> -
> -#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
> -#define TSC2007_MEASURE_AUX (0x2 << 4)
> -#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
> -#define TSC2007_ACTIVATE_XN (0x8 << 4)
> -#define TSC2007_ACTIVATE_YN (0x9 << 4)
> -#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
> -#define TSC2007_SETUP (0xb << 4)
> -#define TSC2007_MEASURE_X (0xc << 4)
> -#define TSC2007_MEASURE_Y (0xd << 4)
> -#define TSC2007_MEASURE_Z1 (0xe << 4)
> -#define TSC2007_MEASURE_Z2 (0xf << 4)
> -
> -#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
> -#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
> -#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
> -#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
> -
> -#define TSC2007_12BIT (0x0 << 1)
> -#define TSC2007_8BIT (0x1 << 1)
> -
> -#define MAX_12BIT ((1 << 12) - 1)
> -
> -#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
> -
> -#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
> -#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
> -#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
> -#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
> -#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
> -
> -struct ts_event {
> - u16 x;
> - u16 y;
> - u16 z1, z2;
> -};
> -
> -struct tsc2007 {
> - struct input_dev *input;
> - char phys[32];
> -
> - struct i2c_client *client;
> -
> - u16 model;
> - u16 x_plate_ohms;
> -
> - struct touchscreen_properties prop;
> -
> - bool report_resistance;
> - u16 min_x;
> - u16 min_y;
> - u16 max_x;
> - u16 max_y;
> - u16 max_rt;
> - unsigned long poll_period; /* in jiffies */
> - int fuzzx;
> - int fuzzy;
> - int fuzzz;
> -
> - unsigned gpio;
> - int irq;
> -
> - wait_queue_head_t wait;
> - bool stopped;
> +#include "tsc2007.h"
>
> - int (*get_pendown_state)(struct device *);
> - void (*clear_penirq)(void);
> -};
>
> -static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
> +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
> {
> s32 data;
> u16 val;
> @@ -137,7 +69,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
> tsc2007_xfer(tsc, PWRDOWN);
> }
>
> -static u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> struct ts_event *tc)
> {
> u32 rt = 0;
> @@ -158,7 +90,7 @@ static u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> return rt;
> }
>
> -static bool tsc2007_is_pen_down(struct tsc2007 *ts)
> +bool tsc2007_is_pen_down(struct tsc2007 *ts)
> {
> /*
> * NOTE: We can't rely on the pressure to determine the pen down
> @@ -191,7 +123,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);
>
> @@ -441,7 +376,8 @@ static void tsc2007_call_exit_platform_hw(void *data)
> static int tsc2007_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> - const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
> + const struct tsc2007_platform_data *pdata =
> + dev_get_platdata(&client->dev);
> struct tsc2007 *ts;
> struct input_dev *input_dev;
> int err;
> @@ -463,7 +399,9 @@ static int tsc2007_probe(struct i2c_client *client,
> 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));
> @@ -534,7 +472,7 @@ static int tsc2007_probe(struct i2c_client *client,
> if (err < 0) {
> dev_err(&client->dev,
> "Failed to setup chip: %d\n", err);
> - return err; /* usually, chip does not respond */
> + return err; /* chip does not respond */
> }
>
> err = input_register_device(input_dev);
> @@ -544,6 +482,14 @@ static int tsc2007_probe(struct i2c_client *client,
> return err;
> }
>
> + return tsc2007_iio_configure(ts);
> +}
> +
> +static int tsc2007_remove(struct i2c_client *client)
> +{
> + struct tsc2007 *ts = i2c_get_clientdata(client);
> +
> + tsc2007_iio_unconfigure(ts);
> return 0;
> }
>
> @@ -569,6 +515,7 @@ static struct i2c_driver tsc2007_driver = {
> },
> .id_table = tsc2007_idtable,
> .probe = tsc2007_probe,
> + .remove = tsc2007_remove,
> };
>
> module_i2c_driver(tsc2007_driver);
> diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c
> new file mode 100644
> index 0000000..ed79944
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2007_iio.c
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG
> + * Nikolaus Schaller <hns-xXXSsgcRVICgSpxsJD1C4w@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.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include "tsc2007.h"
> +
> +struct tsc2007_iio {
> + struct tsc2007 *ts;
> +};
> +
> +#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_iio *iio = iio_priv(indio_dev);
> + struct tsc2007 *tsc = iio->ts;
> + 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,
> +};
> +
> +int tsc2007_iio_configure(struct tsc2007 *ts)
> +{
> + int err;
> + struct iio_dev *indio_dev;
> + struct tsc2007_iio *iio;
> +
> + indio_dev = devm_iio_device_alloc(&ts->client->dev,
> + sizeof(struct tsc2007_iio));
> + if (!indio_dev) {
> + dev_err(&ts->client->dev, "iio_device_alloc failed\n");
> + return -ENOMEM;
> + }
> +
> + iio = iio_priv(indio_dev);
> + iio->ts = ts;
> + ts->iio_dev = (void *) indio_dev;
> +
> + indio_dev->name = "tsc2007";
> + indio_dev->dev.parent = &ts->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(&ts->client->dev, "iio_device_register() failed: %d\n",
> + err);
> + return err;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(tsc2007_iio_configure);
> +
> +void tsc2007_iio_unconfigure(struct tsc2007 *ts)
> +{
> + struct iio_dev *indio_dev = ts->iio_dev;
> +
> + iio_device_unregister(indio_dev);
> +}
> +EXPORT_SYMBOL(tsc2007_iio_unconfigure);
>
--
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 v2 3/4] ARM64: dts: exynos5433: use macros for pinctrl configuration on Exynos5433
From: Andi Shyti @ 2016-12-30 20:28 UTC (permalink / raw)
To: Linus Walleij
Cc: Andi Shyti, Chanwoo Choi, Tomasz Figa, Krzysztof Kozlowski,
Sylwester Nawrocki, Rob Herring, Mark Rutland, Catalin Marinas,
Will Deacon, Kukjin Kim, Javier Martinez Canillas,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-samsung-soc, linux-kernel@vger.kernel.org, stable,
Andi Shyti
In-Reply-To: <CACRpkdaz9AyFJs1Th_SVexqKaP-=iPF0G-1YXOCoSmrQH8ZHDA@mail.gmail.com>
Hi Linus,
> > Use the macros defined in include/dt-bindings/pinctrl/samsung.h
> > instead of hardcoded values.
> >
> > Signed-off-by: Andi Shyti <andi.shyti@samsung.com>
>
> These look fine, but that this and the other DTS patch through ARM SoC.
>
> If you also need the headerfile patch (patch 2) to go through ARM SoC
> that is fine,
> I can take it out of pinctrl if you want.
yes, sure... no problem from my side :)
Thanks,
Andi
^ permalink raw reply
* Re: [PATCH v2 1/2] devicetree: add Garmin vendor prefix
From: Jonathan Cameron @ 2016-12-30 20:26 UTC (permalink / raw)
To: Matt Ranostay, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Rob Herring
In-Reply-To: <20161229051306.28547-1-matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
On 29/12/16 05:13, Matt Ranostay wrote:
> Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
Rob, just to check - is this the right way to go with prefixes?
It's awfully ugly in this case ;)
Jonathan
> ---
> Changes from v1:
> * switch to stock ticker for Garmin Limited
>
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 16d3b5e7f5d1..5749bfc5fc5b 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -107,6 +107,7 @@ firefly Firefly
> focaltech FocalTech Systems Co.,Ltd
> friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
> fsl Freescale Semiconductor
> +grmn Garmin Limited
> ge General Electric Company
> geekbuying GeekBuying
> gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
>
--
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/2] cfg80211: reg: support ieee80211-(min|max)-center-freq DT properties
From: Arend van Spriel @ 2016-12-30 20:20 UTC (permalink / raw)
To: Rafał Miłecki
Cc: Kalle Valo,
linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Martin Blumenstingl, Felix Fietkau, Arnd Bergmann,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Rafał Miłecki
In-Reply-To: <CACna6rwaUWEjpBdfXS6uJSxKXH_mCP7YMGd1KaJropNQgVS7PA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 29-12-16 10:43, Rafał Miłecki wrote:
> On 29 December 2016 at 09:57, Arend van Spriel
> <arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
>> On 28-12-16 22:30, Rafał Miłecki wrote:
>>> On 28 December 2016 at 22:28, Rafał Miłecki <zajec5-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>>> On 28 December 2016 at 22:07, Arend van Spriel
>>>> <arend.vanspriel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> wrote:
>>>>> On 28-12-16 16:59, Rafał Miłecki wrote:
>>>>>> From: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>>>>>
>>>>>> They allow specifying hardware limitations of supported channels. This
>>>>>> may be useful for specifying single band devices or devices that support
>>>>>> only some part of the whole band.
>>>>>> E.g. some tri-band routers have separated radios for lower and higher
>>>>>> part of 5 GHz band.
>>>>>>
>>>>>> Signed-off-by: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>>>>> ---
>>>>>> net/wireless/reg.c | 34 ++++++++++++++++++++++++++++++++++
>>>>>> 1 file changed, 34 insertions(+)
>>>>>>
>>>>>> diff --git a/net/wireless/reg.c b/net/wireless/reg.c
>>>>>> index 5dbac37..35ba5c7 100644
>>>>>> --- a/net/wireless/reg.c
>>>>>> +++ b/net/wireless/reg.c
>>>>>> @@ -1123,6 +1123,26 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
>>>>>> }
>>>>>> EXPORT_SYMBOL(reg_initiator_name);
>>>>>>
>>>>>> +static bool reg_center_freq_of_valid(struct wiphy *wiphy,
>>>>>> + struct ieee80211_channel *chan)
>>>>>> +{
>>>>>> + struct device_node *np = wiphy_dev(wiphy)->of_node;
>>>>>> + u32 val;
>>>>>> +
>>>>>> + if (!np)
>>>>>> + return true;
>>>>>> +
>>>>>> + if (!of_property_read_u32(np, "ieee80211-min-center-freq", &val) &&
>>>>>> + chan->center_freq < KHZ_TO_MHZ(val))
>>>>>> + return false;
>>>>>> +
>>>>>> + if (!of_property_read_u32(np, "ieee80211-max-center-freq", &val) &&
>>>>>> + chan->center_freq > KHZ_TO_MHZ(val))
>>>>>> + return false;
>>>>>
>>>>> I suspect these functions rely on CONFIG_OF. So might not build for
>>>>> !CONFIG_OF.
>>>>
>>>> I compiled it with
>>>> # CONFIG_OF is not set
>>>>
>>>> Can you test it and provide a non-working config if you see a
>>>> compilation error, please?
>>>
>>> include/linux/of.h provides a lot of dummy static inline functions if
>>> CONFIG_OF is not used (they also allow compiler to drop most of the
>>> code).
>>
>> of_propeirty_read_u32 is static inline in of.h calling
>> of_property_read_u32_array, which has a dummy variant in of.h returning
>> -ENOSYS so -38. Pretty sure that is not what you want. At least it does
>> not allow the compiler to drop any code so probably better to do:
>>
>> if (!IS_ENABLED(CONFIG_OF) || !np)
>> return true;
>
> Please verify that using a compiler. If there is a problem I'll be
> happy to work on it, but I need a proof it exists.
I am on vacation right now so not having much more than email and web
browser to use as review reference.
> If compilers sees a:
> if (!-ENOSYS && chan->center_freq < KHZ_TO_MHZ(val))
> condition, it's pretty clear it can be dropped. With both conditional
> blocks dropped function always returns "true" and... can be dropped.
>
> Let me see if I can convince you with the following test:
No need to convince me. I made a mistake reviewing the code. Thanks for
clarifying it.
> $ grep -m 1 CONFIG_OF .config
> # CONFIG_OF is not set
> $ objdump --syms net/wireless/reg.o | grep -c reg_center_freq_of_valid
> 0
>
> $ grep -m 1 CONFIG_OF .config
> CONFIG_OF=y
> $ objdump --syms net/wireless/reg.o | grep -c reg_center_freq_of_valid
> 1
>
>
>> So with this patch you change the channel to DISABLED. I am not very
>> familiar with reg.c so do you know if this is done before or after
>> calling regulatory notifier in the driver. brcmfmac will enable channels
>> querying the device upon regulatory notifier call, which may undo the
>> behavior introduced by your patch.
>
> I'm not regulatory export, so I hope someone will review this patch.
> So far I can say it works for me after trying it on SR400ac with
> BCM43602.
But you probably do not have a mapping table for mapping country code
received in notifier to firmware regulatory code/revision. Only if you
have that, brcmfmac will update the channels in the bands.
Giving this some more consideration I am not sure if this is the proper
place to handle this. ieee80211-(min|max)-center-freq is platform
specific configuration allowing multiple cards to be used in different
(sub)bands. This has nothing to do with regulatory. So probably better
to move it to core.c or chan.c.
Regards,
Arend
--
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 v8 3/8] drivers:input:tsc2007: add iio interface to read external ADC input and temperature
From: Jonathan Cameron @ 2016-12-30 20:17 UTC (permalink / raw)
To: H. Nikolaus Schaller, Dmitry Torokhov
Cc: Jonathan Cameron, Sebastian Reichel, Mark Rutland,
Benoît Cousson, Tony Lindgren, Russell King, Arnd Bergmann,
Michael Welling, Mika Penttilä, Javier Martinez Canillas,
Igor Grinberg, Andrew F. Davis, Mark Brown, Rob Herring,
Alexander Stein, Eric Engestrom, Hans de Goede,
Benjamin Tissoires
In-Reply-To: <FA2666F0-0C5B-458E-97B5-83243F26D0DE@goldelico.com>
On 28/12/16 14:52, H. Nikolaus Schaller wrote:
> Hi Dmitry,
>
>> Am 27.12.2016 um 22:54 schrieb Dmitry Torokhov <dmitry.torokhov@gmail.com>:
>>
>> On Mon, Dec 12, 2016 at 10:21:25PM +0100, H. Nikolaus Schaller wrote:
>>> Hi,
>>>
>>>
>>>> Am 27.11.2016 um 16:47 schrieb H. Nikolaus Schaller <hns@goldelico.com>:
>>>>
>>>> Hi Jonathan,
>>>>
>>>>> Am 27.11.2016 um 12:02 schrieb Jonathan Cameron <jic23@kernel.org>:
>>>>>
>>>>> On 24/11/16 18:05, H. Nikolaus Schaller wrote:
>>>>>>
>>>>>>> Am 24.11.2016 um 18:38 schrieb Jonathan Cameron <jic23@jic23.retrosnub.co.uk>:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 22 November 2016 14:02:30 GMT+00:00, "H. Nikolaus Schaller" <hns@goldelico.com> wrote:
>>>
>>>>
>>>>> - hence cc'd Yann and the Kbuild list
>>>>> to see if they can offer some advices.
>>>
>>> no response / advice so far.
>>
>> Since you are saying that IIO stuff is optional, add it to Kconfig
>> explicitly:
>>
>> config "TOUCHSCREEN_TSC2007_IIO"
>> bool "IIO interface for external ADC input and temperature"
>> depends on TOUCHSCREEN_TSC2007
>> depends on IIO=y || IIO=TOUCHSCREEN_TSC2007
>> help
>> ...
>>
>> and use this symbols in makefile:
>>
>> and in Makefile:
>>
>> obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
>> tsc2007-y := tsc2007-core.o ...
>> tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO) += tsc2007_iio.o
>
> Ah, ok. Well, we tried to make it without an explicit config option
> but it does not hurt to have one.
>
> Your proposal works fine for 3 out of the 4 possible combinations.
>
> I think it is unlikely that the combination TOUCHSCREEN_TSC2007=y IIO=m
> is needed at all (we configure as much as possible as =m in our kernel).
Indeed - that shouldn't work and is blocked by the above which is good.
>
> Patch v9 will come next (where I have also moved this iio driver
> patch to be 8/8).
Wise move ;)
>
> BR and thanks,
> Nikolaus
>
^ permalink raw reply
* Re: [PATCH v4 1/5] devicetree: mfd: Add binding for the TI LM3533
From: Jonathan Cameron @ 2016-12-30 19:50 UTC (permalink / raw)
To: Bjorn Andersson, Rob Herring, Mark Rutland
Cc: Lee Jones, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Richard Purdie, Jacek Anaszewski,
Pavel Machek, Jingoo Han, devicetree, linux-kernel, linux-iio,
linux-leds, Bjorn Andersson
In-Reply-To: <20161226181153.11271-1-bjorn.andersson@linaro.org>
On 26/12/16 18:11, Bjorn Andersson wrote:
> From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
>
> Add the binding for the Texas Instruments LM3533 lighting power
> solution.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>
> I had Acks from Jonathan and Rob on v3, but dropped these due to the added
> compatible properties.
Looks sensible to me
Acked-by: Jonathan Cameron <jic23@kernel.org>
>
> Changes since v3:
> - Added compatible to sub-nodes, per Lee's requested to treat them as separate
> pieces.
>
> Documentation/devicetree/bindings/mfd/lm3533.txt | 205 +++++++++++++++++++++++
> 1 file changed, 205 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/lm3533.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/lm3533.txt b/Documentation/devicetree/bindings/mfd/lm3533.txt
> new file mode 100644
> index 000000000000..909281096ba2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/lm3533.txt
> @@ -0,0 +1,205 @@
> +Texas Instruments LM3533 binding
> +
> +This binding describes the Texas Instruments LM3533, a lighting power solution
> +for smartphone handsets. The common properties are described directly in the
> +node, while each individual component are described in an optional subnode.
> +
> +- compatible:
> + Usage: required
> + Value type: <stringlist>
> + Definition: must be:
> + "ti,lm3533"
> +
> +- reg:
> + Usage: required
> + Value type: <u32>
> + Definition: i2c address of the LM3533 chip
> +
> +- als-supply:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: reference to regulator powering the V_als input; as
> + specified in "../regulator/regulator.txt"
> +
> +- hwen-gpios:
> + Usage: required
> + Value type: <prop-encoded-array>
> + Definition: reference to gpio pin connected to the HWEN input; as
> + specified in "../gpio/gpio.txt"
> +
> +- ti,boost-freq-hz:
> + Usage: required
> + Value type: <u32>
> + Definition: switch-frequency of the boost converter, must be either:
> + 500000 or 1000000
> +
> +- ti,boost-ovp-mv:
> + Usage: required
> + Value type: <u32>
> + Definition: over-voltage protection limit, in mV. Must be one of:
> + 16000, 24000, 32000 or 40000
> +
> +- #address-cells:
> + Usage: required
> + Value type: <u32>
> + Definition: must be 1
> +
> +- #size-cells:
> + Usage: required
> + Value type: <u32>
> + Definition: must be 0
> +
> += ALS SUBNODE
> +The ambient light sensor subnode carrying the light sensor related properties.
> +
> +- compatible:
> + Usage: required
> + Value type: <stringlist>
> + Definition: must be:
> + "ti,lm3533-als"
> +
> +- ti,als-resistance-ohm:
> + Usage: required (unless ti,pwm-mode is specified)
> + Value type: <u32>
> + Definition: specifies the resistor value (R_als), in Ohm. Valid values
> + ranges from 200000 to 1574 Ohm.
> +
> +- ti,pwm-mode:
> + Usage: optional
> + Value type: <empty>
> + Definition: specifies, if present, that the als should operate in PWM
> + mode - rather than analog mode
> +
> += BACKLIGHT NODES
> +Backlight subnodes carrying the backlight related properties.
> +
> +- compatible:
> + Usage: required
> + Value type: <stringlist>
> + Definition: must be:
> + "ti,lm3533-backlight"
> +
> +- reg:
> + Usage: required
> + Value type: <u32>
> + Definition: specifies which of the two backlights this node corresponds
> + to
> +
> +- default-brightness:
> + Usage: optional
> + Value type: <32>
> + Definition: specifies the default brightness for the backlight, in
> + units of brightness [0-255]
> +
> +- label:
> + Usage: required
> + Value type: <string>
> + Definition: specifies a name of this backlight
> +
> +- led-max-microamp:
> + Usage: required
> + Value type: <u32>
> + Definition: specifies the max current for this backlight, in uA, as
> + described in "../leds/common.txt"
> +
> +- ti,pwm-zones:
> + Usage: optional
> + Value type: <u32 list>
> + Definition: lists the ALS zones to be PWM controlled for this backlight,
> + the values in the list are in the range [0 - 4]
> +
> += LED NODES
> +LED subnodes carrying the LED related properties.
> +
> +- compatible:
> + Usage: required
> + Value type: <stringlist>
> + Definition: must be:
> + "ti,lm3533-led"
> +
> +- reg:
> + Usage: required
> + Value type: <u32>
> + Definition: specifies which of the four LEDs this node corresponds to
> +
> +- linux,default-trigger:
> + Usage: optional
> + Value type: <string>
> + Definition: specifies the default trigger for the LED, as described in
> + "../leds/common.txt"
> +
> +- label:
> + Usage: required
> + Value type: <string>
> + Definition: specifies a name of this LED, as described in
> + "../leds/common.txt"
> +
> +- led-max-microamp:
> + Usage: required
> + Value type: <u32>
> + Definition: specifies the max current for this LED, in uA, as described
> + in "../leds/common.txt"
> +
> +- ti,pwm-zones:
> + Usage: optional
> + Value type: <u32 list>
> + Definition: lists the ALS zones to be PWM controlled for this LED, the
> + values in the list are in the range [0 - 4]
> +
> += EXAMPLE
> +
> +i2c@12460000 {
> + compatible = "qcom,i2c-qup-v1.1.1";
> + ...
> +
> + lm3533@36 {
> + compatible = "ti,lm3533";
> + reg = <0x36>;
> +
> + als-supply = <&pm8921_l11>;
> + hwen-gpios = <&pm8921_gpio 26 GPIO_ACTIVE_HIGH>;
> +
> + ti,boost-freq-hz = <500000>;
> + ti,boost-ovp-mv = <24000>;
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + als {
> + compatible = "ti,lm3533-als";
> + ti,als-resistance-ohm = <200000>;
> + };
> +
> + backlight@0 {
> + compatible = "ti,lm3533-backlight";
> + reg = <0>;
> + label = "backlight";
> +
> + led-max-microamp = <20200>;
> + };
> +
> + led@0 {
> + compatible = "ti,lm3533-led";
> + reg = <0>;
> + label = "red";
> +
> + led-max-microamp = <5000>;
> + };
> +
> + led@1 {
> + compatible = "ti,lm3533-led";
> + reg = <1>;
> + label = "green";
> +
> + led-max-microamp = <5000>;
> + };
> +
> + led@2 {
> + compatible = "ti,lm3533-led";
> + reg = <2>;
> + label = "blue";
> +
> + led-max-microamp = <5000>;
> + };
> + };
> +
>
^ permalink raw reply
* Re: [PATCH] iio: adc: Add Renesas GyroADC driver
From: Peter Meerwald-Stadler @ 2016-12-30 19:50 UTC (permalink / raw)
To: Marek Vasut
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven,
Simon Horman
In-Reply-To: <20161230191800.2532-1-marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Add IIO driver for the Renesas RCar GyroADC block. This block is a
> simple 4/8-channel ADC which samples 12/15/24 bits of data every
> cycle from all channels.
comments below
> Signed-off-by: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
> Cc: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
> ---
> .../bindings/iio/adc/renesas,gyroadc.txt | 38 +++
> MAINTAINERS | 6 +
> drivers/iio/adc/Kconfig | 10 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/rcar_gyro_adc.c | 379 +++++++++++++++++++++
> 5 files changed, 434 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
> create mode 100644 drivers/iio/adc/rcar_gyro_adc.c
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
> new file mode 100644
> index 0000000..3fd5f57
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
> @@ -0,0 +1,38 @@
> +* Renesas RCar GyroADC device driver
> +
> +Required properties:
> +- compatible: Should be "renesas,rcar-gyroadc" for regular GyroADC or
> + "renesas,rcar-gyroadc-r8a7792" for GyroADC without interrupt
> + block found in R8A7792.
> +- reg: Address and length of the register set for the device
> +- clocks: References to all the clocks specified in the clock-names
> + property as specified in
> + Documentation/devicetree/bindings/clock/clock-bindings.txt.
> +- clock-names: Shall contain "fck" and "if". The "fck" are the GyroADC block
"fck" is...
> + clock, the "if" are the interface clock.
"if" is ...
> + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
this is just an example and not appropriate here?
> +- power-domains: Must contain a reference to the PM domain, if available.
> +- renesas,gyroadc-mode: GyroADC mode of operation, must be either of:
> + 1 - MB88101A mode, 12bit sampling, 4 channels
> + 2 - ADCS7476 mode, 15bit sampling, 8 channels
> + 3 - MAX1162 mode, 16bit sampling, 8 channels
> +- renesas,gyroadc-vref: Array of reference voltage values for each input to
> + the GyroADC, in uV. Array must have 4 elemenets for
elements
> + mode 1 and 8 elements for mode 2 and 3.
> +
> +Example:
> + &adc {
> + compatible = "renesas,rcar-gyroadc";
> + reg = <0 0xe6e54000 0 64>;
> + clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>;
> + clock-names = "fck", "if";
> + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
> +
> + pinctrl-0 = <&adc_pins>;
> + pinctrl-names = "default";
> +
> + renesas,gyroadc-vref = <4096000 4096000 4096000 4096000
> + 4096000 4096000 4096000 4096000>;
> + renesas,gyroadc-mode = <3>;
> + status = "okay";
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 162d904..751e760 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10271,6 +10271,12 @@ L: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> F: drivers/net/ethernet/renesas/
> F: include/linux/sh_eth.h
>
> +RENESAS RCAR GYROADC DRIVER
> +M: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> +L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +S: Supported
> +F: drivers/iio/adc/rcar_gyro_adc.c
> +
> RENESAS USB2 PHY DRIVER
> M: Yoshihiro Shimoda <yoshihiro.shimoda.uh-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> L: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 99c0514..4a4cac7 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -408,6 +408,16 @@ config QCOM_SPMI_VADC
> To compile this driver as a module, choose M here: the module will
> be called qcom-spmi-vadc.
>
> +config RCAR_GYRO_ADC
> + tristate "Renesas RCAR GyroADC driver"
> + depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
> + help
> + Say yes here to build support for the GyroADC found in Renesas
> + RCar Gen2 SoCs.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called rcar-gyroadc.
called rcar_gyro_adc?
> +
> config ROCKCHIP_SARADC
> tristate "Rockchip SARADC driver"
> depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..253aeb2 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
> obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
> obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
> +obj-$(CONFIG_RCAR_GYRO_ADC) += rcar_gyro_adc.o
> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
> obj-$(CONFIG_STX104) += stx104.o
> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> diff --git a/drivers/iio/adc/rcar_gyro_adc.c b/drivers/iio/adc/rcar_gyro_adc.c
> new file mode 100644
> index 0000000..a74b148
> --- /dev/null
> +++ b/drivers/iio/adc/rcar_gyro_adc.c
> @@ -0,0 +1,379 @@
> +/*
> + * Renesas RCar GyroADC driver
> + *
> + * Copyright 2016 Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@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 as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/of_platform.h>
> +#include <linux/err.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +
> +/* GyroADC registers. */
> +#define RCAR_GYROADC_MODE_SELECT 0x00
> +#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0
> +#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1
> +#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3
> +
> +#define RCAR_GYROADC_START_STOP 0x04
> +#define RCAR_GYROADC_START_STOP_START BIT(0)
> +
> +#define RCAR_GYROADC_CLOCK_LENGTH 0x08
> +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
> +
> +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
> +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
> +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
> +
> +#define RCAR_GYROADC_FIFO_STATUS 0x70
> +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
FIFO_STATUS_... is not used (yet)
4*ch looks suspicious for ch==8??
> +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
> +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
> +
> +#define RCAR_GYROADC_INTR 0x74
> +#define RCAR_GYROADC_INTR_INT BIT(0)
> +
> +#define RCAR_GYROADC_INTENR 0x78
> +#define RCAR_GYROADC_INTENR_INTEN BIT(0)
> +
> +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
> +
> +enum rcar_gyroadc_model {
> + RCAR_GYROADC_MODEL_DEFAULT,
> + RCAR_GYROADC_MODEL_R8A7792,
> +};
> +
> +struct rcar_gyroadc {
> + struct device *dev;
> + void __iomem *regs;
> + struct clk *fclk;
> + struct clk *clk;
> + enum rcar_gyroadc_model model;
> + unsigned int mode;
> + u32 vref_uv[8];
> + u32 buffer[8];
> +};
> +
> +static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv)
> +{
> + unsigned long clk_mhz = clk_get_rate(priv->clk) / 1000000;
> +
> + /* Stop the GyroADC. */
> + writel(0, priv->regs + RCAR_GYROADC_START_STOP);
> +
> + /* Disable IRQ, except on V2H. */
> + if (priv->model != RCAR_GYROADC_MODEL_R8A7792)
> + writel(0, priv->regs + RCAR_GYROADC_INTENR);
> +
> + /* Set mode and timing. */
> + writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT);
> +
> + if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
> + writel(clk_mhz * 10, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
> + else if (priv->mode == RCAR_GYROADC_MODE_SELECT_2_ADCS7476)
> + writel(clk_mhz * 5, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
> + else if (priv->mode == RCAR_GYROADC_MODE_SELECT_3_MAX1162)
> + writel(clk_mhz * 5, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
> + writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH);
> +
> + /*
> + * We can possibly turn the sampling on/off on-demand to reduce power
> + * consumption, but for the sake of quick availability of samples, we
> + * don't do it now.
> + */
> + writel(RCAR_GYROADC_START_STOP_START,
> + priv->regs + RCAR_GYROADC_START_STOP);
> +
> + /* Wait for the first conversion to complete. */
> + udelay(1250);
> +}
> +
> +#define RCAR_GYROADC_CHAN(_idx, _chan_type, _realbits) { \
> + .type = (_chan_type), \
_chan_type is IIO_VOLTAGE always?
> + .indexed = 1, \
> + .channel = (_idx), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .scan_index = (_idx), \
no buffered mode yet, so strictly no need for a scan_index and scan_type
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = (_realbits), \
> + .storagebits = 16, \
> + }, \
> +}
> +
> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 12),
> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 12),
> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 12),
> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 12),
> +};
> +
> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 15),
> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 15),
> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 15),
> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 15),
> + RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 15),
> + RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 15),
> + RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 15),
> + RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 15),
> +};
> +
> +/*
> + * NOTE: The data we receive in mode 3 from MAX1162 have MSByte = 0,
> + * therefore we only use 16bit realbits here instead of 24.
> + */
> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
> + RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 16),
> + RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 16),
> + RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 16),
> + RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 16),
> + RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 16),
> + RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 16),
> + RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 16),
> + RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 16),
> +};
> +
> +static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + if (chan->type != IIO_VOLTAGE)
> + return -EINVAL;
> +
> + if (iio_buffer_enabled(indio_dev))
> + return -EBUSY;
use iio_device_claim_direct_mode()
> +
> + *val = readl(priv->regs + datareg);
> + *val &= BIT(chan->scan_type.realbits) - 1;
> +
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + *val = 0;
> + *val2 = (priv->vref_uv[chan->channel] * 1000) / 0x10000;
> + return IIO_VAL_INT_PLUS_NANO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *val = RCAR_GYROADC_SAMPLE_RATE;
> + *val2 = 0;
*val2 = 0 not needed
> + return IIO_VAL_INT;
> + default:
> + break;
return -EINVAL;
here directly
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev,
> + unsigned int reg, unsigned int writeval,
> + unsigned int *readval)
> +{
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + unsigned int maxreg = RCAR_GYROADC_INTENR;
> +
> + if (readval == NULL)
> + return -EINVAL;
> +
> + if (reg % 4)
> + return -EINVAL;
> +
> + /* Handle the V2H case with missing interrupt block. */
> + if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
> + maxreg = RCAR_GYROADC_FIFO_STATUS;
> +
> + if (reg > maxreg)
> + return -EINVAL;
> +
> + *readval = readl(priv->regs + reg);
> +
> + return 0;
> +}
> +
> +static const struct iio_info rcar_gyroadc_iio_info = {
> + .driver_module = THIS_MODULE,
> + .read_raw = rcar_gyroadc_read_raw,
> + .debugfs_reg_access = rcar_gyroadc_reg_access,
> +};
> +
> +static const struct of_device_id rcar_gyroadc_match[] = {
> + {
> + /* RCar Gen2 compatible GyroADC */
> + .compatible = "renesas,rcar-gyroadc",
> + .data = (void *)RCAR_GYROADC_MODEL_DEFAULT,
> + }, {
> + /* RCar V2H specialty without interrupt registers. */
> + .compatible = "renesas,rcar-gyroadc-r8a7792",
> + .data = (void *)RCAR_GYROADC_MODEL_R8A7792,
> + }, {
> + /* sentinel */
> + }
> +};
> +
> +MODULE_DEVICE_TABLE(of, rcar_gyroadc_match);
> +
> +static int rcar_gyroadc_probe(struct platform_device *pdev)
> +{
> + const struct of_device_id *of_id =
> + of_match_device(rcar_gyroadc_match, &pdev->dev);
> + struct device *dev = &pdev->dev;
> + struct rcar_gyroadc *priv;
> + struct iio_dev *indio_dev;
> + struct resource *mem;
> + int ret, mode;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
> + if (!indio_dev) {
> + dev_err(dev, "Failed to allocate IIO device.\n");
> + return -ENOMEM;
> + }
> +
> + priv = iio_priv(indio_dev);
> + priv->dev = dev;
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->regs = devm_ioremap_resource(dev, mem);
> + if (IS_ERR(priv->regs))
> + return PTR_ERR(priv->regs);
> +
> + priv->fclk = devm_clk_get(dev, "fck");
> + if (IS_ERR(priv->fclk)) {
> + ret = PTR_ERR(priv->fclk);
> + dev_err(dev, "Failed to get FCK clock (ret=%i)\n", ret);
> + return ret;
> + }
> +
> + priv->clk = devm_clk_get(dev, "if");
> + if (IS_ERR(priv->clk)) {
> + ret = PTR_ERR(priv->clk);
> + dev_err(dev, "Failed to get IF clock (ret=%i)\n", ret);
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "renesas,gyroadc-mode",
> + &mode);
> + if (ret || (mode < 1) || (mode > 3)) {
the mode check could be a simple 'else' below?
> + dev_err(dev, "Failed to get GyroADC mode (ret=%i)\n", ret);
> + return ret;
> + }
> +
> + if (mode == 1)
> + priv->mode = RCAR_GYROADC_MODE_SELECT_1_MB88101A;
> + else if (mode == 2)
> + priv->mode = RCAR_GYROADC_MODE_SELECT_2_ADCS7476;
> + else if (mode == 3)
> + priv->mode = RCAR_GYROADC_MODE_SELECT_3_MAX1162;
> +
> + of_property_read_u32_array(pdev->dev.of_node, "renesas,gyroadc-vref",
> + priv->vref_uv, (mode == 1) ? 4 : 8);
> +
> + priv->model = (enum rcar_gyroadc_model)of_id->data;
> +
> + platform_set_drvdata(pdev, indio_dev);
> +
> + indio_dev->name = dev_name(dev);
> + indio_dev->dev.parent = dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->info = &rcar_gyroadc_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + if (mode == 1) {
maybe do the mode differentiation only once, any with a switch?
> + indio_dev->channels = rcar_gyroadc_iio_channels_1;
> + indio_dev->num_channels =
> + ARRAY_SIZE(rcar_gyroadc_iio_channels_1);
> + } else if (mode == 2) {
> + indio_dev->channels = rcar_gyroadc_iio_channels_2;
> + indio_dev->num_channels =
> + ARRAY_SIZE(rcar_gyroadc_iio_channels_2);
> + } else if (mode == 3) {
> + indio_dev->channels = rcar_gyroadc_iio_channels_3;
> + indio_dev->num_channels =
> + ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
> + }
> +
> + ret = clk_prepare_enable(priv->fclk);
> + if (ret) {
> + dev_err(dev, "Could not prepare or enable the FCK clock.\n");
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(priv->clk);
> + if (ret) {
> + dev_err(dev, "Could not prepare or enable the IF clock.\n");
> + goto error_clk_if_enable;
> + }
> +
> + rcar_gyroadc_hw_init(priv);
> +
> + ret = iio_device_register(indio_dev);
> + if (ret) {
> + dev_err(dev, "Couldn't register IIO device.\n");
> + goto error_iio_device_register;
> + }
> +
> + return 0;
> +
> +error_iio_device_register:
> + clk_disable_unprepare(priv->clk);
> +error_clk_if_enable:
> + clk_disable_unprepare(priv->fclk);
> + return ret;
> +}
> +
> +static int rcar_gyroadc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> +
> + /* Stop sampling */
> + writel(0, priv->regs + RCAR_GYROADC_START_STOP);
> +
> + iio_device_unregister(indio_dev);
> + clk_disable_unprepare(priv->clk);
> + clk_disable_unprepare(priv->fclk);
> +
> + return 0;
> +}
> +
> +static struct platform_driver rcar_gyroadc_driver = {
> + .probe = rcar_gyroadc_probe,
> + .remove = rcar_gyroadc_remove,
> + .driver = {
> + .name = "rcar-gyroadc",
> + .of_match_table = rcar_gyroadc_match,
> + },
> +};
> +
> +module_platform_driver(rcar_gyroadc_driver);
> +
> +MODULE_AUTHOR("Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
> +MODULE_DESCRIPTION("Renesas RCAR GyroADC driver");
> +MODULE_LICENSE("GPL v2");
>
--
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
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 linux 2/6] hwmon: occ: Add sysfs interface
From: Guenter Roeck @ 2016-12-30 19:34 UTC (permalink / raw)
To: eajames.ibm
Cc: jdelvare, corbet, mark.rutland, robh+dt, wsa, andrew, joel,
devicetree, linux-doc, linux-hwmon, linux-i2c, linux-kernel,
Edward A. James
In-Reply-To: <1483120568-21082-3-git-send-email-eajames.ibm@gmail.com>
On Fri, Dec 30, 2016 at 11:56:04AM -0600, eajames.ibm@gmail.com wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add a generic mechanism to expose the sensors provided by the OCC in
> sysfs.
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
> ---
> Documentation/hwmon/occ | 48 +++++
> drivers/hwmon/occ/Makefile | 2 +-
> drivers/hwmon/occ/occ_sysfs.c | 492 ++++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/occ/occ_sysfs.h | 52 +++++
> 4 files changed, 593 insertions(+), 1 deletion(-)
> create mode 100644 drivers/hwmon/occ/occ_sysfs.c
> create mode 100644 drivers/hwmon/occ/occ_sysfs.h
>
> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
> index 79d1642..1ee8689 100644
> --- a/Documentation/hwmon/occ
> +++ b/Documentation/hwmon/occ
> @@ -25,6 +25,54 @@ Currently, all versions of the OCC support four types of sensor data: power,
> temperature, frequency, and "caps," which indicate limits and thresholds used
> internally on the OCC.
>
> +sysfs Entries
> +-------------
> +
> +The OCC driver uses the hwmon sysfs framework to provide data to userspace.
> +
> +The driver exports two sysfs files for each frequency, temperature, and power
> +sensor. These are "input" and "label". The input file contains the value of the
> +sensor. The label file contains the sensor id. The sensor id is the unique
> +internal OCC identifier. Sensor ids may be provided by the OCC specification.
> +The names of these files will be in the following format:
> + <sensor type><sensor index>_input
> + <sensor type><sensor index>_label
> +Sensor types will be one of "temp", "freq", or "power". The sensor index is
> +an index to differentiate different sensor files. For example, a single
> +temperature sensor will have two sysfs files: temp1_input and temp1_label.
> +
> +Caps sensors are exported differently. For each caps sensor, the driver will
> +export 6 entries:
> + curr_powercap - current power cap in watts
> + curr_powerreading - current power output in watts
> + norm_powercap - power cap without redundant power
> + max_powercap - maximum power cap that can be set in watts
> + min_powercap - minimum power cap that can be set in watts
> + user_powerlimit - power limit specified by the user in watts
> +In addition, the OCC driver for P9 will export a 7th entry:
> + user_powerlimit_source - can be one of two values depending on who set
> + the user_powerlimit. 0x1 - out of band from BMC or host. 0x2 -
> + in band from other source.
> +The format for these files is caps<sensor index>_<entry type>. For example,
> +caps1_curr_powercap.
> +
> +The driver also provides a number of sysfs entries through hwmon to better
> +control the driver and monitor the OCC.
> + powercap - read or write the OCC user power limit in watts.
> + name - read the name of the driver
> + update_interval - read or write the minimum interval for polling the
> + OCC.
> +
> +The driver also exports a single sysfs file through the communication protocol
> +device (see BMC - Host Communications). The filename is "online" and represents
> +the status of the OCC with respect to the driver. The OCC can be in one of two
> +states: OCC polling enabled or OCC polling disabled. The purpose of this file
> +is to control the behavior of the driver and it's hwmon sysfs entries, not to
> +infer any information about the state of the physical OCC. Reading the file
> +returns either a 0 (polling disabled) or 1 (polling enabled). Writing 1 to the
> +file enables OCC polling in the driver if communications can be established
> +with the OCC. Writing a 0 to the driver disables OCC polling.
> +
> BMC - Host Communications
> -------------------------
>
> diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
> index 93cb52f..a6881f9 100644
> --- a/drivers/hwmon/occ/Makefile
> +++ b/drivers/hwmon/occ/Makefile
> @@ -1 +1 @@
> -obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o
> +obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o occ_sysfs.o
> diff --git a/drivers/hwmon/occ/occ_sysfs.c b/drivers/hwmon/occ/occ_sysfs.c
> new file mode 100644
> index 0000000..b0e063da
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.c
> @@ -0,0 +1,492 @@
> +/*
> + * occ_sysfs.c - OCC sysfs interface
> + *
> + * This file contains the methods and data structures for implementing the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +
> +#include "occ_sysfs.h"
> +
> +#define MAX_SENSOR_ATTR_LEN 32
> +
> +#define RESP_RETURN_CMD_INVAL 0x13
> +
> +struct sensor_attr_data {
> + enum sensor_type type;
> + u32 hwmon_index;
> + u32 attr_id;
> + char name[MAX_SENSOR_ATTR_LEN];
> + struct device_attribute dev_attr;
> +};
> +
> +static ssize_t show_input(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int val;
> + struct sensor_attr_data *sdata = container_of(attr,
> + struct sensor_attr_data,
> + dev_attr);
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> + val = occ_get_sensor_value(driver->occ, sdata->type,
> + sdata->hwmon_index - 1);
> + if (sdata->type == TEMP)
> + val *= 1000; /* in millidegree Celsius */
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> +}
> +
> +/* show_label provides the OCC sensor id. The sensor id will be either a
> + * 2-byte (for P8) or 4-byte (for P9) value. The sensor id is a way to
> + * identify what each sensor represents, according to the OCC specification.
> + */
> +static ssize_t show_label(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int val;
> + struct sensor_attr_data *sdata = container_of(attr,
> + struct sensor_attr_data,
> + dev_attr);
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> + val = occ_get_sensor_id(driver->occ, sdata->type,
> + sdata->hwmon_index - 1);
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> +}
> +
> +static ssize_t show_caps(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int val;
> + struct caps_sensor *sensor;
> + struct sensor_attr_data *sdata = container_of(attr,
> + struct sensor_attr_data,
> + dev_attr);
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> + sensor = occ_get_sensor(driver->occ, CAPS);
> + if (!sensor) {
> + val = -1;
> + return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> + }
> +
> + val = occ_get_caps_value(driver->occ, sensor, sdata->hwmon_index - 1,
> + sdata->attr_id);
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> +}
> +
> +static ssize_t show_update_interval(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%lu\n", driver->update_interval);
> +}
> +
> +static ssize_t store_update_interval(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> + unsigned long val;
> + int rc;
> +
> + rc = kstrtoul(buf, 10, &val);
> + if (rc)
> + return rc;
> +
> + driver->update_interval = val;
> + occ_set_update_interval(driver->occ, val);
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_update_interval,
> + store_update_interval);
> +
> +static ssize_t show_name(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return snprintf(buf, PAGE_SIZE - 1, "occ\n");
> +}
> +
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static ssize_t show_user_powercap(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%u\n", driver->user_powercap);
> +}
> +
> +static ssize_t store_user_powercap(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> + u16 val;
> + int rc;
> +
> + rc = kstrtou16(buf, 10, &val);
> + if (rc)
> + return rc;
> +
> + dev_dbg(dev, "set user powercap to: %d\n", val);
> + rc = occ_set_user_powercap(driver->occ, val);
> + if (rc) {
> + dev_err(dev, "set user powercap failed: 0x%x\n", rc);
> + if (rc == RESP_RETURN_CMD_INVAL) {
> + dev_err(dev, "set invalid powercap value: %d\n", val);
> + return -EINVAL;
> + }
> +
> + return rc;
> + }
> +
> + driver->user_powercap = val;
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR(user_powercap, S_IWUSR | S_IRUGO, show_user_powercap,
> + store_user_powercap);
> +
> +static void deinit_sensor_groups(struct device *dev,
> + struct sensor_group *sensor_groups)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) {
> + if (sensor_groups[i].group.attrs)
> + devm_kfree(dev, sensor_groups[i].group.attrs);
> + if (sensor_groups[i].sattr)
> + devm_kfree(dev, sensor_groups[i].sattr);
> + sensor_groups[i].group.attrs = NULL;
> + sensor_groups[i].sattr = NULL;
> + }
> +}
> +
> +static void sensor_attr_init(struct sensor_attr_data *sdata,
> + char *sensor_group_name,
> + char *attr_name,
> + ssize_t (*show)(struct device *dev,
> + struct device_attribute *attr,
> + char *buf))
> +{
> + sysfs_attr_init(&sdata->dev_attr.attr);
> +
> + snprintf(sdata->name, MAX_SENSOR_ATTR_LEN, "%s%d_%s",
> + sensor_group_name, sdata->hwmon_index, attr_name);
> + sdata->dev_attr.attr.name = sdata->name;
> + sdata->dev_attr.attr.mode = S_IRUGO;
> + sdata->dev_attr.show = show;
> +}
> +
> +static int create_sensor_group(struct occ_sysfs *driver,
> + enum sensor_type type, int sensor_num)
> +{
> + struct device *dev = driver->dev;
> + struct sensor_group *sensor_groups = driver->sensor_groups;
> + struct sensor_attr_data *sdata;
> + int rc, i;
> +
> + /* each sensor has 'label' and 'input' attributes */
> + sensor_groups[type].group.attrs =
> + devm_kzalloc(dev, sizeof(struct attribute *) *
> + sensor_num * 2 + 1, GFP_KERNEL);
> + if (!sensor_groups[type].group.attrs) {
> + rc = -ENOMEM;
> + goto err;
> + }
> +
> + sensor_groups[type].sattr =
> + devm_kzalloc(dev, sizeof(struct sensor_attr_data) *
> + sensor_num * 2, GFP_KERNEL);
> + if (!sensor_groups[type].sattr) {
> + rc = -ENOMEM;
> + goto err;
> + }
> +
> + for (i = 0; i < sensor_num; i++) {
> + sdata = &sensor_groups[type].sattr[i];
> + /* hwmon attributes index starts from 1 */
> + sdata->hwmon_index = i + 1;
> + sdata->type = type;
> + sensor_attr_init(sdata, sensor_groups[type].name, "input",
> + show_input);
> + sensor_groups[type].group.attrs[i] = &sdata->dev_attr.attr;
> +
> + sdata = &sensor_groups[type].sattr[i + sensor_num];
> + sdata->hwmon_index = i + 1;
> + sdata->type = type;
> + sensor_attr_init(sdata, sensor_groups[type].name, "label",
> + show_label);
> + sensor_groups[type].group.attrs[i + sensor_num] =
> + &sdata->dev_attr.attr;
> + }
> +
> + rc = sysfs_create_group(&dev->kobj, &sensor_groups[type].group);
> + if (rc)
> + goto err;
> +
> + return 0;
> +err:
> + deinit_sensor_groups(dev, sensor_groups);
> + return rc;
> +}
> +
> +static void caps_sensor_attr_init(struct sensor_attr_data *sdata,
> + char *attr_name, uint32_t hwmon_index,
> + uint32_t attr_id)
> +{
> + sdata->type = CAPS;
> + sdata->hwmon_index = hwmon_index;
> + sdata->attr_id = attr_id;
> +
> + snprintf(sdata->name, MAX_SENSOR_ATTR_LEN, "%s%d_%s",
> + "caps", sdata->hwmon_index, attr_name);
> +
> + sysfs_attr_init(&sdata->dev_attr.attr);
> + sdata->dev_attr.attr.name = sdata->name;
> + sdata->dev_attr.attr.mode = S_IRUGO;
> + sdata->dev_attr.show = show_caps;
> +}
> +
> +static int create_caps_sensor_group(struct occ_sysfs *driver, int sensor_num)
> +{
> + struct device *dev = driver->dev;
> + struct sensor_group *sensor_groups = driver->sensor_groups;
> + int field_num = driver->num_caps_fields;
> + struct sensor_attr_data *sdata;
> + int i, j, rc;
> +
> + sensor_groups[CAPS].group.attrs =
> + devm_kzalloc(dev, sizeof(struct attribute *) * sensor_num *
> + field_num + 1, GFP_KERNEL);
> + if (!sensor_groups[CAPS].group.attrs) {
> + rc = -ENOMEM;
> + goto err;
> + }
> +
> + sensor_groups[CAPS].sattr =
> + devm_kzalloc(dev, sizeof(struct sensor_attr_data) *
> + sensor_num * field_num, GFP_KERNEL);
> + if (!sensor_groups[CAPS].sattr) {
> + rc = -ENOMEM;
> + goto err;
> + }
> +
> + for (j = 0; j < sensor_num; ++j) {
> + for (i = 0; i < field_num; ++i) {
> + sdata = &sensor_groups[CAPS].sattr[j * field_num + i];
> + caps_sensor_attr_init(sdata,
> + driver->caps_names[i], j + 1, i);
> + sensor_groups[CAPS].group.attrs[j * field_num + i] =
> + &sdata->dev_attr.attr;
> + }
> + }
> +
> + rc = sysfs_create_group(&dev->kobj, &sensor_groups[CAPS].group);
> + if (rc)
> + goto err;
> +
> + return rc;
> +err:
> + deinit_sensor_groups(dev, sensor_groups);
> + return rc;
> +}
> +
> +static void occ_remove_hwmon_attrs(struct occ_sysfs *driver)
> +{
> + struct device *dev = driver->dev;
> +
> + device_remove_file(dev, &dev_attr_user_powercap);
> + device_remove_file(dev, &dev_attr_update_interval);
> + device_remove_file(dev, &dev_attr_name);
> +}
> +
> +static int occ_create_hwmon_attrs(struct occ_sysfs *driver)
> +{
> + int i, rc, id, sensor_num;
> + struct device *dev = driver->dev;
> + struct sensor_group *sensor_groups = driver->sensor_groups;
> + struct occ_blocks *resp = NULL;
> +
> + occ_get_response_blocks(driver->occ, &resp);
> +
> + for (i = 0; i < MAX_OCC_SENSOR_TYPE; ++i)
> + resp->sensor_block_id[i] = -1;
> +
> + /* read sensor data from occ */
> + rc = occ_update_device(driver->occ);
> + if (rc) {
> + dev_err(dev, "cannot get occ sensor data: %d\n", rc);
> + return rc;
> + }
> + if (!resp->blocks)
> + return -ENOMEM;
> +
> + rc = device_create_file(dev, &dev_attr_name);
> + if (rc)
> + goto error;
> +
> + rc = device_create_file(dev, &dev_attr_update_interval);
> + if (rc)
> + goto error;
> +
> + if (resp->sensor_block_id[CAPS] >= 0) {
> + /* user powercap: only for master OCC */
> + rc = device_create_file(dev, &dev_attr_user_powercap);
> + if (rc)
> + goto error;
> + }
> +
> + sensor_groups[FREQ].name = "freq";
> + sensor_groups[TEMP].name = "temp";
> + sensor_groups[POWER].name = "power";
> + sensor_groups[CAPS].name = "caps";
> +
> + for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) {
> + id = resp->sensor_block_id[i];
> + if (id < 0)
> + continue;
> +
> + sensor_num = resp->blocks[id].header.sensor_num;
> + if (i == CAPS)
> + rc = create_caps_sensor_group(driver, sensor_num);
> + else
> + rc = create_sensor_group(driver, i, sensor_num);
> + if (rc)
> + goto error;
> + }
> +
> + return 0;
> +
> +error:
> + dev_err(dev, "cannot create hwmon attributes: %d\n", rc);
> + occ_remove_hwmon_attrs(driver);
> + return rc;
> +}
> +
> +static ssize_t show_occ_online(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%u\n", driver->occ_online);
> +}
> +
> +static ssize_t store_occ_online(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> + unsigned long val;
> + int rc;
> +
> + rc = kstrtoul(buf, 10, &val);
> + if (rc)
> + return rc;
> +
> + if (val == 1) {
> + if (driver->occ_online)
> + return count;
> +
> + driver->dev = hwmon_device_register(dev);
hwmon_device_register() is deprecated. Please consider using
devm_hwmon_device_register_with_info() or at least
hwmon_device_register_with_info().
Uuh ... and registering a hwmon device based on writing into a sysfs attribute
is completely out of the question.
Thanks,
Guenter
> + if (IS_ERR(driver->dev))
> + return PTR_ERR(driver->dev);
> +
> + dev_set_drvdata(driver->dev, driver);
> +
> + rc = occ_create_hwmon_attrs(driver);
> + if (rc) {
> + hwmon_device_unregister(driver->dev);
> + driver->dev = NULL;
> + return rc;
> + }
> + } else if (val == 0) {
> + if (!driver->occ_online)
> + return count;
> +
> + occ_remove_hwmon_attrs(driver);
> + hwmon_device_unregister(driver->dev);
> + driver->dev = NULL;
> + } else
> + return -EINVAL;
> +
> + driver->occ_online = val;
> + return count;
> +}
> +
> +static DEVICE_ATTR(online, S_IWUSR | S_IRUGO, show_occ_online,
> + store_occ_online);
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> + struct occ_sysfs_config *config)
> +{
> + struct occ_sysfs *hwmon = devm_kzalloc(dev, sizeof(struct occ_sysfs),
> + GFP_KERNEL);
> + int rc;
> +
> + if (!hwmon)
> + return ERR_PTR(-ENOMEM);
> +
> + hwmon->occ = occ;
> + hwmon->num_caps_fields = config->num_caps_fields;
> + hwmon->caps_names = config->caps_names;
> +
> + dev_set_drvdata(dev, hwmon);
> +
> + rc = device_create_file(dev, &dev_attr_online);
> + if (rc)
> + return ERR_PTR(rc);
> +
> + return hwmon;
> +}
> +EXPORT_SYMBOL(occ_sysfs_start);
> +
> +int occ_sysfs_stop(struct device *dev, struct occ_sysfs *driver)
> +{
> + if (driver->dev) {
> + occ_remove_hwmon_attrs(driver);
> + hwmon_device_unregister(driver->dev);
> + }
> +
> + device_remove_file(driver->dev, &dev_attr_online);
> +
> + devm_kfree(dev, driver);
Thw point of using devm_ functions is not to require remove/free functions.
Something is completely wrong here if you need that call.
Overall, this is architectually completely wrong. One does not register
or instantiate drivers based on writing into sysfs attributes. Please
reconsider your approach.
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(occ_sysfs_stop);
> +
> +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
> +MODULE_DESCRIPTION("OCC sysfs driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/occ/occ_sysfs.h b/drivers/hwmon/occ/occ_sysfs.h
> new file mode 100644
> index 0000000..2a8044f
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.h
> @@ -0,0 +1,52 @@
> +/*
> + * occ_sysfs.h - OCC sysfs interface
> + *
> + * This file contains the data structures and function prototypes for the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __OCC_SYSFS_H__
> +#define __OCC_SYSFS_H__
> +
> +#include "occ.h"
> +
> +struct sensor_group {
> + char *name;
> + struct sensor_attr_data *sattr;
> + struct attribute_group group;
> +};
> +
> +struct occ_sysfs_config {
> + unsigned int num_caps_fields;
> + char **caps_names;
> +};
> +
> +struct occ_sysfs {
> + struct device *dev;
> + struct occ *occ;
> +
> + u16 user_powercap;
> + bool occ_online;
> + struct sensor_group sensor_groups[MAX_OCC_SENSOR_TYPE];
> + unsigned long update_interval;
> + unsigned int num_caps_fields;
> + char **caps_names;
> +};
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> + struct occ_sysfs_config *config);
> +int occ_sysfs_stop(struct device *dev, struct occ_sysfs *driver);
> +
> +#endif /* __OCC_SYSFS_H__ */
> --
> 1.9.1
>
^ permalink raw reply
* Re: [PATCH linux 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs
From: kbuild test robot @ 2016-12-30 19:18 UTC (permalink / raw)
To: eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w
Cc: kbuild-all-JC7UmRfGjtg, linux-0h96xk9xTtrk1uMJSBkQmQ,
jdelvare-IBi9RG/b67k, corbet-T1hC0tSOHrs,
mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
wsa-z923LK4zBo2bacvFa/9K2g, andrew-zrmu5oMJ5Fs,
joel-U3u1mxZcP9KHXe+LvDLADg, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Edward A. James
In-Reply-To: <1483120568-21082-2-git-send-email-eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 2420 bytes --]
Hi Edward,
[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.10-rc1 next-20161224]
[cannot apply to linux/master]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/eajames-ibm-gmail-com/drivers-hwmon-Add-On-Chip-Controller-driver/20161231-021324
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 6.2.0
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=ia64
All warnings (new ones prefixed by >>):
drivers/hwmon/occ/occ.c: In function 'occ_get_all':
>> drivers/hwmon/occ/occ.c:390:44: warning: passing argument 5 of 'occ_send_cmd' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
rc = occ_send_cmd(driver, 0, OCC_POLL, 1, &poll_cmd_data, occ_data);
^
drivers/hwmon/occ/occ.c:281:11: note: expected 'u8 * {aka unsigned char *}' but argument is of type 'const u8 * {aka const unsigned char *}'
static u8 occ_send_cmd(struct occ *driver, u8 seq, u8 type, u16 length,
^~~~~~~~~~~~
vim +390 drivers/hwmon/occ/occ.c
374 return rc;
375 }
376
377 static int occ_get_all(struct occ *driver)
378 {
379 int i = 0, rc;
380 u8 *occ_data;
381 u16 num_bytes;
382 const u8 poll_cmd_data = OCC_POLL_STAT_SENSOR;
383 struct device *dev = driver->dev;
384 struct occ_response *resp = &driver->response;
385
386 occ_data = devm_kzalloc(dev, OCC_DATA_MAX, GFP_KERNEL);
387 if (!occ_data)
388 return -ENOMEM;
389
> 390 rc = occ_send_cmd(driver, 0, OCC_POLL, 1, &poll_cmd_data, occ_data);
391 if (rc) {
392 dev_err(dev, "OCC poll failed: %d\n", rc);
393 goto out;
394 }
395
396 num_bytes = get_unaligned((u16 *)&occ_data[RESP_DATA_LENGTH]);
397 num_bytes = be16_to_cpu(num_bytes);
398 dev_dbg(dev, "OCC data length: %d\n", num_bytes);
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 45853 bytes --]
^ permalink raw reply
* [PATCH] iio: adc: Add Renesas GyroADC driver
From: Marek Vasut @ 2016-12-30 19:18 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Marek Vasut,
Geert Uytterhoeven, Simon Horman
Add IIO driver for the Renesas RCar GyroADC block. This block is a
simple 4/8-channel ADC which samples 12/15/24 bits of data every
cycle from all channels.
Signed-off-by: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Cc: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
---
.../bindings/iio/adc/renesas,gyroadc.txt | 38 +++
MAINTAINERS | 6 +
drivers/iio/adc/Kconfig | 10 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/rcar_gyro_adc.c | 379 +++++++++++++++++++++
5 files changed, 434 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
create mode 100644 drivers/iio/adc/rcar_gyro_adc.c
diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
new file mode 100644
index 0000000..3fd5f57
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
@@ -0,0 +1,38 @@
+* Renesas RCar GyroADC device driver
+
+Required properties:
+- compatible: Should be "renesas,rcar-gyroadc" for regular GyroADC or
+ "renesas,rcar-gyroadc-r8a7792" for GyroADC without interrupt
+ block found in R8A7792.
+- reg: Address and length of the register set for the device
+- clocks: References to all the clocks specified in the clock-names
+ property as specified in
+ Documentation/devicetree/bindings/clock/clock-bindings.txt.
+- clock-names: Shall contain "fck" and "if". The "fck" are the GyroADC block
+ clock, the "if" are the interface clock.
+ power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
+- power-domains: Must contain a reference to the PM domain, if available.
+- renesas,gyroadc-mode: GyroADC mode of operation, must be either of:
+ 1 - MB88101A mode, 12bit sampling, 4 channels
+ 2 - ADCS7476 mode, 15bit sampling, 8 channels
+ 3 - MAX1162 mode, 16bit sampling, 8 channels
+- renesas,gyroadc-vref: Array of reference voltage values for each input to
+ the GyroADC, in uV. Array must have 4 elemenets for
+ mode 1 and 8 elements for mode 2 and 3.
+
+Example:
+ &adc {
+ compatible = "renesas,rcar-gyroadc";
+ reg = <0 0xe6e54000 0 64>;
+ clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>;
+ clock-names = "fck", "if";
+ power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
+
+ pinctrl-0 = <&adc_pins>;
+ pinctrl-names = "default";
+
+ renesas,gyroadc-vref = <4096000 4096000 4096000 4096000
+ 4096000 4096000 4096000 4096000>;
+ renesas,gyroadc-mode = <3>;
+ status = "okay";
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 162d904..751e760 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10271,6 +10271,12 @@ L: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
F: drivers/net/ethernet/renesas/
F: include/linux/sh_eth.h
+RENESAS RCAR GYROADC DRIVER
+M: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Supported
+F: drivers/iio/adc/rcar_gyro_adc.c
+
RENESAS USB2 PHY DRIVER
M: Yoshihiro Shimoda <yoshihiro.shimoda.uh-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
L: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 99c0514..4a4cac7 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -408,6 +408,16 @@ config QCOM_SPMI_VADC
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-vadc.
+config RCAR_GYRO_ADC
+ tristate "Renesas RCAR GyroADC driver"
+ depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
+ help
+ Say yes here to build support for the GyroADC found in Renesas
+ RCar Gen2 SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rcar-gyroadc.
+
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..253aeb2 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
+obj-$(CONFIG_RCAR_GYRO_ADC) += rcar_gyro_adc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
diff --git a/drivers/iio/adc/rcar_gyro_adc.c b/drivers/iio/adc/rcar_gyro_adc.c
new file mode 100644
index 0000000..a74b148
--- /dev/null
+++ b/drivers/iio/adc/rcar_gyro_adc.c
@@ -0,0 +1,379 @@
+/*
+ * Renesas RCar GyroADC driver
+ *
+ * Copyright 2016 Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+
+/* GyroADC registers. */
+#define RCAR_GYROADC_MODE_SELECT 0x00
+#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0
+#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1
+#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3
+
+#define RCAR_GYROADC_START_STOP 0x04
+#define RCAR_GYROADC_START_STOP_START BIT(0)
+
+#define RCAR_GYROADC_CLOCK_LENGTH 0x08
+#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
+
+#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
+#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
+#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
+
+#define RCAR_GYROADC_FIFO_STATUS 0x70
+#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
+#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
+#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
+
+#define RCAR_GYROADC_INTR 0x74
+#define RCAR_GYROADC_INTR_INT BIT(0)
+
+#define RCAR_GYROADC_INTENR 0x78
+#define RCAR_GYROADC_INTENR_INTEN BIT(0)
+
+#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
+
+enum rcar_gyroadc_model {
+ RCAR_GYROADC_MODEL_DEFAULT,
+ RCAR_GYROADC_MODEL_R8A7792,
+};
+
+struct rcar_gyroadc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *fclk;
+ struct clk *clk;
+ enum rcar_gyroadc_model model;
+ unsigned int mode;
+ u32 vref_uv[8];
+ u32 buffer[8];
+};
+
+static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv)
+{
+ unsigned long clk_mhz = clk_get_rate(priv->clk) / 1000000;
+
+ /* Stop the GyroADC. */
+ writel(0, priv->regs + RCAR_GYROADC_START_STOP);
+
+ /* Disable IRQ, except on V2H. */
+ if (priv->model != RCAR_GYROADC_MODEL_R8A7792)
+ writel(0, priv->regs + RCAR_GYROADC_INTENR);
+
+ /* Set mode and timing. */
+ writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT);
+
+ if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
+ writel(clk_mhz * 10, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
+ else if (priv->mode == RCAR_GYROADC_MODE_SELECT_2_ADCS7476)
+ writel(clk_mhz * 5, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
+ else if (priv->mode == RCAR_GYROADC_MODE_SELECT_3_MAX1162)
+ writel(clk_mhz * 5, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
+ writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH);
+
+ /*
+ * We can possibly turn the sampling on/off on-demand to reduce power
+ * consumption, but for the sake of quick availability of samples, we
+ * don't do it now.
+ */
+ writel(RCAR_GYROADC_START_STOP_START,
+ priv->regs + RCAR_GYROADC_START_STOP);
+
+ /* Wait for the first conversion to complete. */
+ udelay(1250);
+}
+
+#define RCAR_GYROADC_CHAN(_idx, _chan_type, _realbits) { \
+ .type = (_chan_type), \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_realbits), \
+ .storagebits = 16, \
+ }, \
+}
+
+static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
+ RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 12),
+ RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 12),
+ RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 12),
+ RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 12),
+};
+
+static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
+ RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 15),
+ RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 15),
+ RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 15),
+ RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 15),
+ RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 15),
+ RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 15),
+ RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 15),
+ RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 15),
+};
+
+/*
+ * NOTE: The data we receive in mode 3 from MAX1162 have MSByte = 0,
+ * therefore we only use 16bit realbits here instead of 24.
+ */
+static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
+ RCAR_GYROADC_CHAN(0, IIO_VOLTAGE, 16),
+ RCAR_GYROADC_CHAN(1, IIO_VOLTAGE, 16),
+ RCAR_GYROADC_CHAN(2, IIO_VOLTAGE, 16),
+ RCAR_GYROADC_CHAN(3, IIO_VOLTAGE, 16),
+ RCAR_GYROADC_CHAN(4, IIO_VOLTAGE, 16),
+ RCAR_GYROADC_CHAN(5, IIO_VOLTAGE, 16),
+ RCAR_GYROADC_CHAN(6, IIO_VOLTAGE, 16),
+ RCAR_GYROADC_CHAN(7, IIO_VOLTAGE, 16),
+};
+
+static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ *val = readl(priv->regs + datareg);
+ *val &= BIT(chan->scan_type.realbits) - 1;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = (priv->vref_uv[chan->channel] * 1000) / 0x10000;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = RCAR_GYROADC_SAMPLE_RATE;
+ *val2 = 0;
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ unsigned int maxreg = RCAR_GYROADC_INTENR;
+
+ if (readval == NULL)
+ return -EINVAL;
+
+ if (reg % 4)
+ return -EINVAL;
+
+ /* Handle the V2H case with missing interrupt block. */
+ if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
+ maxreg = RCAR_GYROADC_FIFO_STATUS;
+
+ if (reg > maxreg)
+ return -EINVAL;
+
+ *readval = readl(priv->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info rcar_gyroadc_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = rcar_gyroadc_read_raw,
+ .debugfs_reg_access = rcar_gyroadc_reg_access,
+};
+
+static const struct of_device_id rcar_gyroadc_match[] = {
+ {
+ /* RCar Gen2 compatible GyroADC */
+ .compatible = "renesas,rcar-gyroadc",
+ .data = (void *)RCAR_GYROADC_MODEL_DEFAULT,
+ }, {
+ /* RCar V2H specialty without interrupt registers. */
+ .compatible = "renesas,rcar-gyroadc-r8a7792",
+ .data = (void *)RCAR_GYROADC_MODEL_R8A7792,
+ }, {
+ /* sentinel */
+ }
+};
+
+MODULE_DEVICE_TABLE(of, rcar_gyroadc_match);
+
+static int rcar_gyroadc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(rcar_gyroadc_match, &pdev->dev);
+ struct device *dev = &pdev->dev;
+ struct rcar_gyroadc *priv;
+ struct iio_dev *indio_dev;
+ struct resource *mem;
+ int ret, mode;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev) {
+ dev_err(dev, "Failed to allocate IIO device.\n");
+ return -ENOMEM;
+ }
+
+ priv = iio_priv(indio_dev);
+ priv->dev = dev;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ priv->fclk = devm_clk_get(dev, "fck");
+ if (IS_ERR(priv->fclk)) {
+ ret = PTR_ERR(priv->fclk);
+ dev_err(dev, "Failed to get FCK clock (ret=%i)\n", ret);
+ return ret;
+ }
+
+ priv->clk = devm_clk_get(dev, "if");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "Failed to get IF clock (ret=%i)\n", ret);
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "renesas,gyroadc-mode",
+ &mode);
+ if (ret || (mode < 1) || (mode > 3)) {
+ dev_err(dev, "Failed to get GyroADC mode (ret=%i)\n", ret);
+ return ret;
+ }
+
+ if (mode == 1)
+ priv->mode = RCAR_GYROADC_MODE_SELECT_1_MB88101A;
+ else if (mode == 2)
+ priv->mode = RCAR_GYROADC_MODE_SELECT_2_ADCS7476;
+ else if (mode == 3)
+ priv->mode = RCAR_GYROADC_MODE_SELECT_3_MAX1162;
+
+ of_property_read_u32_array(pdev->dev.of_node, "renesas,gyroadc-vref",
+ priv->vref_uv, (mode == 1) ? 4 : 8);
+
+ priv->model = (enum rcar_gyroadc_model)of_id->data;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &rcar_gyroadc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ if (mode == 1) {
+ indio_dev->channels = rcar_gyroadc_iio_channels_1;
+ indio_dev->num_channels =
+ ARRAY_SIZE(rcar_gyroadc_iio_channels_1);
+ } else if (mode == 2) {
+ indio_dev->channels = rcar_gyroadc_iio_channels_2;
+ indio_dev->num_channels =
+ ARRAY_SIZE(rcar_gyroadc_iio_channels_2);
+ } else if (mode == 3) {
+ indio_dev->channels = rcar_gyroadc_iio_channels_3;
+ indio_dev->num_channels =
+ ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
+ }
+
+ ret = clk_prepare_enable(priv->fclk);
+ if (ret) {
+ dev_err(dev, "Could not prepare or enable the FCK clock.\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(dev, "Could not prepare or enable the IF clock.\n");
+ goto error_clk_if_enable;
+ }
+
+ rcar_gyroadc_hw_init(priv);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Couldn't register IIO device.\n");
+ goto error_iio_device_register;
+ }
+
+ return 0;
+
+error_iio_device_register:
+ clk_disable_unprepare(priv->clk);
+error_clk_if_enable:
+ clk_disable_unprepare(priv->fclk);
+ return ret;
+}
+
+static int rcar_gyroadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+
+ /* Stop sampling */
+ writel(0, priv->regs + RCAR_GYROADC_START_STOP);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->fclk);
+
+ return 0;
+}
+
+static struct platform_driver rcar_gyroadc_driver = {
+ .probe = rcar_gyroadc_probe,
+ .remove = rcar_gyroadc_remove,
+ .driver = {
+ .name = "rcar-gyroadc",
+ .of_match_table = rcar_gyroadc_match,
+ },
+};
+
+module_platform_driver(rcar_gyroadc_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("Renesas RCAR GyroADC driver");
+MODULE_LICENSE("GPL v2");
--
2.10.2
^ permalink raw reply related
* Re: [PATCH] ARM: dts: am572x-idk: Add gpios property to control PCIE_RESETn
From: Tony Lindgren @ 2016-12-30 19:00 UTC (permalink / raw)
To: Kishon Vijay Abraham I
Cc: Benoît Cousson, linux-omap-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland,
Russell King, nsekhar-l0cyMroinI0
In-Reply-To: <1483085240-16102-1-git-send-email-kishon-l0cyMroinI0@public.gmane.org>
* Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org> [161230 00:08]:
> Add 'gpios' property to pcie1 dt node and populate it with
> GPIO3_23 in order to drive PCIE_RESETn high.
>
> This gets PCIe cards to be detected in AM572X IDK board.
>
> Signed-off-by: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
> ---
> arch/arm/boot/dts/am572x-idk.dts | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/arch/arm/boot/dts/am572x-idk.dts b/arch/arm/boot/dts/am572x-idk.dts
> index 27d9149..1540f7a 100644
> --- a/arch/arm/boot/dts/am572x-idk.dts
> +++ b/arch/arm/boot/dts/am572x-idk.dts
> @@ -87,3 +87,7 @@
> &sn65hvs882 {
> load-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
> };
> +
> +&pcie1 {
> + gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>;
> +};
I'll apply this into omap-for-v4.10/fixes thanks.
Tony
--
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] ARM: dts: Add dra7 iodelay configuration and use it for MMC
From: Tony Lindgren @ 2016-12-30 18:49 UTC (permalink / raw)
To: Linus Walleij
Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon,
Rob Herring, devicetree, linux-gpio, linux-omap, Lokesh Vutla
In-Reply-To: <20161230183732.5595-3-tony@atomide.com>
Add dra7 iodelay configuration and use it for MMC.
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi | 21 ++++++++++++++++++++-
arch/arm/boot/dts/dra7.dtsi | 8 ++++++++
include/dt-bindings/pinctrl/dra.h | 4 ++++
3 files changed, 32 insertions(+), 1 deletion(-)
---
And ere are the related dts changes.
diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
--- a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
+++ b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
@@ -190,6 +190,25 @@
>;
};
};
+
+&dra7_iodelay_core
+{
+ mmc2_iodelay_3v3_conf: mmc2_iodelay_3v3_conf {
+ pinctrl-pin-array = <
+ 0x18c A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A19_IN */
+ 0x1a4 A_DELAY_PS(265) G_DELAY_PS(360) /* CFG_GPMC_A20_IN */
+ 0x1b0 A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A21_IN */
+ 0x1bc A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A22_IN */
+ 0x1c8 A_DELAY_PS(287) G_DELAY_PS(420) /* CFG_GPMC_A23_IN */
+ 0x1d4 A_DELAY_PS(144) G_DELAY_PS(240) /* CFG_GPMC_A24_IN */
+ 0x1e0 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A25_IN */
+ 0x1ec A_DELAY_PS(120) G_DELAY_PS(0) /* CFG_GPMC_A26_IN */
+ 0x1f8 A_DELAY_PS(120) G_DELAY_PS(180) /* CFG_GPMC_A27_IN */
+ 0x360 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_CS1_IN */
+ >;
+ };
+};
+
&i2c1 {
status = "okay";
clock-frequency = <400000>;
@@ -452,7 +471,7 @@
status = "okay";
pinctrl-names = "default";
- pinctrl-0 = <&mmc2_pins_default>;
+ pinctrl-0 = <&mmc2_pins_default &mmc2_iodelay_3v3_conf>;
vmmc-supply = <&vdd_3v3>;
bus-width = <8>;
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -401,6 +401,14 @@
reg = <0x40d00000 0x100>;
};
+ dra7_iodelay_core: padconf@4844a000 {
+ compatible = "ti,dra7-iodelay";
+ reg = <0x4844a000 0x0d1c>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #pinctrl-cells = <2>;
+ };
+
sdma: dma-controller@4a056000 {
compatible = "ti,omap4430-sdma";
reg = <0x4a056000 0x1000>;
diff --git a/include/dt-bindings/pinctrl/dra.h b/include/dt-bindings/pinctrl/dra.h
--- a/include/dt-bindings/pinctrl/dra.h
+++ b/include/dt-bindings/pinctrl/dra.h
@@ -73,5 +73,9 @@
*/
#define DRA7XX_CORE_IOPAD(pa, val) (((pa) & 0xffff) - 0x3400) (val)
+/* DRA7 IODELAY configuration parameters */
+#define A_DELAY_PS(val) ((val) & 0xffff)
+#define G_DELAY_PS(val) ((val) & 0xffff)
+
#endif
--
2.11.0
^ permalink raw reply
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver
From: Tony Lindgren @ 2016-12-30 18:37 UTC (permalink / raw)
To: Linus Walleij
Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon,
Rob Herring, devicetree, linux-gpio, linux-omap, Lokesh Vutla
In-Reply-To: <20161230183732.5595-1-tony@atomide.com>
From: Nishanth Menon <nm@ti.com>
SoC family such as DRA7 family of processors have, in addition
to the regular muxing of pins (as done by pinctrl-single), a separate
hardware module called IODelay which is also expected to be configured.
The "IODelay" module has it's own register space that is independent
of the control module and the padconf register area.
With recent changes to the pinctrl framework, we can now support
this hardware with a reasonably minimal driver by using #pinctrl-cells,
GENERIC_PINCTRL_GROUPS and GENERIC_PINMUX_FUNCTIONS.
It is advocated strongly in TI's official documentation considering
the existing design of the DRA7 family of processors during mux or
IODelay reconfiguration, there is a potential for a significant glitch
which may cause functional impairment to certain hardware. It is
hence recommended to do as little of muxing as absolutely necessary
without I/O isolation (which can only be done in initial stages of
bootloader).
NOTE: with the system wide I/O isolation scheme present in DRA7 SoC
family, it is not reasonable to do stop all I/O operations for every
such pad configuration scheme. So, we will let it glitch when used in
this mode.
Even with the above limitation, certain functionality such as MMC has
mandatory need for IODelay reconfiguration requirements, depending on
speed of transfer. In these cases, with careful examination of usecase
involved, the expected glitch can be controlled such that it does not
impact functionality.
In short, IODelay module support as a padconf driver being introduced
here is not expected to do SoC wide I/O Isolation and is meant for
a limited subset of IODelay configuration requirements that need to
be dynamic and whose glitchy behavior will not cause functionality
failure for that interface.
IMPORTANT NOTE: we take the approach of keeping LOCK_BITs cleared
to 0x0 at all times, even when configuring Manual IO Timing Modes.
This is done by eliminating the LOCK_BIT=1 setting from Step
of the Manual IO timing Mode configuration procedure. This option
leaves the CFG_* registers unprotected from unintended writes to the
CTRL_CORE_PAD_* registers while Manual IO Timing Modes are configured.
This approach is taken to allow for a generic driver to exist in kernel
world that has to be used carefully in required usecases.
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
[tony@atomide.com: updated to use generic pinctrl functions, added
binding documentation, updated comments]
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
.../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 +
drivers/pinctrl/Kconfig | 1 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/ti/Kconfig | 10 +
drivers/pinctrl/ti/Makefile | 1 +
drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 945 +++++++++++++++++++++
6 files changed, 1005 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt
create mode 100644 drivers/pinctrl/ti/Kconfig
create mode 100644 drivers/pinctrl/ti/Makefile
create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c
diff --git a/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt
@@ -0,0 +1,47 @@
+* Pin configuration for TI IODELAY controller
+
+TI dra7 based SoCs such as am57xx have a controller for setting the IO delay
+for each pin. For most part the IO delay values are programmed by the bootloader,
+but some pins need to be configured dynamically by the kernel such as the
+MMC pins.
+
+Required Properties:
+
+ - compatible: Must be "ti,dra7-iodelay"
+ - reg: Base address and length of the memory resource used
+ - #address-cells: Number of address cells
+ - #size-cells: Size of cells
+ - #pinctrl-cells: Number of pinctrl cells, must be 2. See also
+ Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+
+Example
+-------
+
+In the SoC specific dtsi file:
+
+ dra7_iodelay_core: padconf@4844a000 {
+ compatible = "ti,dra7-iodelay";
+ reg = <0x4844a000 0x0d1c>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #pinctrl-cells = <2>;
+ };
+
+In board-specific file:
+
+&dra7_iodelay_core {
+ mmc2_iodelay_3v3_conf: mmc2_iodelay_3v3_conf {
+ pinctrl-pin-array = <
+ 0x18c A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A19_IN */
+ 0x1a4 A_DELAY_PS(265) G_DELAY_PS(360) /* CFG_GPMC_A20_IN */
+ 0x1b0 A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A21_IN */
+ 0x1bc A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A22_IN */
+ 0x1c8 A_DELAY_PS(287) G_DELAY_PS(420) /* CFG_GPMC_A23_IN */
+ 0x1d4 A_DELAY_PS(144) G_DELAY_PS(240) /* CFG_GPMC_A24_IN */
+ 0x1e0 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A25_IN */
+ 0x1ec A_DELAY_PS(120) G_DELAY_PS(0) /* CFG_GPMC_A26_IN */
+ 0x1f8 A_DELAY_PS(120) G_DELAY_PS(180) /* CFG_GPMC_A27_IN */
+ 0x360 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_CS1_IN */
+ >;
+ };
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -300,6 +300,7 @@ source "drivers/pinctrl/spear/Kconfig"
source "drivers/pinctrl/stm32/Kconfig"
source "drivers/pinctrl/sunxi/Kconfig"
source "drivers/pinctrl/tegra/Kconfig"
+source "drivers/pinctrl/ti/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
source "drivers/pinctrl/vt8500/Kconfig"
source "drivers/pinctrl/mediatek/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/
obj-$(CONFIG_PINCTRL_SPEAR) += spear/
obj-$(CONFIG_PINCTRL_STM32) += stm32/
obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/
+obj-y += ti/
obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_VT8500) += vt8500/
obj-$(CONFIG_PINCTRL_MTK) += mediatek/
diff --git a/drivers/pinctrl/ti/Kconfig b/drivers/pinctrl/ti/Kconfig
new file mode 100644
--- /dev/null
+++ b/drivers/pinctrl/ti/Kconfig
@@ -0,0 +1,10 @@
+config PINCTRL_TI_IODELAY
+ tristate "TI IODelay Module pinconf driver"
+ depends on OF
+ select GENERIC_PINCTRL_GROUPS
+ select GENERIC_PINMUX_FUNCTIONS
+ select GENERIC_PINCONF
+ select REGMAP_MMIO
+ help
+ Say Y here to support Texas Instruments' IO delay pinconf driver.
+ IO delay module is used for the DRA7 SoC family.
diff --git a/drivers/pinctrl/ti/Makefile b/drivers/pinctrl/ti/Makefile
new file mode 100644
--- /dev/null
+++ b/drivers/pinctrl/ti/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o
diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c
new file mode 100644
--- /dev/null
+++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c
@@ -0,0 +1,945 @@
+/*
+ * Support for configuration of IO Delay module found on Texas Instruments SoCs
+ * such as DRA7
+ *
+ * Copyright (C) 2015 Texas Instruments, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "../core.h"
+#include "../devicetree.h"
+
+#define DRIVER_NAME "ti-iodelay"
+
+/**
+ * struct ti_iodelay_reg_data - Describes the registers for the iodelay instance
+ * @signature_mask: CONFIG_REG mask for the signature bits (see TRM)
+ * @signature_value: CONFIG_REG signature value to be written (see TRM)
+ * @lock_mask: CONFIG_REG mask for the lock bits (see TRM)
+ * @lock_val: CONFIG_REG lock value for the lock bits (see TRM)
+ * @unlock_val:CONFIG_REG unlock value for the lock bits (see TRM)
+ * @binary_data_coarse_mask: CONFIG_REG coarse mask (see TRM)
+ * @binary_data_fine_mask: CONFIG_REG fine mask (see TRM)
+ * @reg_refclk_offset: Refclk register offset
+ * @refclk_period_mask: Refclk mask
+ * @reg_coarse_offset: Coarse register configuration offset
+ * @coarse_delay_count_mask: Coarse delay count mask
+ * @coarse_ref_count_mask: Coarse ref count mask
+ * @reg_fine_offset: Fine register configuration offset
+ * @fine_delay_count_mask: Fine delay count mask
+ * @fine_ref_count_mask: Fine ref count mask
+ * @reg_global_lock_offset: Global iodelay module lock register offset
+ * @global_lock_mask: Lock mask
+ * @global_unlock_val: Unlock value
+ * @global_lock_val: Lock value
+ * @reg_start_offset: Offset to iodelay registers after the CONFIG_REG_0 to 8
+ * @reg_nr_per_pin: Number of iodelay registers for each pin
+ * @regmap_config: Regmap configuration for the IODelay region
+ */
+struct ti_iodelay_reg_data {
+ u32 signature_mask;
+ u32 signature_value;
+ u32 lock_mask;
+ u32 lock_val;
+ u32 unlock_val;
+ u32 binary_data_coarse_mask;
+ u32 binary_data_fine_mask;
+
+ u32 reg_refclk_offset;
+ u32 refclk_period_mask;
+
+ u32 reg_coarse_offset;
+ u32 coarse_delay_count_mask;
+ u32 coarse_ref_count_mask;
+
+ u32 reg_fine_offset;
+ u32 fine_delay_count_mask;
+ u32 fine_ref_count_mask;
+
+ u32 reg_global_lock_offset;
+ u32 global_lock_mask;
+ u32 global_unlock_val;
+ u32 global_lock_val;
+
+ u32 reg_start_offset;
+ u32 reg_nr_per_pin;
+
+ struct regmap_config *regmap_config;
+};
+
+/**
+ * struct ti_iodelay_reg_values - Computed io_reg configuration values (see TRM)
+ * @coarse_ref_count: Coarse reference count
+ * @coarse_delay_count: Coarse delay count
+ * @fine_ref_count: Fine reference count
+ * @fine_delay_count: Fine Delay count
+ * @ref_clk_period: Reference Clock period
+ * @cdpe: Coarse delay parameter
+ * @fdpe: Fine delay parameter
+ */
+struct ti_iodelay_reg_values {
+ u16 coarse_ref_count;
+ u16 coarse_delay_count;
+
+ u16 fine_ref_count;
+ u16 fine_delay_count;
+
+ u16 ref_clk_period;
+
+ u32 cdpe;
+ u32 fdpe;
+};
+
+/**
+ * struct ti_iodelay_cfg - Description of each configuration parameters
+ * @offset: Configuration register offset
+ * @a_delay: Agnostic Delay (in ps)
+ * @g_delay: Gnostic Delay (in ps)
+ */
+struct ti_iodelay_cfg {
+ u16 offset;
+ u16 a_delay;
+ u16 g_delay;
+};
+
+/**
+ * struct ti_iodelay_pingroup - Structure that describes one group
+ * @name: Name of the group
+ * @map: pinctrl map allocated for the group
+ * @cfg: configuration array for the pin (from dt)
+ * @ncfg: number of configuration values allocated
+ * @config: pinconf "Config" - currently a dummy value
+ */
+struct ti_iodelay_pingroup {
+ struct ti_iodelay_cfg *cfg;
+ int ncfg;
+ unsigned long config;
+};
+
+/**
+ * struct ti_iodelay_device - Represents information for a iodelay instance
+ * @dev: Device pointer
+ * @phys_base: Physical address base of the iodelay device
+ * @reg_base: Virtual address base of the iodelay device
+ * @regmap: Regmap for this iodelay instance
+ * @pctl: Pinctrl device
+ * @desc: pinctrl descriptor for pctl
+ * @pa: pinctrl pin wise description
+ * @names: names of the pins
+ * @reg_data: Register definition data for the IODelay instance
+ * @reg_init_conf_values: Initial configuration values.
+ */
+struct ti_iodelay_device {
+ struct device *dev;
+ unsigned long phys_base;
+ void __iomem *reg_base;
+ struct regmap *regmap;
+
+ struct pinctrl_dev *pctl;
+ struct pinctrl_desc desc;
+ struct pinctrl_pin_desc *pa;
+
+ const struct ti_iodelay_reg_data *reg_data;
+ struct ti_iodelay_reg_values reg_init_conf_values;
+};
+
+/**
+ * ti_iodelay_extract() - extract bits for a field
+ * @val: Register value
+ * @mask: Mask
+ *
+ * Return: extracted value which is appropriately shifted
+ */
+static inline u32 ti_iodelay_extract(u32 val, u32 mask)
+{
+ return (val & mask) >> __ffs(mask);
+}
+
+/**
+ * ti_iodelay_compute_dpe() - Compute equation for delay parameter
+ * @period: Period to use
+ * @ref: Reference Count
+ * @delay: Delay count
+ * @delay_m: Delay multiplier
+ *
+ * Return: Computed delay parameter
+ */
+static inline u32 ti_iodelay_compute_dpe(u16 period, u16 ref, u16 delay,
+ u16 delay_m)
+{
+ u64 m, d;
+
+ /* Handle overflow conditions */
+ m = 10 * (u64)period * (u64)ref;
+ d = 2 * (u64)delay * (u64)delay_m;
+
+ /* Truncate result back to 32 bits */
+ return div64_u64(m, d);
+}
+
+/**
+ * ti_iodelay_pinconf_set() - Configure the pin configuration
+ * @iod: iodelay device
+ * @val: Configuration value
+ *
+ * Update the configuration register as per TRM and lockup once done.
+ * *IMPORTANT NOTE* SoC TRM does recommend doing iodelay programmation only
+ * while in Isolation. But, then, isolation also implies that every pin
+ * on the SoC (including DDR) will be isolated out. The only benefit being
+ * a glitchless configuration, However, the intent of this driver is purely
+ * to support a "glitchy" configuration where applicable.
+ *
+ * Return: 0 in case of success, else appropriate error value
+ */
+static int ti_iodelay_pinconf_set(struct ti_iodelay_device *iod,
+ struct ti_iodelay_cfg *cfg)
+{
+ const struct ti_iodelay_reg_data *reg = iod->reg_data;
+ struct ti_iodelay_reg_values *ival = &iod->reg_init_conf_values;
+ struct device *dev = iod->dev;
+ u32 g_delay_coarse, g_delay_fine;
+ u32 a_delay_coarse, a_delay_fine;
+ u32 c_elements, f_elements;
+ u32 total_delay;
+ u32 reg_mask, reg_val, tmp_val;
+ int r;
+
+ /* NOTE: Truncation is expected in all division below */
+ g_delay_coarse = cfg->g_delay / 920;
+ g_delay_fine = ((cfg->g_delay % 920) * 10) / 60;
+
+ a_delay_coarse = cfg->a_delay / ival->cdpe;
+ a_delay_fine = ((cfg->a_delay % ival->cdpe) * 10) / ival->fdpe;
+
+ c_elements = g_delay_coarse + a_delay_coarse;
+ f_elements = (g_delay_fine + a_delay_fine) / 10;
+
+ if (f_elements > 22) {
+ total_delay = c_elements * ival->cdpe + f_elements * ival->fdpe;
+ c_elements = total_delay / ival->cdpe;
+ f_elements = (total_delay % ival->cdpe) / ival->fdpe;
+ }
+
+ reg_mask = reg->signature_mask;
+ reg_val = reg->signature_value << __ffs(reg->signature_mask);
+
+ reg_mask |= reg->binary_data_coarse_mask;
+ tmp_val = c_elements << __ffs(reg->binary_data_coarse_mask);
+ if (tmp_val & ~reg->binary_data_coarse_mask) {
+ dev_err(dev, "Masking overflow of coarse elements %08x\n",
+ tmp_val);
+ tmp_val &= reg->binary_data_coarse_mask;
+ }
+ reg_val |= tmp_val;
+
+ reg_mask |= reg->binary_data_fine_mask;
+ tmp_val = f_elements << __ffs(reg->binary_data_fine_mask);
+ if (tmp_val & ~reg->binary_data_fine_mask) {
+ dev_err(dev, "Masking overflow of fine elements %08x\n",
+ tmp_val);
+ tmp_val &= reg->binary_data_fine_mask;
+ }
+ reg_val |= tmp_val;
+
+ /*
+ * NOTE: we leave the iodelay values unlocked - this is to work around
+ * situations such as those found with mmc mode change.
+ * However, this leaves open any unwarranted changes to padconf register
+ * impacting iodelay configuration. Use with care!
+ */
+ reg_mask |= reg->lock_mask;
+ reg_val |= reg->unlock_val << __ffs(reg->lock_mask);
+ r = regmap_update_bits(iod->regmap, cfg->offset, reg_mask, reg_val);
+
+ dev_info(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n",
+ cfg->offset, cfg->a_delay, cfg->g_delay, c_elements,
+ f_elements, reg_val);
+
+ return r;
+}
+
+/**
+ * ti_iodelay_pinconf_init_dev() - Initialize IODelay device
+ * @iod: iodelay device
+ *
+ * Unlocks the iodelay region, computes the common parameters
+ *
+ * Return: 0 in case of success, else appropriate error value
+ */
+static int ti_iodelay_pinconf_init_dev(struct ti_iodelay_device *iod)
+{
+ const struct ti_iodelay_reg_data *reg = iod->reg_data;
+ struct device *dev = iod->dev;
+ struct ti_iodelay_reg_values *ival = &iod->reg_init_conf_values;
+ u32 val;
+ int r;
+
+ /* unlock the iodelay region */
+ r = regmap_update_bits(iod->regmap, reg->reg_global_lock_offset,
+ reg->global_lock_mask, reg->global_unlock_val);
+ if (r)
+ return r;
+
+ /* Read up Recalibration sequence done by bootloader */
+ r = regmap_read(iod->regmap, reg->reg_refclk_offset, &val);
+ if (r)
+ return r;
+ ival->ref_clk_period = ti_iodelay_extract(val, reg->refclk_period_mask);
+ dev_dbg(dev, "refclk_period=0x%04x\n", ival->ref_clk_period);
+
+ r = regmap_read(iod->regmap, reg->reg_coarse_offset, &val);
+ if (r)
+ return r;
+ ival->coarse_ref_count =
+ ti_iodelay_extract(val, reg->coarse_ref_count_mask);
+ ival->coarse_delay_count =
+ ti_iodelay_extract(val, reg->coarse_delay_count_mask);
+ if (!ival->coarse_delay_count) {
+ dev_err(dev, "Invalid Coarse delay count (0) (reg=0x%08x)\n",
+ val);
+ return -EINVAL;
+ }
+ ival->cdpe = ti_iodelay_compute_dpe(ival->ref_clk_period,
+ ival->coarse_ref_count,
+ ival->coarse_delay_count, 88);
+ if (!ival->cdpe) {
+ dev_err(dev, "Invalid cdpe computed params = %d %d %d\n",
+ ival->ref_clk_period, ival->coarse_ref_count,
+ ival->coarse_delay_count);
+ return -EINVAL;
+ }
+ dev_dbg(iod->dev, "coarse: ref=0x%04x delay=0x%04x cdpe=0x%08x\n",
+ ival->coarse_ref_count, ival->coarse_delay_count, ival->cdpe);
+
+ r = regmap_read(iod->regmap, reg->reg_fine_offset, &val);
+ if (r)
+ return r;
+ ival->fine_ref_count =
+ ti_iodelay_extract(val, reg->fine_ref_count_mask);
+ ival->fine_delay_count =
+ ti_iodelay_extract(val, reg->fine_delay_count_mask);
+ if (!ival->fine_delay_count) {
+ dev_err(dev, "Invalid Fine delay count (0) (reg=0x%08x)\n",
+ val);
+ return -EINVAL;
+ }
+ ival->fdpe = ti_iodelay_compute_dpe(ival->ref_clk_period,
+ ival->fine_ref_count,
+ ival->fine_delay_count, 264);
+ if (!ival->fdpe) {
+ dev_err(dev, "Invalid fdpe(0) computed params = %d %d %d\n",
+ ival->ref_clk_period, ival->fine_ref_count,
+ ival->fine_delay_count);
+ return -EINVAL;
+ }
+ dev_dbg(iod->dev, "fine: ref=0x%04x delay=0x%04x fdpe=0x%08x\n",
+ ival->fine_ref_count, ival->fine_delay_count, ival->fdpe);
+
+ return 0;
+}
+
+/**
+ * ti_iodelay_pinconf_deinit_dev() - deinit the iodelay device
+ * @iod: IODelay device
+ *
+ * Deinitialize the IODelay device (basically just lock the region back up.
+ */
+static void ti_iodelay_pinconf_deinit_dev(struct ti_iodelay_device *iod)
+{
+ const struct ti_iodelay_reg_data *reg = iod->reg_data;
+
+ /* lock the iodelay region back again */
+ regmap_update_bits(iod->regmap, reg->reg_global_lock_offset,
+ reg->global_lock_mask, reg->global_lock_val);
+}
+
+/**
+ * ti_iodelay_get_pingroup() - Find the group mapped by a group selector
+ * @iod: iodelay device
+ * @selector: Group Selector
+ *
+ * Return: Corresponding group representing group selector
+ */
+static struct ti_iodelay_pingroup *
+ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector)
+{
+ struct group_desc *g;
+
+ g = pinctrl_generic_get_group(iod->pctl, selector);
+ if (!g) {
+ dev_err(iod->dev, "%s could not find pingroup %i\n", __func__,
+ selector);
+
+ return NULL;
+ }
+
+ return g->data;
+}
+
+/**
+ * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index
+ * @iod: iodelay driver instance
+ * @selector: Pin index
+ */
+static unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod,
+ unsigned int selector)
+{
+ const struct ti_iodelay_reg_data *r = iod->reg_data;
+ unsigned int offset;
+
+ offset = selector * r->regmap_config->reg_stride;
+ offset *= r->reg_nr_per_pin;
+ offset += r->reg_start_offset;
+
+ return offset;
+}
+
+/**
+ * ti_iodelay_offset_to_pin() - get a pin index based on the register offset
+ * @iod: iodelay driver instance
+ * @offset: register offset from the base
+ */
+static int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod,
+ unsigned int offset)
+{
+ const struct ti_iodelay_reg_data *r = iod->reg_data;
+ unsigned int index;
+
+ if (offset > r->regmap_config->max_register) {
+ dev_err(iod->dev, "mux offset out of range: 0x%x (0x%x)\n",
+ offset, r->regmap_config->max_register);
+ return -EINVAL;
+ }
+
+ index = (offset - r->reg_start_offset) / r->regmap_config->reg_stride;
+ index /= r->reg_nr_per_pin;
+
+ return index;
+}
+
+/**
+ *
+ * @pctldev: Pin controller driver
+ * @np: Device node
+ * @pinctrl_spec: Parsed arguments from device tree
+ * @pins: Array of pins in the pin group
+ * @pin_index: Pin index in the pin array
+ * @data: Pin controller driver specific data
+ *
+ */
+static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ const struct of_phandle_args *pinctrl_spec,
+ int *pins, int pin_index, void *data)
+{
+ struct ti_iodelay_device *iod;
+ struct ti_iodelay_cfg *cfg = data;
+ const struct ti_iodelay_reg_data *r;
+ struct pinctrl_pin_desc *pd;
+ int pin;
+
+ iod = pinctrl_dev_get_drvdata(pctldev);
+ if (!iod)
+ return -EINVAL;
+
+ r = iod->reg_data;
+
+ if (pinctrl_spec->args_count < r->reg_nr_per_pin) {
+ dev_err(iod->dev, "invalid args_count for spec: %i\n",
+ pinctrl_spec->args_count);
+
+ return -EINVAL;
+ }
+
+ /* Index plus two value cells */
+ cfg[pin_index].offset = pinctrl_spec->args[0];
+ cfg[pin_index].a_delay = pinctrl_spec->args[1] & 0xffff;
+ cfg[pin_index].g_delay = pinctrl_spec->args[2] & 0xffff;
+
+ pin = ti_iodelay_offset_to_pin(iod, cfg[pin_index].offset);
+ if (pin < 0) {
+ dev_err(iod->dev, "could not add functions for %s %ux\n",
+ np->name, cfg[pin_index].offset);
+ return -ENODEV;
+ }
+ pins[pin_index] = pin;
+
+ pd = &iod->pa[pin];
+ pd->drv_data = &cfg[pin_index];
+
+ dev_dbg(iod->dev, "%s offset=%x a_delay = %d g_delay = %d\n",
+ np->name, cfg[pin_index].offset, cfg[pin_index].a_delay,
+ cfg[pin_index].g_delay);
+
+ return 0;
+}
+
+/**
+ * ti_iodelay_dt_node_to_map() - Map a device tree node to appropriate group
+ * @pctldev: pinctrl device representing IODelay device
+ * @np: Node Pointer (device tree)
+ * @map: Pinctrl Map returned back to pinctrl framework
+ * @num_maps: Number of maps (1)
+ *
+ * Maps the device tree description into a group of configuration parameters
+ * for iodelay block entry.
+ *
+ * Return: 0 in case of success, else appropriate error value
+ */
+static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map,
+ unsigned int *num_maps)
+{
+ struct ti_iodelay_device *iod;
+ struct ti_iodelay_cfg *cfg;
+ struct ti_iodelay_pingroup *g;
+ const char *name = "pinctrl-pin-array";
+ int rows, *pins, error = -EINVAL, i;
+
+ iod = pinctrl_dev_get_drvdata(pctldev);
+ if (!iod)
+ return -EINVAL;
+
+ rows = pinctrl_count_index_with_args(np, name);
+ if (rows == -EINVAL)
+ return rows;
+
+ *map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL);
+ if (!*map)
+ return -ENOMEM;
+ *num_maps = 0;
+
+ g = devm_kzalloc(iod->dev, sizeof(*g), GFP_KERNEL);
+ if (!g) {
+ error = -ENOMEM;
+ goto free_map;
+ }
+
+ pins = devm_kzalloc(iod->dev, sizeof(*pins) * rows, GFP_KERNEL);
+ if (!pins)
+ goto free_group;
+
+ cfg = devm_kzalloc(iod->dev, sizeof(*cfg) * rows, GFP_KERNEL);
+ if (!cfg) {
+ error = -ENOMEM;
+ goto free_pins;
+ }
+
+ for (i = 0; i < rows; i++) {
+ struct of_phandle_args pinctrl_spec;
+
+ error = pinctrl_parse_index_with_args(np, name, i,
+ &pinctrl_spec);
+ if (error)
+ goto free_data;
+
+ error = ti_iodelay_node_iterator(pctldev, np, &pinctrl_spec,
+ pins, i, cfg);
+ if (error)
+ goto free_data;
+ }
+
+ g->cfg = cfg;
+ g->ncfg = i;
+ g->config = PIN_CONFIG_END;
+
+ error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g);
+ if (error < 0)
+ goto free_data;
+
+ (*map)->type = PIN_MAP_TYPE_CONFIGS_GROUP;
+ (*map)->data.configs.group_or_pin = np->name;
+ (*map)->data.configs.configs = &g->config;
+ (*map)->data.configs.num_configs = 1;
+ *num_maps = 1;
+
+ return 0;
+
+free_data:
+ devm_kfree(iod->dev, cfg);
+free_pins:
+ devm_kfree(iod->dev, pins);
+free_group:
+ devm_kfree(iod->dev, g);
+free_map:
+ devm_kfree(iod->dev, *map);
+
+ return error;
+}
+
+/**
+ * ti_iodelay_pinconf_group_get() - Get the group configuration
+ * @pctldev: pinctrl device representing IODelay device
+ * @selector: Group selector
+ * @config: Configuration returned
+ *
+ * Return: The configuration if the group is valid, else returns -EINVAL
+ */
+static int ti_iodelay_pinconf_group_get(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ unsigned long *config)
+{
+ struct ti_iodelay_device *iod;
+ struct device *dev;
+ struct ti_iodelay_pingroup *group;
+
+ iod = pinctrl_dev_get_drvdata(pctldev);
+ dev = iod->dev;
+ group = ti_iodelay_get_pingroup(iod, selector);
+
+ if (!group)
+ return -EINVAL;
+
+ *config = group->config;
+ return 0;
+}
+
+/**
+ * ti_iodelay_pinconf_group_set() - Configure the groups of pins
+ * @pctldev: pinctrl device representing IODelay device
+ * @selector: Group selector
+ * @configs: Configurations
+ * @num_configs: Number of configurations
+ *
+ * Return: 0 if all went fine, else appropriate error value.
+ */
+static int ti_iodelay_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct ti_iodelay_device *iod;
+ struct device *dev;
+ struct ti_iodelay_pingroup *group;
+ int i;
+
+ iod = pinctrl_dev_get_drvdata(pctldev);
+ dev = iod->dev;
+ group = ti_iodelay_get_pingroup(iod, selector);
+
+ if (num_configs != 1) {
+ dev_err(dev, "Unsupported number of configurations %d\n",
+ num_configs);
+ return -EINVAL;
+ }
+
+ if (*configs != PIN_CONFIG_END) {
+ dev_err(dev, "Unsupported configuration\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < group->ncfg; i++) {
+ if (ti_iodelay_pinconf_set(iod, &group->cfg[i]))
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void ti_iodelay_pin_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned int pin)
+{
+ struct ti_iodelay_device *iod;
+ struct pinctrl_pin_desc *pd;
+ struct ti_iodelay_cfg *cfg;
+ const struct ti_iodelay_reg_data *r;
+ unsigned long offset;
+ u32 in, oen, out;
+
+ iod = pinctrl_dev_get_drvdata(pctldev);
+ r = iod->reg_data;
+
+ offset = ti_iodelay_pin_to_offset(iod, pin);
+ if (pin < 0) {
+ dev_err(iod->dev, "invalid pin offset for pin%i\n", pin);
+
+ return;
+ }
+
+ pd = &iod->pa[pin];
+ cfg = pd->drv_data;
+
+ regmap_read(iod->regmap, offset, &in);
+ regmap_read(iod->regmap, offset + r->regmap_config->reg_stride, &oen);
+ regmap_read(iod->regmap, offset + r->regmap_config->reg_stride * 2,
+ &out);
+
+ seq_printf(s, "%lx a: %i g: %i (%08x %08x %08x) %s ",
+ iod->phys_base + offset,
+ cfg ? cfg->a_delay : -1,
+ cfg ? cfg->g_delay : -1,
+ in, oen, out, DRIVER_NAME);
+}
+
+/**
+ * ti_iodelay_pinconf_group_dbg_show() - show the group information
+ * @pctldev: Show the group information
+ * @s: Sequence file
+ * @selector: Group selector
+ *
+ * Provide the configuration information of the selected group
+ */
+static void ti_iodelay_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned int selector)
+{
+ struct ti_iodelay_device *iod;
+ struct device *dev;
+ struct ti_iodelay_pingroup *group;
+ int i;
+
+ iod = pinctrl_dev_get_drvdata(pctldev);
+ dev = iod->dev;
+ group = ti_iodelay_get_pingroup(iod, selector);
+ if (!group)
+ return;
+
+ for (i = 0; i < group->ncfg; i++) {
+ struct ti_iodelay_cfg *cfg;
+ u32 reg = 0;
+
+ cfg = &group->cfg[i];
+ regmap_read(iod->regmap, cfg->offset, ®),
+ seq_printf(s, "\n\t0x%08x = 0x%08x (%3d, %3d)",
+ cfg->offset, reg, cfg->a_delay,
+ cfg->g_delay);
+ }
+}
+#endif
+
+static struct pinctrl_ops ti_iodelay_pinctrl_ops = {
+ .get_groups_count = pinctrl_generic_get_group_count,
+ .get_group_name = pinctrl_generic_get_group_name,
+ .get_group_pins = pinctrl_generic_get_group_pins,
+ .pin_dbg_show = ti_iodelay_pin_dbg_show,
+ .dt_node_to_map = ti_iodelay_dt_node_to_map,
+};
+
+static struct pinconf_ops ti_iodelay_pinctrl_pinconf_ops = {
+ .pin_config_group_get = ti_iodelay_pinconf_group_get,
+ .pin_config_group_set = ti_iodelay_pinconf_group_set,
+#ifdef CONFIG_DEBUG_FS
+ .pin_config_group_dbg_show = ti_iodelay_pinconf_group_dbg_show,
+#endif
+};
+
+/**
+ * ti_iodelay_alloc_pins() - Allocate structures needed for pins for iodelay
+ * @dev: Device pointer
+ * @iod: iodelay device
+ * @base_phy: Base Physical Address
+ *
+ * Return: 0 if all went fine, else appropriate error value.
+ */
+static int ti_iodelay_alloc_pins(struct device *dev,
+ struct ti_iodelay_device *iod, u32 base_phy)
+{
+ const struct ti_iodelay_reg_data *r = iod->reg_data;
+ struct pinctrl_pin_desc *pin;
+ u32 phy_reg;
+ int nr_pins, i;
+
+ nr_pins = ti_iodelay_offset_to_pin(iod, r->regmap_config->max_register);
+ dev_dbg(dev, "Allocating %i pins\n", nr_pins);
+
+ iod->pa = devm_kzalloc(dev, sizeof(*iod->pa) * nr_pins, GFP_KERNEL);
+ if (!iod->pa)
+ return -ENOMEM;
+
+ iod->desc.pins = iod->pa;
+ iod->desc.npins = nr_pins;
+
+ phy_reg = r->reg_start_offset + base_phy;
+
+ for (i = 0; i < nr_pins; i++, phy_reg += 4) {
+ pin = &iod->pa[i];
+ pin->number = i;
+ }
+
+ return 0;
+}
+
+static struct regmap_config dra7_iodelay_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xd1c,
+};
+
+static struct ti_iodelay_reg_data dra7_iodelay_data = {
+ .signature_mask = 0x0003f000,
+ .signature_value = 0x29,
+ .lock_mask = 0x00000400,
+ .lock_val = 1,
+ .unlock_val = 0,
+ .binary_data_coarse_mask = 0x000003e0,
+ .binary_data_fine_mask = 0x0000001f,
+
+ .reg_refclk_offset = 0x14,
+ .refclk_period_mask = 0xffff,
+
+ .reg_coarse_offset = 0x18,
+ .coarse_delay_count_mask = 0xffff0000,
+ .coarse_ref_count_mask = 0x0000ffff,
+
+ .reg_fine_offset = 0x1C,
+ .fine_delay_count_mask = 0xffff0000,
+ .fine_ref_count_mask = 0x0000ffff,
+
+ .reg_global_lock_offset = 0x2c,
+ .global_lock_mask = 0x0000ffff,
+ .global_unlock_val = 0x0000aaaa,
+ .global_lock_val = 0x0000aaab,
+
+ .reg_start_offset = 0x30,
+ .reg_nr_per_pin = 3,
+ .regmap_config = &dra7_iodelay_regmap_config,
+};
+
+static const struct of_device_id ti_iodelay_of_match[] = {
+ {.compatible = "ti,dra7-iodelay", .data = &dra7_iodelay_data},
+ { /* Hopefully no more.. */ },
+};
+MODULE_DEVICE_TABLE(of, ti_iodelay_of_match);
+
+/**
+ * ti_iodelay_probe() - Standard probe
+ * @pdev: platform device
+ *
+ * Return: 0 if all went fine, else appropriate error value.
+ */
+static int ti_iodelay_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = of_node_get(dev->of_node);
+ const struct of_device_id *match;
+ struct resource *res;
+ struct ti_iodelay_device *iod;
+ int ret = 0;
+
+ if (!np) {
+ ret = -EINVAL;
+ dev_err(dev, "No OF node\n");
+ goto exit_out;
+ }
+
+ match = of_match_device(ti_iodelay_of_match, dev);
+ if (!match) {
+ ret = -EINVAL;
+ dev_err(dev, "No DATA match\n");
+ goto exit_out;
+ }
+
+ iod = devm_kzalloc(dev, sizeof(*iod), GFP_KERNEL);
+ if (!iod) {
+ ret = -ENOMEM;
+ goto exit_out;
+ }
+ iod->dev = dev;
+ iod->reg_data = match->data;
+
+ /* So far We can assume there is only 1 bank of registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "Missing MEM resource\n");
+ ret = -ENODEV;
+ goto exit_out;
+ }
+
+ iod->phys_base = res->start;
+ iod->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(iod->reg_base)) {
+ ret = PTR_ERR(iod->reg_base);
+ goto exit_out;
+ }
+
+ iod->regmap = devm_regmap_init_mmio(dev, iod->reg_base,
+ iod->reg_data->regmap_config);
+ if (IS_ERR(iod->regmap)) {
+ dev_err(dev, "Regmap MMIO init failed.\n");
+ ret = PTR_ERR(iod->regmap);
+ goto exit_out;
+ }
+
+ if (ti_iodelay_pinconf_init_dev(iod))
+ goto exit_out;
+
+ ret = ti_iodelay_alloc_pins(dev, iod, res->start);
+ if (ret)
+ goto exit_out;
+
+ iod->desc.pctlops = &ti_iodelay_pinctrl_ops;
+ /* no pinmux ops - we are pinconf */
+ iod->desc.confops = &ti_iodelay_pinctrl_pinconf_ops;
+ iod->desc.name = dev_name(dev);
+ iod->desc.owner = THIS_MODULE;
+
+ iod->pctl = pinctrl_register(&iod->desc, dev, iod);
+ if (!iod->pctl) {
+ dev_err(dev, "Failed to register pinctrl\n");
+ ret = -ENODEV;
+ goto exit_out;
+ }
+
+ platform_set_drvdata(pdev, iod);
+
+exit_out:
+ of_node_put(np);
+ return ret;
+}
+
+/**
+ * ti_iodelay_remove() - standard remove
+ * @pdev: platform device
+ *
+ * Return: 0 if all went fine, else appropriate error value.
+ */
+static int ti_iodelay_remove(struct platform_device *pdev)
+{
+ struct ti_iodelay_device *iod = platform_get_drvdata(pdev);
+
+ if (!iod)
+ return 0;
+
+ if (iod->pctl)
+ pinctrl_unregister(iod->pctl);
+
+ ti_iodelay_pinconf_deinit_dev(iod);
+
+ /* Expect other allocations to be freed by devm */
+
+ return 0;
+}
+
+static struct platform_driver ti_iodelay_driver = {
+ .probe = ti_iodelay_probe,
+ .remove = ti_iodelay_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .of_match_table = ti_iodelay_of_match,
+ },
+};
+module_platform_driver(ti_iodelay_driver);
+
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("Pinconf driver for TI's IO Delay module");
+MODULE_LICENSE("GPL v2");
--
2.11.0
^ permalink raw reply
* [PATCH 1/2] pinctrl: core: Make dt_free_map optional
From: Tony Lindgren @ 2016-12-30 18:37 UTC (permalink / raw)
To: Linus Walleij
Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161230183732.5595-1-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
If the pin controller driver is using devm_kzalloc, there may not be
anything to do for dt_free_map. Let's make it optional to avoid
unncessary boilerplate code.
Signed-off-by: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
---
drivers/pinctrl/core.c | 3 ---
drivers/pinctrl/devicetree.c | 3 ++-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -1913,9 +1913,6 @@ static int pinctrl_check_ops(struct pinctrl_dev *pctldev)
!ops->get_group_name)
return -EINVAL;
- if (ops->dt_node_to_map && !ops->dt_free_map)
- return -EINVAL;
-
return 0;
}
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
--- a/drivers/pinctrl/devicetree.c
+++ b/drivers/pinctrl/devicetree.c
@@ -42,7 +42,8 @@ static void dt_free_map(struct pinctrl_dev *pctldev,
{
if (pctldev) {
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
- ops->dt_free_map(pctldev, map, num_maps);
+ if (ops->dt_free_map)
+ ops->dt_free_map(pctldev, map, num_maps);
} else {
/* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */
kfree(map);
--
2.11.0
--
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 0/2] Add TI iodelay driver using #pinctrl-cells
From: Tony Lindgren @ 2016-12-30 18:37 UTC (permalink / raw)
To: Linus Walleij
Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA
Hi all,
Here's a respin of Nishanth's iodelay driver that I've updated to
use #pinctrl-cells and generic pinctrl group and function code.
Gary, note that this one has an iterator function that you may
be able to use too, and hopefully we can simplify things further
eventually by introducing a generic iterator.
Note that these patches are against the current pinctrl devel
branch at commit b61d1546906d ("pinctrl: single: Use generic
pinmux helpers for managing functions").
Regards,
Tony
Nishanth Menon (1):
pinctrl: Introduce TI IOdelay configuration driver
Tony Lindgren (1):
pinctrl: core: Make dt_free_map optional
.../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 +
drivers/pinctrl/Kconfig | 1 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/core.c | 3 -
drivers/pinctrl/devicetree.c | 3 +-
drivers/pinctrl/ti/Kconfig | 10 +
drivers/pinctrl/ti/Makefile | 1 +
drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 945 +++++++++++++++++++++
8 files changed, 1007 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt
create mode 100644 drivers/pinctrl/ti/Kconfig
create mode 100644 drivers/pinctrl/ti/Makefile
create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c
--
2.11.0
--
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 10/20] gpio: pca953x: Add optional reset gpio control
From: Steve Longerbeam @ 2016-12-30 18:03 UTC (permalink / raw)
To: Linus Walleij, LW
Cc: Mark Rutland, Alexandre Courbot, devel@driverdev.osuosl.org,
Philipp Zabel, devicetree@vger.kernel.org, Greg KH, Russell King,
linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org,
Rob Herring, Sascha Hauer, Fabio Estevam, Mauro Carvalho Chehab,
Shawn Guo, linux-arm-kernel@lists.infradead.org,
linux-media@vger.kernel.org
In-Reply-To: <CACRpkdZG2j4_LwP8KUVBTOtXma75YvYtC6CW1QqYwOm-MOZgHg@mail.gmail.com>
Hi Linus, Lothar,
On 12/30/2016 05:17 AM, Linus Walleij wrote:
> On Thu, Dec 29, 2016 at 11:27 PM, Steve Longerbeam
> <slongerbeam@gmail.com> wrote:
>
>> Add optional reset-gpios pin control. If present, de-assert the
>> specified reset gpio pin to bring the chip out of reset.
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> Cc: Linus Walleij <linus.walleij@linaro.org>
>> Cc: Alexandre Courbot <gnurou@gmail.com>
>> Cc: linux-gpio@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org
> This seems like a separate patch from the other 19 patches so please send it
> separately so I can handle logistics easier in the future.
Ok, I'll send the next version separately.
>
>
>> @@ -133,6 +134,7 @@ struct pca953x_chip {
>> const char *const *names;
>> unsigned long driver_data;
>> struct regulator *regulator;
>> + struct gpio_desc *reset_gpio;
> Why do you even keep this around in the device state container?
>
> As you only use it in the probe() function, use a local variable.
>
> The descriptor will be free():ed by the devm infrastructure anyways.
I think my reasoning for putting the gpio handle into the device
struct was for possibly using it for run-time reset control at some
point. But that could be done later if needed, so I'll go ahead and
make it local.
>> + /* see if we need to de-assert a reset pin */
>> + chip->reset_gpio = devm_gpiod_get_optional(&client->dev,
>> + "reset",
>> + GPIOD_OUT_LOW);
>> + if (IS_ERR(chip->reset_gpio)) {
>> + dev_err(&client->dev, "request for reset pin failed\n");
>> + return PTR_ERR(chip->reset_gpio);
>> + }
> Nice.
>
>> + if (chip->reset_gpio) {
>> + /* bring chip out of reset */
>> + dev_info(&client->dev, "releasing reset\n");
>> + gpiod_set_value(chip->reset_gpio, 0);
>> + }
> Is this really needed given that you set it low in the
> devm_gpiod_get_optional()?
Yep, this is left over from a previous iteration, but it isn't needed now.
I'll remove it.
Steve
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox