* [PATCH] tty/serial: at91: fix hardware handshake on Atmel platforms
From: Uwe Kleine-König @ 2016-10-25 17:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025161135.7316-1-richard.genoud@gmail.com>
Hello,
On Tue, Oct 25, 2016 at 06:11:35PM +0200, Richard Genoud wrote:
> commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management when
> hardware handshake is enabled"), despite its title, broke hardware
> handshake on *every* Atmel platforms.
s/platforms/platform/
> The only one partially working is the SAMA5D2.
>
> To understand why, one has to understand the flag ATMEL_US_USMODE_HWHS
> first:
> Before commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management
> when hardware handshake is enabled"), this flag was never set.
> Thus, the CTS/RTS where only handled by serial_core (and everything
> worked just fine).
>
> This commit introduced the use of the ATMEL_US_USMODE_HWHS flag,
> enabling it for all boards when the user space enables flow control.
>
> When the ATMEL_US_USMODE_HWHS is set, the Atmel USART controller
> handles a part of the flow control job:
> - disable the transmitter when the CTS pin gets high.
> - drive the RTS pin high when the DMA buffer transfer is completed or
> PDC RX buffer full or RX FIFO is beyond threshold. (depending on the
> controller version).
I don't understand the DMA buffer part.
> NB: This feature is *not* mandatory for the flow control to work.
>
> Now, the specifics of the ATMEL_US_USMODE_HWHS flag:
>
> - For platforms with DMAC and no FIFOs (sam9x25, sam9x35, sama5D3,
> sama5D4, sam9g15, sam9g25, sam9g35)* this feature simply doesn't work.
> ( source: https://lkml.org/lkml/2016/9/7/598 )
What does "doesn't work" mean? Is ATMEL_US_USMODE_HWHS a noop, or does
it break something?
> Tested it on sam9g35, the RTS pins always stays up, even when RXEN=1
> or a new DMA transfer descriptor is set.
> => ATMEL_US_USMODE_HWHS should not be used for those platforms
Depending on the answer to the above question it might not matter if it
is set or not.
> - For platforms with a PDC (sam926{0,1,3}, sam9g10, sam9g20, sam9g45,
> sam9g46)*, there's another kind of problem. Once the flag
> ATMEL_US_USMODE_HWHS is set, the RTS pin can't be driven anymore via
> RTSEN/RTSDIS in USART Control Register. The RTS pin can only be driven
> by enabling/disabling the receiver or setting RCR=RNCR=0 in the PDC
> (Receive (Next) Counter Register).
> => Doing this is beyond the scope of this patch and could add other
> bugs, so the original (and working) behaviour should be set for those
> platforms (meaning ATMEL_US_USMODE_HWHS flag should be unset).
Then maybe just revert the faulty patch for now and do it better later
on top of this?
> - For platforms with a FIFO (sama5d2)*, the RTS pin is driven according
> to the RX FIFO thresholds, and can be also driven by RTSEN/RTSDIS in
> USART Control Register. No problem here.
> (This was the use case of commit 1cf6e8fc8341 ("tty/serial: at91: fix
> RTS line management when hardware handshake is enabled"))
> NB: If the CTS pin declared as a GPIO in the DTS, (for instance
> cts-gpios = <&pioA PIN_PB31 GPIO_ACTIVE_LOW>), the transmitter will be
> disabled.
> => ATMEL_US_USMODE_HWHS flag can be set for this platform ONLY IF the
> CTS pin is not a GPIO.
How did you test this? What I consider interesting here is if the
hardware CTS function was muxed on a pin and in which state this pin (if
any) is. If it is not muxed anywhere and disables the transmitter
because of an internal pull up that is IMHO a hw bug and should be
mentioned more explicitly in the comment.
> So, the only case when ATMEL_US_USMODE_HWHS can be enabled is when
> (atmel_use_fifo(port) &&
> !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS))
>
> Tested on all Atmel USART controller flavours:
> AT91SAM9G35-CM, AT91SAM9G20-EK and SAMA5D2xplained
> ^^^^ ^^^^ ^^^^
> (DMAC flavour), (PDC flavour) and (FIFO flavour)
I'd write that as: Tested on all Atmel USART controller flavours:
AT91SAM9G35-CM (DMAC flavour), AT91SAM9G20-EK (PDC flavour),
SAMA5D2xplained (FIFO flavour).
> Changes since v4:
> - the mctrl_gpio_use_rtscts() is gone since it was atmel_serial
> specific. (so patch 1 is gone)
> - patches 2 and 3 have been merged together since it didn't make
> a lot of sense to correct the GPIO case in one separate patch.
> - ATMEL_US_USMODE_HWHS is now unset for platform with PDC
>
> Changes since v3:
> - remove superfluous #include <linux/err.h> (thanks to Uwe)
> - rebase on next-20160930
>
> Changes since v2:
> - remove IS_ERR_OR_NULL() test in patch 1/3 as Uwe suggested.
> - fix typos in patch 2/3
> - rebase on next-20160927
> - simplify the logic in patch 3/3.
>
> Changes since v1:
> - Correct patch 1 with the error found by kbuild.
> - Add Alexandre's Acked-by on patch 2
> - Rewrite patch 3 logic in the light of the on-going discussion
> with Cyrille and Alexandre.
>
> * the list may not be exhaustive
Add a Fixes: line please.
> Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
> ---
> drivers/tty/serial/atmel_serial.c | 25 +++++++++++++++++++++----
> 1 file changed, 21 insertions(+), 4 deletions(-)
>
> I think this should go in the stable tree since it fixes the flow
> control broken since v4.0.
> But It won't compile on versions before 4.9rc1 because:
> function atmel_use_fifo was introduced in 4.4.12 / 4.7
> variable atmel_port was introduced in 4.9rc1
>
> That's why I didn't add the Cc stable in the email body.
>
>
> diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
> index fd8aa1f4ba78..2c7c45904ba7 100644
> --- a/drivers/tty/serial/atmel_serial.c
> +++ b/drivers/tty/serial/atmel_serial.c
> @@ -2132,11 +2132,28 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> mode |= ATMEL_US_USMODE_RS485;
> } else if (termios->c_cflag & CRTSCTS) {
> /* RS232 with hardware handshake (RTS/CTS) */
> - if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) {
> - dev_info(port->dev, "not enabling hardware flow control because DMA is used");
> - termios->c_cflag &= ~CRTSCTS;
This if was not introduced in commit 1cf6e8fc8341. Is it still right to
remove this here?
> - } else {
> + if (atmel_use_fifo(port) &&
> + !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) {
> + /*
> + * with ATMEL_US_USMODE_HWHS set, the controller will
> + * be able to drive the RTS pin high/low when the RX
> + * FIFO is above RXFTHRES/below RXFTHRES2.
> + * It will also disable the transmitter when the CTS
> + * pin is high.
> + * This mode is not activated if CTS pin is a GPIO
> + * because in this case, the transmitter is always
> + * disabled.
> + * If the RTS pin is a GPIO, the controller won't be
> + * able to drive it according to the FIFO thresholds,
> + * but it will be handled by the driver.
> + */
> mode |= ATMEL_US_USMODE_HWHS;
> + } else {
> + /*
> + * For platforms without FIFO, the flow control is
> + * handled by the driver.
> + */
> + mode |= ATMEL_US_USMODE_NORMAL;
> }
> } else {
> /* RS232 without hadware handshake */
(unrelated to this patch) s/hadware/hardware/
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* [PATCH 2/3] ARM: convert to generated system call tables
From: Geert Uytterhoeven @ 2016-10-25 17:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4146248.jXuviLlvH5@wuerfel>
On Tue, Oct 25, 2016 at 12:28 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday, October 25, 2016 10:12:10 PM CEST Michael Cree wrote:
>> On Fri, Oct 21, 2016 at 03:06:45PM +0200, Arnd Bergmann wrote:
>> > I see your point, but I think there are serious issues with the current
>> > approach as well:
>> >
>> > - a lot of the less common architectures just don't get updated
>> > in time, out of 22 architectures that don't use asm-generic/unistd.h,
>> > only 12 have pwritev2 in linux-next, and only three have pkey_mprotect
>> >
>> > - some architectures that add all syscalls sometimes make a mistake
>> > and forget one, e.g. alpha apparently never added __NR_bpf, but it
>> > did add the later __NR_execveat.
>>
>> __NR_bpf was not forgotten on Alpha. It was not wired up because
>> extra architecture support is needed which has not been implemented.
>>
>> But maybe we should just wire it up to sys_ni_syscall in the meantime
>> so a syscall number is reserved for it, and user space can call it to
>> get -ENOSYS returned.
>
> Ah, I must have misinterpreted the code then. I assumed that the
> bpf syscall always works on all architectures, but that only the
> jit compiler for it required architecture specific code to make it
> more efficient.
>
> The implementation of sys_bfp is compile-time selectable at the moment
> and falls back to sys_no_syscall if CONFIG_BPF_SYSCALL is disabled.
> If it doesn't work on Alpha, maybe that symbol could have a "depends
> on !ALPHA" or "depends on BPF_SUPPORT"?
Yes, BPF should just work (m68k has it).
> sys_seccomp is another one that falls into a similar category, but
> it already depends on HAVE_ARCH_SECCOMP_FILTER, and most other
> architectures have assigned a syscall number but not set this symbol.
> This one will actually allow you to set strict seccomp mode even
> without the Kconfig symbol, just not allow to set a filter.
Seccomp needs architecture support (m68k doesn't have it).
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH V2] arm64: Neaten show_regs, remove KERN_CONT
From: Mark Rutland @ 2016-10-25 17:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <289fb30081aba03fec0c960e7334f707bf36c881.1477413522.git.joe@perches.com>
On Tue, Oct 25, 2016 at 09:40:26AM -0700, Joe Perches wrote:
> commit db4b0710fae9 ("arm64: fix show_regs fallout from KERN_CONT changes")
> corrected the KERN_CONT fallout from commit 4bcc595ccd80
> ("printk: reinstate KERN_CONT for printing continuation lines"), but
> the code still has unnecessary KERN_CONT uses.
>
> Remove the KERN_CONT uses to avoid possible message interleaving.
>
> Miscellanea:
>
> o Remove unnecessary trailing blank from the output too.
> o Convert i and top_reg to unsigned int
> o Move the extra blank line after __show_reg to the caller for symmetry
> Signed-off-by: Joe Perches <joe@perches.com>
> ---
> arch/arm64/kernel/process.c | 21 ++++++++++-----------
> 1 file changed, 10 insertions(+), 11 deletions(-)
>
> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index 01753cd7d3f0..5ba12f019bf7 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -168,7 +168,7 @@ void machine_restart(char *cmd)
>
> void __show_regs(struct pt_regs *regs)
> {
> - int i, top_reg;
> + unsigned int i, top_reg;
This change is not necessary. These will work perfectly fine as ints;
please leave them as-is.
> u64 lr, sp;
>
> if (compat_user_mode(regs)) {
> @@ -190,24 +190,23 @@ void __show_regs(struct pt_regs *regs)
>
> i = top_reg;
>
> - while (i >= 0) {
> - printk("x%-2d: %016llx ", i, regs->regs[i]);
> + if (i % 2) {
This should be:
if (i % 2 == 0) {
Otherwise we'll lose x0 in the native case (and x29 will be given a line
of its own).
> + printk("x%-2d: %016llx\n", i, regs->regs[i]);
> i--;
> -
> - if (i % 2 == 0) {
> - pr_cont("x%-2d: %016llx ", i, regs->regs[i]);
> - i--;
> - }
> -
> - pr_cont("\n");
> }
> - printk("\n");
> + while (i > 0) {
> + printk("x%-2d: %016llx x%-2d: %016llx\n",
> + i, regs->regs[i],
> + i - 1, regs->regs[i - 1]);
> + i -= 2;
> + }
> }
>
> void show_regs(struct pt_regs * regs)
> {
> printk("\n");
> __show_regs(regs);
> + printk("\n");
> }
Other functions call __show_regs() directly and the trailing newline
there is expected. Please leave it in __show_regs().
Otherwise, this looks fine to me.
Thanks,
Mark.
^ permalink raw reply
* [PATCH 2/3] ARM: convert to generated system call tables
From: Richard Henderson @ 2016-10-25 17:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4146248.jXuviLlvH5@wuerfel>
On 10/25/2016 03:28 AM, Arnd Bergmann wrote:
> On Tuesday, October 25, 2016 10:12:10 PM CEST Michael Cree wrote:
>> On Fri, Oct 21, 2016 at 03:06:45PM +0200, Arnd Bergmann wrote:
>>> I see your point, but I think there are serious issues with the current
>>> approach as well:
>>>
>>> - a lot of the less common architectures just don't get updated
>>> in time, out of 22 architectures that don't use asm-generic/unistd.h,
>>> only 12 have pwritev2 in linux-next, and only three have pkey_mprotect
>>>
>>> - some architectures that add all syscalls sometimes make a mistake
>>> and forget one, e.g. alpha apparently never added __NR_bpf, but it
>>> did add the later __NR_execveat.
>>
>> __NR_bpf was not forgotten on Alpha. It was not wired up because
>> extra architecture support is needed which has not been implemented.
>>
>> But maybe we should just wire it up to sys_ni_syscall in the meantime
>> so a syscall number is reserved for it, and user space can call it to
>> get -ENOSYS returned.
>
> Ah, I must have misinterpreted the code then. I assumed that the
> bpf syscall always works on all architectures, but that only the
> jit compiler for it required architecture specific code to make it
> more efficient.
That was my interpretation as well. What's the problem, Michael?
r~
^ permalink raw reply
* [PATCH v2 0/4] ARM: K2G: Add support for TI-SCI Generic PM Domains
From: Kevin Hilman @ 2016-10-25 17:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161019203347.17893-1-d-gerlach@ti.com>
Dave Gerlach <d-gerlach@ti.com> writes:
> Hi,
> This is v2 of the series to add support for TI SCI PM Domains. v1 of
> the series can be found here [1]. Several things have changed since v1:
>
> - New patch to add a void *data to struct generic_pm_domain_data to
> allow to store per device data associated with a genpd
> - From v1, squash patch 1 and 2 to introduce docs and dt-bindings in
> one patch based on comment from Ulf
> - Fix some grammar errors in Documentation
> - Based on comments from Ulf, rework actual genpd implementation to
> avoid creating one genpd per device and instead use device start/stop
> hooks provided as part of genpd to control device state based on pm_runtime
> implementation. Also make use of new of_genpd_add_provider_simple API
> introduced by Jon Hunter and do not provide custom of_xlate to genpd core,
> instead registering devices as they probe through attach_dev hook provided
> by genpd framework.
>
> Most of the changes were motivated by the comments from Ulf Hannson on v1 that we
> should not use a 1-to-1 genpd to device mapping. The new approach allows us to
> create a single genpd and store information about each device as they attach to
> the genpd. Then the device start/stop hooks for that genpd leverage the
> per-device data to control power states over the TI SCI protocol.
>
> This driver makes use of the ti_sci driver sent here [2] by Nishanth Menon and
> applies on top of his series on v4.9-rc1.
Reviewed-by: Kevin Hilman <khilman@baylibre.com>
^ permalink raw reply
* [PATCH/RFT v2 12/17] USB: ochi-da8xx: Use a regulator for vbus/overcurrent
From: David Lechner @ 2016-10-25 16:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKXjFTOxQ58rThpPw9t73k-BBZ8YKGvMhWfAG1r+3kC=GJVxWg@mail.gmail.com>
On 10/25/2016 03:24 AM, Axel Haslam wrote:
> On Tue, Oct 25, 2016 at 3:39 AM, David Lechner <david@lechnology.com> wrote:
>> On 10/24/2016 11:46 AM, ahaslam at baylibre.com wrote:
>>>
>>> From: Axel Haslam <ahaslam@baylibre.com>
>>>
>>> Currently, the da8xx ohci driver uses a set of gpios and callbacks in
>>> board files to handle vbus and overcurrent irqs form the power supply.
>>> However, this does not play nice when moving to a DT based boot were
>>> we wont have board files.
>>>
>>> Instead of requesting and handling the gpio, use the regulator framework
>>> to take care of enabling and disabling vbus power.
>>> This has the benefit
>>> that we dont need to pass any more platform data to the driver:
>>>
>>> These will be handled by the regulator framework:
>>> set_power -> regulator_enable/regulator_disable
>>> get_power -> regulator_is_enabled
>>> get_oci -> regulator_get_mode
>>> ocic_notify -> regulator notification
>>>
>>> We can keep the default potpgt and use the regulator start delay instead:
>>> potpgt -> regulator startup delay time
>>>
>>> The hawk board does not have a GPIO/OVERCURRENT gpio to control vbus,
>>> (they should not have been decleared/reserved) so, just remove those
>>> definitions from the hwk board file.
>>>
>>> Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
>>> ---
>>
>>
>>
>> How do you recover after an overcurrent event?
>>
>> I have configured a fixed-regulator using device-tree, but similar to the
>> configuration in the board files here. However, when I shorted out the VBUS
>> and caused an overcurrent event, I see nothing in the kernel log saying that
>> there was an overcurrent event and after I remove the short, the regulator
>> is never turned back on.
>>
>>
>
> You should have the patch to fix gpiolib, and you should declare the
> over current gpio on the regulator as such:
> (if the pin is enabled high you should add oc-active-high);
>
> vbus_fixed: fixed-regulator-vbus {
> compatible = "regulator-fixed";
> gpio = <&gpio 109 0>;
> oc-gpio = <&gpio 36 0>;
> regulator-boot-on;
> enable-active-high;
> regulator-name = "vbus";
> regulator-min-microvolt = <5000000>;
> regulator-max-microvolt = <5000000>;
> };
>
>
> Question: Do you see that the over current gpio was requested
> in debugfs/gpio? and, do you see the interrupt in /proc/interrupts?
>
> If you unplug and plug in back the usb device it should work again.
> also you can unbind and bind it should also start to work:
> something like:
>
> echo usb1 >/sys/bus/usb/drivers/usb/unbind
> echo usb1 >/sys/bus/usb/drivers/usb/bind
>
>
I have added oc-active-high and I get different results, but it is still
not quite right. When I short the VBUS, I can see that my overcurrent
gpio changes state. However, the driver does not turn of the VBUS. When
I remove the short, I get an overcurrent error in the kernel log. I
would expect this when I create the short, not when I remove it. I also
tried adding GPIO_ACTIVE_LOW to the oc-gpio, but this did not change
the behavior. In either case, the oc_gpio shows as high under normal
conditions, so perhaps there is a problem with the gpio-davinci driver
not picking up GPIO_ACTIVE_LOW from the device tree.
My regulator is basically the same. My device just uses different gpios.
vbus_reg: vbus-reg {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&usb11_pins>;
gpio = <&gpio 101 GPIO_ACTIVE_LOW>;
oc-gpio = <&gpio 99 0>;
enable-active-high;
oc-active-high;
regulator-name = "vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
}
It seems to me though that I should not have oc-active-high since under
normal conditions, the oc_gpio is high and during an overcurrent event,
the oc_gpio is low. Double-checking the behavior without oc-active-high,
I see that the vbus gpio is turned off in response to the overcurrent
event, but I don't get the overcurrent message in the kernel log.
Perhaps this is because as soon as there is an overcurrent event the
vbus turns off and the oc_gpio returns to normal before the usb driver
has a chance to poll the overcurrent state?
Also, unplugging the device and plugging it back in does nothing.
Unbinding and binding the driver does work, but that does not seem like
a very nice way to have to recover from an overcurrent event.
^ permalink raw reply
* [PATCH 3/4] dt-bindings: Update domain-idle-state binding to use correct compatibles
From: Sudeep Holla @ 2016-10-25 16:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025162440.GA48977@linaro.org>
On 25/10/16 17:24, Lina Iyer wrote:
> On Tue, Oct 25 2016 at 09:59 -0600, Sudeep Holla wrote:
>>
>>
>> On 25/10/16 16:26, Lina Iyer wrote:
>>> Update domain-idle-state binding to use "domain-idle-state" compatible
>>> from Documentation/devicetree/bindings/arm/idle-states.txt.
>>>
>>> Cc: <devicetree@vger.kernel.org>
>>> Cc: Rob Herring <robh@kernel.org>
>>> Suggested-by: Sudeep Holla <sudeep.holla@arm.com>
>>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>> ---
>>> Documentation/devicetree/bindings/power/power_domain.txt | 9 +++++----
>>> 1 file changed, 5 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt
>>> b/Documentation/devicetree/bindings/power/power_domain.txt
>>> index e165036..6fb53a3 100644
>>> --- a/Documentation/devicetree/bindings/power/power_domain.txt
>>> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
>>> @@ -30,8 +30,9 @@ Optional properties:
>>> available in the next section.
>>>
>>> - domain-idle-states : A phandle of an idle-state that shall be
>>> soaked into a
>>> - generic domain power state. The idle state
>>> definitions are
>>> - compatible with arm,idle-state specified in [1].
>>> + generic domain power state. The idle state
>>> definitions must be
>>> + compatible with "domain-idle-state"
>>
>> I would reword the below a bit different so that it's flexible to be
>> reused without "arm,idle-state".
>>
>>> as well as
>>> + "arm,idle-state" as defined in [1].
>>
>> 'Idle states that are "arm,idle-state" compatible are generally
>> "domain-idle-state" compatible as well if it's a PM domain.'
>>
> I believe we should have both compatible strings. Per [1], any CPU that
> follows the idle state compatible *must* have "arm,idle-state" as a
> compatible.
Yes that's implicit for a CPU device. But generic power domain bindings
should not have that explicitly as it *can be* used for non CPU device.
> Since we are re-using the same compatible, its only correct
> that we retain what is already spec'd up in [1] and in addition provide
> this new compatible.
>
Yes [1] applies for *CPUs only* while this applies for *any device* and
*any power domain*, so I would drop *must have* "arm,idle-state" here
to keep this generic based on my understanding on how compatibles work.
--
Regards,
Sudeep
^ permalink raw reply
* [PATCH 2/2] arm64: Support systems without FP/ASIMD
From: Suzuki K Poulose @ 2016-10-25 16:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu9mzX5dUgkJzDYEwxP+u9RVL=RWdsPepQ-UeCCxpx1j_g@mail.gmail.com>
On 25/10/16 15:00, Ard Biesheuvel wrote:
> Hi Suzuki,
>
> On 25 October 2016 at 14:50, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>> The arm64 kernel assumes that FP/ASIMD units are always present
>> and accesses the FP/ASIMD specific registers unconditionally. This
>> could cause problems when they are absent. This patch adds the
>> support for kernel handling systems without FP/ASIMD by skipping the
>> register access within the kernel. For kvm, we trap the accesses
>> to FP/ASIMD and inject an undefined instruction exception to the VM.
>>
>> The callers of the exported kernel_neon_begin_parital() should
>> make sure that the FP/ASIMD is supported.
>>
>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>> Cc: Will Deacon <will.deacon@arm.com>
>> Cc: Christoffer Dall <christoffer.dall@linaro.org>
>> Cc: Marc Zyngier <marc.zyngier@arm.com>
>> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>> ---
>> ---
>> arch/arm64/crypto/aes-ce-ccm-glue.c | 2 +-
>> arch/arm64/crypto/aes-ce-cipher.c | 2 ++
>> arch/arm64/crypto/ghash-ce-glue.c | 2 ++
>> arch/arm64/crypto/sha1-ce-glue.c | 2 ++
>> arch/arm64/include/asm/cpufeature.h | 8 +++++++-
>> arch/arm64/include/asm/neon.h | 3 ++-
>> arch/arm64/kernel/cpufeature.c | 15 +++++++++++++++
>> arch/arm64/kernel/fpsimd.c | 14 ++++++++++++++
>> arch/arm64/kvm/handle_exit.c | 11 +++++++++++
>> arch/arm64/kvm/hyp/hyp-entry.S | 9 ++++++++-
>> arch/arm64/kvm/hyp/switch.c | 5 ++++-
>> 11 files changed, 68 insertions(+), 5 deletions(-)
>>
>> diff --git a/arch/arm64/crypto/aes-ce-ccm-glue.c b/arch/arm64/crypto/aes-ce-ccm-glue.c
>> index f4bf2f2..d001b4e 100644
>> --- a/arch/arm64/crypto/aes-ce-ccm-glue.c
>> +++ b/arch/arm64/crypto/aes-ce-ccm-glue.c
>> @@ -296,7 +296,7 @@ static struct aead_alg ccm_aes_alg = {
>>
>> static int __init aes_mod_init(void)
>> {
>> - if (!(elf_hwcap & HWCAP_AES))
>> + if (!(elf_hwcap & HWCAP_AES) || !system_supports_fpsimd())
>
> This looks weird to me. All crypto extensionsinstructions except CRC
> operate strictly on FP/ASIMD registers, and so support for FP/ASIMD is
> implied by having HWCAP_AES. In other words, I think it makes more
> sense to sanity check that the info registers are consistent with each
> other in core code than modifying each user (which for HWCAP_xxx
> includes userland) to double check that the world is sane.
You're right. I will respin it.
Btw, I think we need the following feature check for the above. I will send
that in and remove the explicit HWCAP_AES check.
module_cpu_feature_match(AES, aes_mod_init());
Cheers
Suzuki
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply
* [PATCH] arm64: Neaten show_regs, remove KERN_CONT
From: Joe Perches @ 2016-10-25 16:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025143237.GD8898@leverpostej>
(adding Linus Torvalds)
On Tue, 2016-10-25 at 15:32 +0100, Mark Rutland wrote:
> On Mon, Oct 24, 2016 at 09:27:57AM -0700, Joe Perches wrote:
> > On Mon, 2016-10-24 at 12:31 +0100, Mark Rutland wrote:
> > > On Sun, Oct 23, 2016 at 01:40:49PM -0700, Joe Perches wrote:
> > > > commit db4b0710fae9 ("arm64: fix show_regs fallout from KERN_CONT changes")
> > > > corrected the KERN_CONT fallout from commit 4bcc595ccd80
> > > > ("printk: reinstate KERN_CONT for printing continuation lines"), but
> > > > the code still has unnecessary KERN_CONT uses. Remove them.
> > >
> > > Why are these unnecessary KERN_CONTs a larger problem than duplicating
> > > the format string for a third time? Having to duplicate it at all was
> > > annoying enough.
> >
> > Not printing partial lines is the best solution to avoiding
> > message output interleaving.
>
> Looking further, it seems that KERN_CONT is terminally broken. The core
> code somehow swallows newlines from some KERN_CONT prints in a
> non-deterministic fashion, and also appears to insert newlines from thin
> air. This happens in the absence of intervening printks.
>
> With the current code in v4.9-rc2, we get output like:
>
> x29: 0000ffffe4938c80 x28: 0000000000000000
> x27: 0000000000000000 x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
> x23: 0000000000000000 x22: 0000000000000000
> x21: 0000000000400470 x20: 0000000000000000
> x19: 0000000000000000 x18: 0000ffffe4938b60
> x17: 0000000000411000 x16: 0000ffff82f72c9c
> x15: 0000ffff830c8000 x14: 0000000000000040
> x13: 0000ffff830c8028 x12: 0000000000008738
> x11: 0000000000000008
> x10: 00000000ffffffff
> x9 : 0000ffff830b4e40 x8 : 2f2f2f2f2f2f2f2f
> x7 : b3b3bab7acff8b8a x6 : 0000ffff83097aa8
> x5 : 54d58839205d3679 x4 : 0000000000000000
> x3 : 00000000004005d0 x2 : ffff000000000000
> x1 : 0000ffffe4938e08 x0 : ffff000000000000
>
> ... or:
>
> x29: 0000fffff6f6a600 x28: 0000000000000000 x27: 0000000000000000 x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000 x23: 0000000000000000 x22: 0000000000000000 x21: 0000000000400470 x20: 0000000000000000 x19: 0000000000000000 x18: 0000fffff6f6a4e0 x17: 0000000000411000 x16: 0000ffffa6e1fc9c x15: 0000ffffa6f75000 x14: 0000000000000040
> x13: 0000ffffa6f75028 x12: 0000000000008738 x11: 0000000000000008 x10: 00000000ffffffff
> x9 : 0000ffffa6f61e40 x8 : 2f2f2f2f2f2f2f2f x7 : b3b3bab7acff8b8a x6 : 0000ffffa6f44aa8
> x5 : 874b6ebb9d5e2f3d x4 : 0000000000000000 x3 : 00000000004005d0 x2 : ffff000000000000
> x1 : 0000fffff6f6a788 x0 : ffff000000000000
>
> ... and of course, the buffer shown by $(dmesg) or $(demsg -T) is equally
> insane, but different.
>
> I found that adding a space prior to newlines prevented them from being
> swallowed, but $(dmesg) would still suffer from random additions.
>
> Given all that, unless the core code is changed to as to behave
> deterministically at least for trivial cases like this one, I think we
> should avoid KERN_CONT like the plague.
>
> So FWIW, so long as you fold in the changes I requested in my other
> reply, please add:
>
> Acked-by: Mark Rutland <mark.rutland@arm.com>
>
> ... I'll go fix up show_pte() without pr_cont().
>
> Thanks,
> Mark.
^ permalink raw reply
* [PATCH] drm: convert DT component matching to component_match_add_release()
From: Sean Paul @ 2016-10-25 16:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <E1bwo6l-0005Io-Q1@rmk-PC.armlinux.org.uk>
On Wed, Oct 19, 2016 at 6:28 AM, Russell King
<rmk+kernel@armlinux.org.uk> wrote:
> Convert DT component matching to use component_match_add_release().
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Applied to drm-misc, thanks!
Sean
> ---
> Can we please get this patch from May merged into the drm-misc or
> whatever trees so that we don't end up with conflicts? I've no idea
> who looks after drm-misc, as they have _still_ failed to add
> themselves to MAINTAINERS.
>
> drivers/gpu/drm/arm/hdlcd_drv.c | 3 ++-
> drivers/gpu/drm/arm/malidp_drv.c | 4 +++-
> drivers/gpu/drm/armada/armada_drv.c | 2 +-
> drivers/gpu/drm/drm_of.c | 28 +++++++++++++++++++++++--
> drivers/gpu/drm/etnaviv/etnaviv_drv.c | 5 +++--
> drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 7 ++++---
> drivers/gpu/drm/mediatek/mtk_drm_drv.c | 4 +++-
> drivers/gpu/drm/msm/msm_drv.c | 12 ++++++-----
> drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 6 ++++--
> drivers/gpu/drm/sti/sti_drv.c | 5 +++--
> drivers/gpu/drm/sun4i/sun4i_drv.c | 3 ++-
> drivers/gpu/drm/tilcdc/tilcdc_external.c | 4 +++-
> include/drm/drm_of.h | 12 +++++++++++
> 13 files changed, 73 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
> index fb6a418ce6be..6477d1a65266 100644
> --- a/drivers/gpu/drm/arm/hdlcd_drv.c
> +++ b/drivers/gpu/drm/arm/hdlcd_drv.c
> @@ -453,7 +453,8 @@ static int hdlcd_probe(struct platform_device *pdev)
> return -EAGAIN;
> }
>
> - component_match_add(&pdev->dev, &match, compare_dev, port);
> + drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
> + of_node_put(port);
>
> return component_master_add_with_match(&pdev->dev, &hdlcd_master_ops,
> match);
> diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
> index 9280358b8f15..9f4739452a25 100644
> --- a/drivers/gpu/drm/arm/malidp_drv.c
> +++ b/drivers/gpu/drm/arm/malidp_drv.c
> @@ -493,7 +493,9 @@ static int malidp_platform_probe(struct platform_device *pdev)
> return -EAGAIN;
> }
>
> - component_match_add(&pdev->dev, &match, malidp_compare_dev, port);
> + drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev,
> + port);
> + of_node_put(port);
> return component_master_add_with_match(&pdev->dev, &malidp_master_ops,
> match);
> }
> diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> index 1e0e68f608e4..94e46da9a758 100644
> --- a/drivers/gpu/drm/armada/armada_drv.c
> +++ b/drivers/gpu/drm/armada/armada_drv.c
> @@ -254,7 +254,7 @@ static void armada_add_endpoints(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, match, compare_of, remote);
> + drm_of_component_match_add(dev, match, compare_of, remote);
> of_node_put(remote);
> }
> }
> diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
> index bc98bb94264d..47848ed8ca48 100644
> --- a/drivers/gpu/drm/drm_of.c
> +++ b/drivers/gpu/drm/drm_of.c
> @@ -6,6 +6,11 @@
> #include <drm/drm_crtc.h>
> #include <drm/drm_of.h>
>
> +static void drm_release_of(struct device *dev, void *data)
> +{
> + of_node_put(data);
> +}
> +
> /**
> * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node
> * @dev: DRM device
> @@ -64,6 +69,24 @@ uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
> EXPORT_SYMBOL(drm_of_find_possible_crtcs);
>
> /**
> + * drm_of_component_match_add - Add a component helper OF node match rule
> + * @master: master device
> + * @matchptr: component match pointer
> + * @compare: compare function used for matching component
> + * @node: of_node
> + */
> +void drm_of_component_match_add(struct device *master,
> + struct component_match **matchptr,
> + int (*compare)(struct device *, void *),
> + struct device_node *node)
> +{
> + of_node_get(node);
> + component_match_add_release(master, matchptr, drm_release_of,
> + compare, node);
> +}
> +EXPORT_SYMBOL_GPL(drm_of_component_match_add);
> +
> +/**
> * drm_of_component_probe - Generic probe function for a component based master
> * @dev: master device containing the OF node
> * @compare_of: compare function used for matching components
> @@ -101,7 +124,7 @@ int drm_of_component_probe(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, &match, compare_of, port);
> + drm_of_component_match_add(dev, &match, compare_of, port);
> of_node_put(port);
> }
>
> @@ -140,7 +163,8 @@ int drm_of_component_probe(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, &match, compare_of, remote);
> + drm_of_component_match_add(dev, &match, compare_of,
> + remote);
> of_node_put(remote);
> }
> of_node_put(port);
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> index aa687669e22b..0dee6acbd880 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> @@ -16,6 +16,7 @@
>
> #include <linux/component.h>
> #include <linux/of_platform.h>
> +#include <drm/drm_of.h>
>
> #include "etnaviv_drv.h"
> #include "etnaviv_gpu.h"
> @@ -629,8 +630,8 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
> if (!core_node)
> break;
>
> - component_match_add(&pdev->dev, &match, compare_of,
> - core_node);
> + drm_of_component_match_add(&pdev->dev, &match,
> + compare_of, core_node);
> of_node_put(core_node);
> }
> } else if (dev->platform_data) {
> diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
> index 90377a609c98..e88fde18c946 100644
> --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
> +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
> @@ -24,6 +24,7 @@
> #include <drm/drm_fb_cma_helper.h>
> #include <drm/drm_atomic_helper.h>
> #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
>
> #include "kirin_drm_drv.h"
>
> @@ -260,14 +261,13 @@ static struct device_node *kirin_get_remote_node(struct device_node *np)
> DRM_ERROR("no valid endpoint node\n");
> return ERR_PTR(-ENODEV);
> }
> - of_node_put(endpoint);
>
> remote = of_graph_get_remote_port_parent(endpoint);
> + of_node_put(endpoint);
> if (!remote) {
> DRM_ERROR("no valid remote node\n");
> return ERR_PTR(-ENODEV);
> }
> - of_node_put(remote);
>
> if (!of_device_is_available(remote)) {
> DRM_ERROR("not available for remote node\n");
> @@ -294,7 +294,8 @@ static int kirin_drm_platform_probe(struct platform_device *pdev)
> if (IS_ERR(remote))
> return PTR_ERR(remote);
>
> - component_match_add(dev, &match, compare_of, remote);
> + drm_of_component_match_add(dev, &match, compare_of, remote);
> + of_node_put(remote);
>
> return component_master_add_with_match(dev, &kirin_drm_ops, match);
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index cf83f6507ec8..9c5430fb82a2 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -18,6 +18,7 @@
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_gem.h>
> #include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> #include <linux/component.h>
> #include <linux/iommu.h>
> #include <linux/of_address.h>
> @@ -415,7 +416,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
> comp_type == MTK_DPI) {
> dev_info(dev, "Adding component match for %s\n",
> node->full_name);
> - component_match_add(dev, &match, compare_of, node);
> + drm_of_component_match_add(dev, &match, compare_of,
> + node);
> } else {
> struct mtk_ddp_comp *comp;
>
> diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
> index fb5c0b0a7594..84d38eaea585 100644
> --- a/drivers/gpu/drm/msm/msm_drv.c
> +++ b/drivers/gpu/drm/msm/msm_drv.c
> @@ -15,6 +15,8 @@
> * this program. If not, see <http://www.gnu.org/licenses/>.
> */
>
> +#include <drm/drm_of.h>
> +
> #include "msm_drv.h"
> #include "msm_debugfs.h"
> #include "msm_fence.h"
> @@ -919,8 +921,8 @@ static int add_components_mdp(struct device *mdp_dev,
> continue;
> }
>
> - component_match_add(master_dev, matchptr, compare_of, intf);
> -
> + drm_of_component_match_add(master_dev, matchptr, compare_of,
> + intf);
> of_node_put(intf);
> of_node_put(ep_node);
> }
> @@ -962,8 +964,8 @@ static int add_display_components(struct device *dev,
> put_device(mdp_dev);
>
> /* add the MDP component itself */
> - component_match_add(dev, matchptr, compare_of,
> - mdp_dev->of_node);
> + drm_of_component_match_add(dev, matchptr, compare_of,
> + mdp_dev->of_node);
> } else {
> /* MDP4 */
> mdp_dev = dev;
> @@ -996,7 +998,7 @@ static int add_gpu_components(struct device *dev,
> if (!np)
> return 0;
>
> - component_match_add(dev, matchptr, compare_of, np);
> + drm_of_component_match_add(dev, matchptr, compare_of, np);
>
> of_node_put(np);
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 8c8cbe837e61..6fe161192bb4 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -20,6 +20,7 @@
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_fb_helper.h>
> #include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> #include <linux/dma-mapping.h>
> #include <linux/pm_runtime.h>
> #include <linux/module.h>
> @@ -388,7 +389,7 @@ static void rockchip_add_endpoints(struct device *dev,
> continue;
> }
>
> - component_match_add(dev, match, compare_of, remote);
> + drm_of_component_match_add(dev, match, compare_of, remote);
> of_node_put(remote);
> }
> }
> @@ -437,7 +438,8 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
> }
>
> of_node_put(iommu);
> - component_match_add(dev, &match, compare_of, port->parent);
> + drm_of_component_match_add(dev, &match, compare_of,
> + port->parent);
> of_node_put(port);
> }
>
> diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
> index 2784919a7366..5e819876e642 100644
> --- a/drivers/gpu/drm/sti/sti_drv.c
> +++ b/drivers/gpu/drm/sti/sti_drv.c
> @@ -17,6 +17,7 @@
> #include <drm/drm_crtc_helper.h>
> #include <drm/drm_gem_cma_helper.h>
> #include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_of.h>
>
> #include "sti_crtc.h"
> #include "sti_drv.h"
> @@ -423,8 +424,8 @@ static int sti_platform_probe(struct platform_device *pdev)
> child_np = of_get_next_available_child(node, NULL);
>
> while (child_np) {
> - component_match_add(dev, &match, compare_of, child_np);
> - of_node_put(child_np);
> + drm_of_component_match_add(dev, &match, compare_of,
> + child_np);
> child_np = of_get_next_available_child(node, child_np);
> }
>
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 0da9862ad8ed..b3c4ad605e81 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -18,6 +18,7 @@
> #include <drm/drm_fb_cma_helper.h>
> #include <drm/drm_gem_cma_helper.h>
> #include <drm/drm_fb_helper.h>
> +#include <drm/drm_of.h>
>
> #include "sun4i_crtc.h"
> #include "sun4i_drv.h"
> @@ -239,7 +240,7 @@ static int sun4i_drv_add_endpoints(struct device *dev,
> /* Add current component */
> DRM_DEBUG_DRIVER("Adding component %s\n",
> of_node_full_name(node));
> - component_match_add(dev, match, compare_of, node);
> + drm_of_component_match_add(dev, match, compare_of, node);
> count++;
> }
>
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> index 68e895021005..06a4c584f3cb 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> @@ -10,6 +10,7 @@
>
> #include <linux/component.h>
> #include <linux/of_graph.h>
> +#include <drm/drm_of.h>
>
> #include "tilcdc_drv.h"
> #include "tilcdc_external.h"
> @@ -160,7 +161,8 @@ int tilcdc_get_external_components(struct device *dev,
>
> dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
> if (match)
> - component_match_add(dev, match, dev_match_of, node);
> + drm_of_component_match_add(dev, match, dev_match_of,
> + node);
> of_node_put(node);
> count++;
> }
> diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h
> index 3fd87b386ed7..d6b4c5587bbe 100644
> --- a/include/drm/drm_of.h
> +++ b/include/drm/drm_of.h
> @@ -4,6 +4,7 @@
> #include <linux/of_graph.h>
>
> struct component_master_ops;
> +struct component_match;
> struct device;
> struct drm_device;
> struct drm_encoder;
> @@ -12,6 +13,10 @@ struct device_node;
> #ifdef CONFIG_OF
> extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
> struct device_node *port);
> +extern void drm_of_component_match_add(struct device *master,
> + struct component_match **matchptr,
> + int (*compare)(struct device *, void *),
> + struct device_node *node);
> extern int drm_of_component_probe(struct device *dev,
> int (*compare_of)(struct device *, void *),
> const struct component_master_ops *m_ops);
> @@ -25,6 +30,13 @@ static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
> return 0;
> }
>
> +static void drm_of_component_match_add(struct device *master,
> + struct component_match **matchptr,
> + int (*compare)(struct device *, void *),
> + struct device_node *node)
> +{
> +}
> +
> static inline int
> drm_of_component_probe(struct device *dev,
> int (*compare_of)(struct device *, void *),
> --
> 2.1.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH V2] arm64: Neaten show_regs, remove KERN_CONT
From: Joe Perches @ 2016-10-25 16:40 UTC (permalink / raw)
To: linux-arm-kernel
commit db4b0710fae9 ("arm64: fix show_regs fallout from KERN_CONT changes")
corrected the KERN_CONT fallout from commit 4bcc595ccd80
("printk: reinstate KERN_CONT for printing continuation lines"), but
the code still has unnecessary KERN_CONT uses.
Remove the KERN_CONT uses to avoid possible message interleaving.
Miscellanea:
o Remove unnecessary trailing blank from the output too.
o Convert i and top_reg to unsigned int
o Move the extra blank line after __show_reg to the caller for symmetry
Signed-off-by: Joe Perches <joe@perches.com>
---
arch/arm64/kernel/process.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 01753cd7d3f0..5ba12f019bf7 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -168,7 +168,7 @@ void machine_restart(char *cmd)
void __show_regs(struct pt_regs *regs)
{
- int i, top_reg;
+ unsigned int i, top_reg;
u64 lr, sp;
if (compat_user_mode(regs)) {
@@ -190,24 +190,23 @@ void __show_regs(struct pt_regs *regs)
i = top_reg;
- while (i >= 0) {
- printk("x%-2d: %016llx ", i, regs->regs[i]);
+ if (i % 2) {
+ printk("x%-2d: %016llx\n", i, regs->regs[i]);
i--;
-
- if (i % 2 == 0) {
- pr_cont("x%-2d: %016llx ", i, regs->regs[i]);
- i--;
- }
-
- pr_cont("\n");
}
- printk("\n");
+ while (i > 0) {
+ printk("x%-2d: %016llx x%-2d: %016llx\n",
+ i, regs->regs[i],
+ i - 1, regs->regs[i - 1]);
+ i -= 2;
+ }
}
void show_regs(struct pt_regs * regs)
{
printk("\n");
__show_regs(regs);
+ printk("\n");
}
static void tls_thread_flush(void)
--
2.10.0.rc2.1.g053435c
^ permalink raw reply related
* [PATCH] exynos-drm: Fix display manager failing to start without IOMMU problem
From: Shuah Khan @ 2016-10-25 16:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <49780a7f-59a4-d2b0-380c-4bd330660ba2@osg.samsung.com>
On 10/19/2016 04:27 PM, Shuah Khan wrote:
> On 10/19/2016 08:16 AM, Inki Dae wrote:
>> Hi Shuah,
>>
>> 2016-10-13 8:11 GMT+09:00 Shuah Khan <shuahkh@osg.samsung.com>:
>>> Hi Inki,
>>>
>>> On 08/15/2016 10:40 PM, Inki Dae wrote:
>>>
>>>>>
>>>>> okay the very first commit that added IOMMU support
>>>>> introduced the code that rejects non-contig gem memory
>>>>> type without IOMMU.
>>>>>
>>>>> commit 0519f9a12d0113caab78980c48a7902d2bd40c2c
>>>>> Author: Inki Dae <inki.dae@samsung.com>
>>>>> Date: Sat Oct 20 07:53:42 2012 -0700
>>>>>
>>>>> drm/exynos: add iommu support for exynos drm framework
>>>>>
>>>
>>> I haven't given up on this yet. I am still seeing the following failure:
>>>
>>> Additional debug messages I added:
>>> [ 15.287403] exynos_drm_gem_create_ioctl() 1
>>> [ 15.287419] exynos_drm_gem_create() flags 1
>>>
>>> [ 15.311511] [drm:exynos_drm_framebuffer_init] *ERROR* Non-contiguous GEM memory is not supported.
>>>
>>> Additional debug message I added:
>>> [ 15.318981] [drm:exynos_user_fb_create] *ERROR* failed to initialize framebuffer
>>>
>>> This is what happens:
>>>
>>> 1. exynos_drm_gem_create_ioctl() gets called with EXYNOS_BO_NONCONTIG request
>>> 2. exynos_drm_gem_create(0 goes ahead and creates the GEM buffers
>>> 3. exynos_user_fb_create() tries to associate GEM to fb and fails during
>>> check_fb_gem_memory_type()
>>>
>>> At this point, there is no recovery and lightdm fails
>>>
>>> xf86-video-armsoc/src/drmmode_exynos/drmmode_exynos.c assumes contiguous
>>> allocations are not supported in some exynos drm versions: The following
>>> commit introduced this change:
>>>
>>> https://git.linaro.org/arm/xorg/driver/xf86-video-armsoc.git/commitdiff/3be1f6273441fe95dd442f44064387322e16b7e9
>>>
>>> excerpts from the diff:- if (create_gem->buf_type == ARMSOC_BO_SCANOUT)
>>> - create_exynos.flags = EXYNOS_BO_CONTIG;
>>> - else
>>> - create_exynos.flags = EXYNOS_BO_NONCONTIG;
>>> +
>>> + /* Contiguous allocations are not supported in some exynos drm versions.
>>> + * When they are supported all allocations are effectively contiguous
>>> + * anyway, so for simplicity we always request non contiguous buffers.
>>> + */
>>> + create_exynos.flags = EXYNOS_BO_NONCONTIG;
>>>
>>
>> Above comment, "Contiguous allocations are not supported in some
>> exynos drm versions.", seems wrong assumption.
>> The root cause, contiguous allocation is not supported, would be that
>> they used Linux kernel which didn't have CMA region enough - as
>> default 16MB, or didn't declare CMA region enough for the DMA device.
>> So I think they should not force to flag EXYNOS_BO_NONCONTIG and they
>> should manage the error case if the allocation failed.
>
> This assumption doesn't sound correct and forcing NONCONTIG isn't right
> either.
>
>>
>>> There might have been logic on exynos_drm that forced Contig when it coudn't
>>> support NONCONTIG. At least, that is what this comment suggests. This assumption
>>> doesn't appear to be a good one and not sure if this change was made to fix a bug.
>>>
>>> After the IOMMU support, this assumption is no longer true. Hence, with IOMMU
>>> support, latest kernels have a mismatch with the installed xf86-video-armsoc
>>>
>>> This is what I am running into. This leads to the following question:
>>>
>>> 1. How do we ensure exynos_drm kernel changes don't break user-space
>>> specifically xf86-video-armsoc
>>> 2. This seems to have gone undetected for a while. I see a change in
>>> exynos_drm_gem_dumb_create() that is probably addressing this type
>>> of breakage. Commit 122beea84bb90236b1ae545f08267af58591c21b adds
>>> handling for IOMMU NONCONTIG case.
>>
>> Seems this patch has a problem. This patch forces to flag NONCONTIG if
>> iommu is enabled. The flag should be depend on the argument from
>> user-space.
>> I.e., if user-space requested a gem allocation with CONTIG flag, then
>> Exynos drm driver should allocate contiguous memory even though iommu
>> is enabled.
>>
>>>
>>> Anyway, I am interested in getting the exynos_drm kernel side code
>>> and xf86-video-armsoc in sync to resolve the issue.
>>>
>>> Could you recommend a going forward plan?
>>
>> My opinion are,
>>
>> 1. Do not force to flag EXYNOS_BO_NONCONTIG at xf86-video-armsoc
Okay more on this. I fixed xf86-video-armso to ask for EXYNOS_BO_CONTIG
for ARMSOC_BO_SCANOUT and EXYNOS_BO_NONCONTIG in all other cases.
Revert of the commit: 3be1f6273441fe95dd442f44064387322e16b7e9
With this change, now display manager starts just fine. However, it turns
out xf86-video-armsoc is obsoleted in favor of xf86-video-modesetting. The
last update to xf86-video-armsoc git was 3 years ago.
I am not sure where to send the fix and doesn't look like it is being
maintained actively. I can pursue it further and try to get this into
xf86-video-armsoc provided I can find the maintainer for this seemingly
inactive project.
I brought in the latest xf86-video-modesetting bits from
https://cgit.freedesktop.org/xorg/driver/xf86-video-modesetting
I removed xf86-video-armsoc and installed xf86-video-modesetting and that
worked just fine. xf86-video-modesetting uses dumb_create interface instead
of DRM_IOCTL_EXYNOS_GEM_CREATE.
dumb_create interface eliminates the need for DRM_IOCTL_EXYNOS_GEM_CREATE
in userspace. libdrm-2.4.71 does call DRM_IOCTL_EXYNOS_GEM_CREATE.
The question is do we still need to continue to support the custom gem
create interface DRM_IOCTL_EXYNOS_GEM_CREATE? Some drm drivers seem to
support it and some don't. Unfortunately, if userspace requests noncontig
for scanout, we will continue to see problems with display manager when
iommu is disabled. dumb create hides all of that, are there good reasons
to continue to support it in exynos drm?
Exposing CONTIG and NONCONTIG to userspace appears to be causing problems
when exynos drm determines it can't support non-contig GEM buffers in fb
init after userspace allocates them.
>> 2. Do not force to flag NONCONTIG at Exynos drm driver even though
>> iommu is enabled and flag allocation type with the argument from
>> user-space.
>>
exynos_drm_gem_dumb_create() doesn't take any flags, there is no need
to change the above.
thanks,
-- Shuah
^ permalink raw reply
* [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Configure STM32 ADC to use dma by default.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 3fcd941..a00f7a6 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -186,6 +186,8 @@
#io-channel-cells = <1>;
reg = <0x0>;
clocks = <&rcc 0 168>;
+ dmas = <&dma2 0 0 0x400 0x0>;
+ dma-names = "rx";
status = "disabled";
};
@@ -201,6 +203,8 @@
#io-channel-cells = <1>;
reg = <0x100>;
clocks = <&rcc 0 169>;
+ dmas = <&dma2 3 1 0x400 0x0>;
+ dma-names = "rx";
status = "disabled";
};
@@ -216,6 +220,8 @@
#io-channel-cells = <1>;
reg = <0x200>;
clocks = <&rcc 0 170>;
+ dmas = <&dma2 1 2 0x400 0x0>;
+ dma-names = "rx";
status = "disabled";
};
--
1.9.1
^ permalink raw reply related
* [PATCH 09/10] ARM: dts: stm32f429: enable adc on eval board
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Enable analog to digital converter on stm32f429i-eval board.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 6bfc595..0e8c900 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -65,6 +65,20 @@
serial0 = &usart1;
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_vref: regulator at 0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
leds {
compatible = "gpio-leds";
green {
@@ -123,3 +137,19 @@
pinctrl-names = "default";
status = "okay";
};
+
+&adc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ vref-supply = <®_vref>;
+ status = "okay";
+ adc3: adc3-slave at 200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+
+ jadc3: adc3-injected at 200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 08/10] ARM: dts: stm32f429: Add adc support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add adc support & pinctrl analog phandle (adc3_in8) to stm32f429.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 62 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..3fcd941 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -172,6 +172,62 @@
status = "disabled";
};
+ adc: adc at 40012000 {
+ compatible = "st,stm32f4-adc";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adc1: adc1-master at 0 {
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ status = "disabled";
+ };
+
+ jadc1: adc1-injected at 0 {
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ st,injected;
+ status = "disabled";
+ };
+
+ adc2: adc2-slave at 100 {
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ status = "disabled";
+ };
+
+ jadc2: adc2-injected at 100 {
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ st,injected;
+ status = "disabled";
+ };
+
+ adc3: adc3-slave at 200 {
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ status = "disabled";
+ };
+
+ jadc3: adc3-injected at 200 {
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ st,injected;
+ status = "disabled";
+ };
+ };
+
syscfg: system-config at 40013800 {
compatible = "syscon";
reg = <0x40013800 0x400>;
@@ -332,6 +388,12 @@
slew-rate = <2>;
};
};
+
+ adc3_in8_pin: adc at 0 {
+ pins {
+ pinmux = <STM32F429_PF10_FUNC_ANALOG>;
+ };
+ };
};
rcc: rcc at 40023810 {
--
1.9.1
^ permalink raw reply related
* [PATCH 07/10] ARM: configs: stm32: enable IIO ADC driver
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/configs/stm32_defconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 1e5ec2a..5a5f50c 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -57,6 +57,8 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_DMADEVICES=y
CONFIG_STM32_DMA=y
+CONFIG_IIO=y
+CONFIG_STM32F4_ADC=y
# CONFIG_FILE_LOCKING is not set
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
--
1.9.1
^ permalink raw reply related
* [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add per channel "smpr" IIO extended attribute, to allow sampling
time configuration.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 75 +++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 25 +++++++++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 48 ++++++++++++++++++++++++
3 files changed, 148 insertions(+)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 9b4b459..1681f75 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -38,6 +38,61 @@
#include "stm32-adc.h"
/**
+ * stm32_adc_conf_smp() - Configure sampling time for each channel
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ */
+static int stm32_adc_conf_smp(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regs *smp =
+ adc->common->data->adc_reginfo->smpr_regs;
+ struct stm32_adc_chan *stm32_chan;
+ const struct iio_chan_spec *chan;
+ u32 bit, val;
+ int i;
+
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ chan = indio_dev->channels + bit;
+ stm32_chan = to_stm32_chan(adc, chan);
+ i = chan->channel;
+
+ if (i >= adc->max_channels)
+ return -EINVAL;
+
+ val = stm32_adc_readl(adc, smp[i].reg);
+ val &= ~smp[i].mask;
+ val |= (stm32_chan->smpr << smp[i].shift) & smp[i].mask;
+ stm32_adc_writel(adc, smp[i].reg, val);
+ }
+
+ return 0;
+}
+
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int smpr)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+ stm32_chan->smpr = smpr;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_set_smpr);
+
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+ return stm32_chan->smpr;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_get_smpr);
+
+/**
* stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
* @indio_dev: IIO device
* @scan_mask: channels to be converted
@@ -126,6 +181,12 @@ static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
return ret;
}
+ ret = stm32_adc_conf_smp(indio_dev, scan_mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to configure samp time\n");
+ goto err_dis;
+ }
+
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
if (ret) {
dev_err(&indio_dev->dev, "Failed to configure sequence\n");
@@ -938,6 +999,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
const struct stm32_adc_info *adc_info)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_common *common = adc->common;
struct device_node *node = indio_dev->dev.of_node;
struct property *prop;
const __be32 *cur;
@@ -945,6 +1007,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
int scan_index = 0, num_channels = 0;
u32 val;
+ if (!common->stm32_chans[adc->id]) {
+ /* Allocate extended attributes structure for an instance */
+ struct stm32_adc_chan *stm32_chans;
+
+ stm32_chans = devm_kcalloc(&indio_dev->dev,
+ adc_info->max_channels,
+ sizeof(*stm32_chans), GFP_KERNEL);
+ if (!stm32_chans)
+ return -ENOMEM;
+
+ common->stm32_chans[adc->id] = stm32_chans;
+ }
+
of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
num_channels++;
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 6c9b70d..8cf1d5c 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -143,6 +143,14 @@ struct stm32_adc_chan_spec {
};
/**
+ * struct stm32_adc_chan - Extended specifications of stm32 adc channels
+ * @smpr: per channel sampling time selection
+ */
+struct stm32_adc_chan {
+ unsigned smpr:3;
+};
+
+/**
* struct stm32_adc_trig_info - ADC trigger info
* @extsel: trigger selection for regular or injected
* @name: name of the trigger, corresponding to its source
@@ -206,6 +214,7 @@ struct stm32_adc_trig_reginfo {
* @jdr: injected data registers offsets
* @sqr_regs: Regular sequence registers description
* @jsqr_reg: Injected sequence register description
+ * @smpr_regs: Sampling time registers description
* @trig_reginfo: regular trigger control registers description
* @jtrig_reginfo: injected trigger control registers description
*/
@@ -220,6 +229,7 @@ struct stm32_adc_reginfo {
u32 jdr[4];
const struct stm32_adc_regs *sqr_regs;
const struct stm32_adc_regs *jsqr_reg;
+ const struct stm32_adc_regs *smpr_regs;
const struct stm32_adc_trig_reginfo *trig_reginfo;
const struct stm32_adc_trig_reginfo *jtrig_reginfo;
};
@@ -328,6 +338,7 @@ struct stm32_adc {
* @aclk: common clock for the analog circuitry
* @vref: regulator reference
* @vref_mv: vref voltage (mv)
+ * @stm32_chans: stm32 channels extended specification data
* @gpio_descs: gpio descriptor used to configure EXTi triggers
* @lock: mutex
*/
@@ -341,11 +352,21 @@ struct stm32_adc_common {
struct clk *aclk;
struct regulator *vref;
int vref_mv;
+ struct stm32_adc_chan *stm32_chans[STM32_ADC_ID_MAX];
struct gpio_descs *gpios;
struct mutex lock; /* read_raw lock */
};
/* Helper routines */
+static inline struct stm32_adc_chan *to_stm32_chan(struct stm32_adc *adc,
+ const struct iio_chan_spec
+ *chan)
+{
+ struct stm32_adc_chan *stm32_chans = adc->common->stm32_chans[adc->id];
+
+ return &stm32_chans[chan->channel];
+}
+
static inline int stm32_adc_start_conv(struct stm32_adc *adc)
{
return adc->common->data->start_conv(adc);
@@ -458,6 +479,10 @@ static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
/* STM32 common extended attributes */
extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int smpr);
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
int stm32_adc_probe(struct platform_device *pdev);
int stm32_adc_remove(struct platform_device *pdev);
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index e033a68..e0e6211 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -330,6 +330,32 @@ enum stm32f4_adc_smpr {
};
/**
+ * stm32f4_smpr_regs[] - describe sampling time registers & bit fields
+ * Sorted so it can be indexed by channel number.
+ */
+static const struct stm32_adc_regs stm32f4_smpr_regs[] = {
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP0_MASK, STM32F4_SMP0_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP1_MASK, STM32F4_SMP1_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP2_MASK, STM32F4_SMP2_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP3_MASK, STM32F4_SMP3_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP4_MASK, STM32F4_SMP4_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP5_MASK, STM32F4_SMP5_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP6_MASK, STM32F4_SMP6_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP7_MASK, STM32F4_SMP7_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP8_MASK, STM32F4_SMP8_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP9_MASK, STM32F4_SMP9_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP10_MASK, STM32F4_SMP10_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP11_MASK, STM32F4_SMP11_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP12_MASK, STM32F4_SMP12_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP13_MASK, STM32F4_SMP13_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP14_MASK, STM32F4_SMP14_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP15_MASK, STM32F4_SMP15_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP16_MASK, STM32F4_SMP16_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP17_MASK, STM32F4_SMP17_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP18_MASK, STM32F4_SMP18_SHIFT },
+};
+
+/**
* stm32f4_sqr_regs - describe regular sequence registers
* - L: sequence len (register & bit field)
* - SQ1..SQ16: sequence entries (register & bit field)
@@ -407,6 +433,7 @@ enum stm32f4_adc_smpr {
},
.sqr_regs = stm32f4_sqr_regs,
.jsqr_reg = stm32f4_jsqr_reg,
+ .smpr_regs = stm32f4_smpr_regs,
.trig_reginfo = &stm32f4_adc_trig_reginfo,
.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
};
@@ -555,7 +582,28 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
return 0;
}
+/* stm32f4_smpr_items : Channel-wise programmable sampling time */
+static const char * const stm32f4_smpr_items[] = {
+ [STM32F4_SMPR_3_CK_CYCLES] = "3_cycles",
+ [STM32F4_SMPR_15_CK_CYCLES] = "15_cycles",
+ [STM32F4_SMPR_28_CK_CYCLES] = "28_cycles",
+ [STM32F4_SMPR_56_CK_CYCLES] = "56_cycles",
+ [STM32F4_SMPR_84_CK_CYCLES] = "84_cycles",
+ [STM32F4_SMPR_112_CK_CYCLES] = "112_cycles",
+ [STM32F4_SMPR_144_CK_CYCLES] = "144_cycles",
+ [STM32F4_SMPR_480_CK_CYCLES] = "480_cycles",
+};
+
+static const struct iio_enum stm32f4_smpr = {
+ .items = stm32f4_smpr_items,
+ .num_items = ARRAY_SIZE(stm32f4_smpr_items),
+ .get = stm32_adc_get_smpr,
+ .set = stm32_adc_set_smpr,
+};
+
static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+ IIO_ENUM("smpr", IIO_SEPARATE, &stm32f4_smpr),
+ IIO_ENUM_AVAILABLE("smpr", &stm32f4_smpr),
IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
{
.name = "trigger_pol_available",
--
1.9.1
^ permalink raw reply related
* [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add trigger polarity extended attribute.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 41 ++++++++++++++++++++++++++++++++++++-
drivers/iio/adc/stm32/stm32-adc.h | 4 ++++
drivers/iio/adc/stm32/stm32f4-adc.c | 12 +++++++++++
3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1a13450..9b4b459 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -207,7 +207,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
/* trigger source */
extsel = trig_info[ret].extsel;
- exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+
+ /* default to rising edge if no polarity */
+ if (adc->exten == STM32_EXTEN_SWTRIG)
+ adc->exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+ exten = adc->exten;
}
spin_lock_irqsave(&adc->lock, flags);
@@ -221,6 +225,40 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
return 0;
}
+static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int type)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ adc->exten = type;
+
+ return 0;
+}
+
+static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ return adc->exten;
+}
+
+static const char * const stm32_trig_pol_items[] = {
+ [STM32_EXTEN_SWTRIG] = "swtrig",
+ [STM32_EXTEN_HWTRIG_RISING_EDGE] = "rising-edge",
+ [STM32_EXTEN_HWTRIG_FALLING_EDGE] = "falling-edge",
+ [STM32_EXTEN_HWTRIG_BOTH_EDGES] = "both-edges",
+};
+
+const struct iio_enum stm32_adc_trig_pol = {
+ .items = stm32_trig_pol_items,
+ .num_items = ARRAY_SIZE(stm32_trig_pol_items),
+ .get = stm32_adc_get_trig_pol,
+ .set = stm32_adc_set_trig_pol,
+};
+EXPORT_SYMBOL_GPL(stm32_adc_trig_pol);
+
/**
* stm32_adc_conv_irq_enable() - Unmask end of conversion irq
* @adc: stm32 adc instance
@@ -893,6 +931,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
chan->scan_type.realbits = adc->common->data->highres;
chan->scan_type.storagebits = STM32_STORAGEBITS;
chan->scan_type.shift = 0;
+ chan->ext_info = adc->common->data->ext_info;
}
static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index fe3568b..6c9b70d 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -234,6 +234,7 @@ struct stm32_adc_reginfo {
* @ext_triggers: Reference to trigger info for regular channels
* @jext_triggers: Reference to trigger info for injected channels
* @adc_reginfo: stm32 ADC registers description
+ * @ext_info: Extended channel info
* @highres: Max resolution
* @max_clock_rate: Max input clock rate
* @clk_sel: routine to select common clock and prescaler
@@ -251,6 +252,7 @@ struct stm32_adc_ops {
const struct stm32_adc_trig_info *ext_triggers;
const struct stm32_adc_trig_info *jext_triggers;
const struct stm32_adc_reginfo *adc_reginfo;
+ const struct iio_chan_spec_ext_info *ext_info;
int highres;
unsigned long max_clock_rate;
int (*clk_sel)(struct stm32_adc *adc);
@@ -273,6 +275,7 @@ struct stm32_adc_ops {
* @id: ADC instance number (e.g. adc 1, 2 or 3)
* @offset: ADC instance register offset in ADC block
* @max_channels: Max channels number for this ADC.
+ * @exten: External trigger config (enable/polarity)
* @extrig_list: External trigger list (for regular channel)
* @completion: end of single conversion completion
* @buffer: data buffer
@@ -295,6 +298,7 @@ struct stm32_adc {
int id;
int offset;
int max_channels;
+ enum stm32_adc_exten exten;
struct list_head extrig_list;
struct completion completion;
u16 *buffer;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 4d7a2a8..e033a68 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -555,11 +555,23 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
return 0;
}
+static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+ IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
+ {
+ .name = "trigger_pol_available",
+ .shared = IIO_SHARED_BY_ALL,
+ .read = iio_enum_available_read,
+ .private = (uintptr_t)&stm32_adc_trig_pol,
+ },
+ {},
+};
+
static const struct stm32_adc_ops stm32f4_adc_ops = {
.adc_info = stm32f4_adc_info,
.ext_triggers = stm32f4_adc_ext_triggers,
.jext_triggers = stm32f4_adc_jext_triggers,
.adc_reginfo = &stm32f4_adc_reginfo,
+ .ext_info = stm32f4_adc_ext_info,
.highres = 12,
.max_clock_rate = 36000000,
.clk_sel = stm32f4_adc_clk_sel,
--
1.9.1
^ permalink raw reply related
* [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
STM32 ADC may use EXTi signals routed internally as trigger source
for conversions. Configure them as interrupt to configure this path
in HW to adc.
Note: interrupt handler isn't required here, and corresponding interrupt
can be kept masked at exti controller level.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 45 +++++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 3 +++
2 files changed, 48 insertions(+)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 25d0307..1a13450 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -482,6 +482,12 @@ static irqreturn_t stm32_adc_common_isr(int irq, void *data)
return ret;
}
+static irqreturn_t stm32_adc_exti_handler(int irq, void *data)
+{
+ /* Exti handler should not be invoqued, and is not used */
+ return IRQ_HANDLED;
+}
+
/**
* stm32_adc_validate_trigger() - validate trigger for stm32 adc
* @indio_dev: IIO device
@@ -1051,6 +1057,41 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
}
}
+static int stm32_adc_exti_probe(struct stm32_adc_common *common)
+{
+ int i, irq, ret;
+
+ common->gpios = devm_gpiod_get_array_optional(common->dev, NULL,
+ GPIOD_IN);
+ if (!common->gpios)
+ return 0;
+
+ for (i = 0; i < common->gpios->ndescs; i++) {
+ irq = gpiod_to_irq(common->gpios->desc[i]);
+ if (irq < 0) {
+ dev_err(common->dev, "gpio %d to irq failed\n", i);
+ return irq;
+ }
+
+ ret = devm_request_irq(common->dev, irq, stm32_adc_exti_handler,
+ 0, dev_name(common->dev), common);
+ if (ret) {
+ dev_err(common->dev, "request IRQ %d failed\n", irq);
+ return ret;
+ }
+
+ /*
+ * gpios are configured as interrupts, so exti trigger path is
+ * configured in HW. But getting interrupts when starting
+ * conversions is unused here, so mask it in on exti
+ * controller by default.
+ */
+ disable_irq(irq);
+ }
+
+ return 0;
+}
+
int stm32_adc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node, *child;
@@ -1131,6 +1172,10 @@ int stm32_adc_probe(struct platform_device *pdev)
goto err_clk_disable;
}
+ ret = stm32_adc_exti_probe(common);
+ if (ret)
+ goto err_clk_disable;
+
/* Parse adc child nodes to retrieve master/slave instances data */
for_each_available_child_of_node(np, child) {
ret = stm32_adc_register(common, child);
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 01a0dda..fe3568b 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -23,6 +23,7 @@
#define __STM32_ADC_H
#include <linux/dmaengine.h>
+#include <linux/gpio/consumer.h>
#include <linux/irq_work.h>
/*
@@ -323,6 +324,7 @@ struct stm32_adc {
* @aclk: common clock for the analog circuitry
* @vref: regulator reference
* @vref_mv: vref voltage (mv)
+ * @gpio_descs: gpio descriptor used to configure EXTi triggers
* @lock: mutex
*/
struct stm32_adc_common {
@@ -335,6 +337,7 @@ struct stm32_adc_common {
struct clk *aclk;
struct regulator *vref;
int vref_mv;
+ struct gpio_descs *gpios;
struct mutex lock; /* read_raw lock */
};
--
1.9.1
^ permalink raw reply related
* [PATCH 03/10] iio: adc: stm32: add optional dma support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add optional DMA support to STM32 ADC.
Use dma cyclic mode with at least two periods.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/Kconfig | 2 +
drivers/iio/adc/stm32/stm32-adc.c | 222 ++++++++++++++++++++++++++++++++----
drivers/iio/adc/stm32/stm32-adc.h | 15 +++
drivers/iio/adc/stm32/stm32f4-adc.c | 20 +++-
4 files changed, 235 insertions(+), 24 deletions(-)
diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
index 245d037..5554ac8 100644
--- a/drivers/iio/adc/stm32/Kconfig
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -8,6 +8,7 @@ config STM32_ADC
select REGULATOR_FIXED_VOLTAGE
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select IRQ_WORK
help
Say yes here to build the driver for the STMicroelectronics
STM32 analog-to-digital converter (ADC).
@@ -18,6 +19,7 @@ config STM32_ADC
config STM32F4_ADC
tristate "STMicroelectronics STM32F4 adc"
depends on ARCH_STM32 || COMPILE_TEST
+ depends on HAS_DMA
depends on OF
select STM32_ADC
help
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1e0850d..25d0307 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -21,6 +21,7 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
@@ -371,6 +372,34 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
return ret;
}
+static int stm32_adc_residue(struct stm32_adc *adc)
+{
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ if (!adc->rx_buf)
+ return 0;
+
+ status = dmaengine_tx_status(adc->dma_chan,
+ adc->dma_chan->cookie,
+ &state);
+ if (status == DMA_IN_PROGRESS) {
+ /* Residue is size in bytes from end of buffer */
+ int i = adc->rx_buf_sz - state.residue;
+ int size;
+
+ /* Return available bytes */
+ if (i >= adc->bufi)
+ size = i - adc->bufi;
+ else
+ size = adc->rx_buf_sz - adc->bufi + i;
+
+ return size;
+ }
+
+ return 0;
+}
+
/**
* stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
*/
@@ -394,8 +423,8 @@ static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
status &= mask;
- /* Regular data */
- if (status & reginfo->eoc) {
+ /* Regular data (when dma isn't used) */
+ if ((status & reginfo->eoc) && (!adc->rx_buf)) {
adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
if (iio_buffer_enabled(indio_dev)) {
adc->bufi++;
@@ -553,29 +582,154 @@ static int stm32_adc_validate_device(struct iio_trigger *trig,
return indio != indio_dev ? -EINVAL : 0;
}
-static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
- bool state)
+static void stm32_adc_dma_irq_work(struct irq_work *work)
{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ /**
+ * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
+ * irq context, and cannot be called directly from dma callback,
+ * dma cb has to schedule this work instead.
+ */
+ iio_trigger_poll(indio_dev->trig);
+}
+
+static void stm32_adc_dma_buffer_done(void *data)
+{
+ struct iio_dev *indio_dev = data;
struct stm32_adc *adc = iio_priv(indio_dev);
- int ret;
- if (state) {
- /* Reset adc buffer index */
- adc->bufi = 0;
+ /* invoques iio_trigger_poll() from hard irq context */
+ irq_work_queue(&adc->work);
+}
+
+static int stm32_adc_buffer_alloc_dma_start(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_slave_config config;
+ dma_cookie_t cookie;
+ int ret, size, watermark;
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ if (!adc->dma_chan) {
/* Allocate adc buffer */
adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
if (!adc->buffer)
return -ENOMEM;
+ return 0;
+ }
+
+ /*
+ * Allocate at least twice the buffer size for dma cyclic transfers, so
+ * we can work with at least two dma periods. There should be :
+ * - always one buffer (period) dma is working on
+ * - one buffer (period) driver can push with iio_trigger_poll().
+ */
+ size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length;
+ size = max(indio_dev->scan_bytes * 2, size);
+
+ adc->rx_buf = dma_alloc_coherent(adc->common->dev, PAGE_ALIGN(size),
+ &adc->rx_dma_buf,
+ GFP_KERNEL);
+ if (!adc->rx_buf)
+ return -ENOMEM;
+ adc->rx_buf_sz = size;
+ watermark = indio_dev->buffer->bytes_per_datum
+ * indio_dev->buffer->watermark;
+ watermark = max(indio_dev->scan_bytes, watermark);
+ watermark = rounddown(watermark, indio_dev->scan_bytes);
+
+ dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size,
+ watermark);
+
+ /* Configure DMA channel to read data register */
+ memset(&config, 0, sizeof(config));
+ config.src_addr = (dma_addr_t)adc->common->phys_base;
+ config.src_addr += adc->offset + reginfo->dr;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ goto config_err;
+
+ /* Prepare a DMA cyclic transaction */
+ desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
+ adc->rx_dma_buf,
+ size, watermark,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ ret = -ENODEV;
+ goto config_err;
+ }
+
+ desc->callback = stm32_adc_dma_buffer_done;
+ desc->callback_param = indio_dev;
+
+ cookie = dmaengine_submit(desc);
+ if (dma_submit_error(cookie)) {
+ ret = dma_submit_error(cookie);
+ goto config_err;
+ }
+
+ /* Issue pending DMA requests */
+ dma_async_issue_pending(adc->dma_chan);
+
+ return 0;
+
+config_err:
+ dma_free_coherent(adc->common->dev, PAGE_ALIGN(size), adc->rx_buf,
+ adc->rx_dma_buf);
+
+ return ret;
+}
+
+static void stm32_adc_dma_stop_buffer_free(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan) {
+ kfree(adc->buffer);
+ } else {
+ dmaengine_terminate_all(adc->dma_chan);
+ irq_work_sync(&adc->work);
+ dma_free_coherent(adc->common->dev, PAGE_ALIGN(adc->rx_buf_sz),
+ adc->rx_buf, adc->rx_dma_buf);
+ adc->rx_buf = NULL;
+ }
+
+ adc->buffer = NULL;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ if (state) {
+ ret = stm32_adc_buffer_alloc_dma_start(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "alloc failed\n");
+ return ret;
+ }
+
ret = stm32_adc_set_trig(indio_dev, trig);
if (ret) {
dev_err(&indio_dev->dev, "Can't set trigger\n");
goto err_buffer_free;
}
- stm32_adc_conv_irq_enable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_enable(adc);
ret = stm32_adc_start_conv(adc);
if (ret) {
@@ -589,25 +743,25 @@ static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
return ret;
}
- stm32_adc_conv_irq_disable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_disable(adc);
ret = stm32_adc_set_trig(indio_dev, NULL);
if (ret)
dev_warn(&indio_dev->dev, "Can't clear trigger\n");
- kfree(adc->buffer);
- adc->buffer = NULL;
+ stm32_adc_dma_stop_buffer_free(indio_dev);
}
return 0;
err_irq_trig_disable:
- stm32_adc_conv_irq_disable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_disable(adc);
stm32_adc_set_trig(indio_dev, NULL);
err_buffer_free:
- kfree(adc->buffer);
- adc->buffer = NULL;
+ stm32_adc_dma_stop_buffer_free(indio_dev);
return ret;
}
@@ -624,17 +778,30 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct stm32_adc *adc = iio_priv(indio_dev);
- dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
-
- /* reset buffer index */
- adc->bufi = 0;
- iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
- pf->timestamp);
+ if (!adc->dma_chan) {
+ adc->bufi = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+ pf->timestamp);
+ } else {
+ int residue = stm32_adc_residue(adc);
+
+ while (residue >= indio_dev->scan_bytes) {
+ adc->buffer = (u16 *)&adc->rx_buf[adc->bufi];
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ adc->buffer,
+ pf->timestamp);
+ residue -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->rx_buf_sz)
+ adc->bufi = 0;
+ }
+ }
iio_trigger_notify_done(indio_dev->trig);
/* re-enable eoc irq */
- stm32_adc_conv_irq_enable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_enable(adc);
return IRQ_HANDLED;
}
@@ -827,6 +994,10 @@ static int stm32_adc_register(struct stm32_adc_common *common,
if (ret)
goto err_clk_disable;
+ adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
+ if (adc->dma_chan)
+ init_irq_work(&adc->work, stm32_adc_dma_irq_work);
+
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
&stm32_adc_trigger_handler,
@@ -850,6 +1021,8 @@ static int stm32_adc_register(struct stm32_adc_common *common,
iio_triggered_buffer_cleanup(indio_dev);
err_trig_unregister:
+ if (adc->dma_chan)
+ dma_release_channel(adc->dma_chan);
stm32_adc_trig_unregister(indio_dev);
err_clk_disable:
@@ -870,6 +1043,8 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
stm32_adc_trig_unregister(indio_dev);
+ if (adc->dma_chan)
+ dma_release_channel(adc->dma_chan);
if (adc->clk) {
clk_disable_unprepare(adc->clk);
clk_put(adc->clk);
@@ -900,6 +1075,7 @@ int stm32_adc_probe(struct platform_device *pdev)
common->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(common->base))
return PTR_ERR(common->base);
+ common->phys_base = res->start;
common->data = match->data;
common->dev = &pdev->dev;
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 0be603c..01a0dda 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -22,6 +22,9 @@
#ifndef __STM32_ADC_H
#define __STM32_ADC_H
+#include <linux/dmaengine.h>
+#include <linux/irq_work.h>
+
/*
* STM32 - ADC global register map
* ________________________________________________________
@@ -276,6 +279,11 @@ struct stm32_adc_ops {
* @num_conv: expected number of scan conversions
* @injected: use injected channels on this adc
* @lock: spinlock
+ * @work: irq work used to call trigger poll routine
+ * @dma_chan: dma channel
+ * @rx_buf: dma rx buffer cpu address
+ * @rx_dma_buf: dma rx buffer bus address
+ * @rx_buf_sz: dma rx buffer size
* @clk: optional adc clock, for this adc instance
* @calib: optional calibration data
* @en: emulates enabled state on some stm32 adc
@@ -293,6 +301,11 @@ struct stm32_adc {
int num_conv;
bool injected;
spinlock_t lock; /* interrupt lock */
+ struct irq_work work;
+ struct dma_chan *dma_chan;
+ u8 *rx_buf;
+ dma_addr_t rx_dma_buf;
+ int rx_buf_sz;
struct clk *clk;
void *calib;
bool en;
@@ -302,6 +315,7 @@ struct stm32_adc {
* struct stm32_adc_common - private data of ADC driver, common to all
* ADC instances (ADC block)
* @dev: device for this controller
+ * @phys_base: control registers base physical addr
* @base: control registers base cpu addr
* @irq: Common irq line for all adc instances
* @data: STM32 dependent data from compatible
@@ -313,6 +327,7 @@ struct stm32_adc {
*/
struct stm32_adc_common {
struct device *dev;
+ phys_addr_t phys_base;
void __iomem *base;
int irq;
const struct stm32_adc_ops *data;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 147fe9c..4d7a2a8 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -437,16 +437,30 @@ static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
* @adc: stm32 adc instance
*
* Start single conversions for regular or injected channels.
+ * Also take care of normal or DMA mode. DMA is used in circular mode for
+ * regular conversions, in IIO buffer modes. Rely on rx_buf as raw
+ * read doesn't use dma, but direct DR read.
*/
static int stm32f4_adc_start_conv(struct stm32_adc *adc)
{
- u32 trig_msk, start_msk;
+ u32 val, trig_msk, start_msk;
+ unsigned long flags;
dev_dbg(adc->common->dev, "%s %s\n", __func__,
adc->injected ? "injected" : "regular");
stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ if (!adc->injected) {
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+ val &= ~(STM32F4_DMA | STM32F4_DDS);
+ if (adc->rx_buf)
+ val |= STM32F4_DMA | STM32F4_DDS;
+ stm32_adc_writel(adc, STM32F4_ADCX_CR2, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+ }
+
if (!stm32f4_adc_is_started(adc)) {
stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
STM32F4_EOCS | STM32F4_ADON);
@@ -494,6 +508,10 @@ static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
}
+ if (!adc->injected)
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2,
+ STM32F4_DMA | STM32F4_DDS);
+
return 0;
}
--
1.9.1
^ permalink raw reply related
* [PATCH 02/10] iio: adc: Add stm32 support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32/Kconfig | 34 ++
drivers/iio/adc/stm32/Makefile | 4 +
drivers/iio/adc/stm32/stm32-adc.c | 999 ++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 442 ++++++++++++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
7 files changed, 2056 insertions(+)
create mode 100644 drivers/iio/adc/stm32/Kconfig
create mode 100644 drivers/iio/adc/stm32/Makefile
create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf32..5c96a55 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -583,4 +583,6 @@ config XILINX_XADC
The driver can also be build as a module. If so, the module will be called
xilinx-xadc.
+source "drivers/iio/adc/stm32/Kconfig"
+
endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..a9dbf3a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
+obj-$(CONFIG_ARCH_STM32) += stm32/
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
new file mode 100644
index 0000000..245d037
--- /dev/null
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -0,0 +1,34 @@
+#
+# STM32 familly ADC drivers
+#
+
+config STM32_ADC
+ tristate
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build the driver for the STMicroelectronics
+ STM32 analog-to-digital converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc.
+
+config STM32F4_ADC
+ tristate "STMicroelectronics STM32F4 adc"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ select STM32_ADC
+ help
+ Say yes here to build support for STMicroelectronics stm32f4 Analog
+ to Digital Converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32f4-adc.
+
+config STM32_ADC_DEBUG
+ bool "Enable debug for stm32 ADC drivers"
+ depends on STM32_ADC
+ help
+ Say "yes" to enable debug messages, on stm32 ADC drivers.
diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
new file mode 100644
index 0000000..83e8154
--- /dev/null
+++ b/drivers/iio/adc/stm32/Makefile
@@ -0,0 +1,4 @@
+# Core
+subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
new file mode 100644
index 0000000..1e0850d
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -0,0 +1,999 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include "stm32-adc.h"
+
+/**
+ * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ *
+ * Conversion sequence :
+ * Configure ADC scan sequence based on selected channels in scan_mask.
+ * Add channels to SQR or JSQR registers, from scan_mask LSB to MSB, then
+ * program sequence len.
+ *
+ * Note: This can be done only when ADC enabled and no conversion is ongoing.
+ */
+static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regs *sq;
+ const struct iio_chan_spec *chan;
+ u32 val, bit;
+ int sq_max, i = 0;
+
+ if (!stm32_adc_is_enabled(adc))
+ return -EBUSY;
+
+ if (adc->injected) {
+ if (stm32_adc_injected_started(adc))
+ return -EBUSY;
+
+ sq = adc->common->data->adc_reginfo->jsqr_reg;
+ sq_max = STM32_ADC_MAX_JSQ;
+ } else {
+ if (stm32_adc_regular_started(adc))
+ return -EBUSY;
+
+ sq = adc->common->data->adc_reginfo->sqr_regs;
+ sq_max = STM32_ADC_MAX_SQ;
+ }
+
+ /* Build sequence for regular or injected channels */
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ chan = indio_dev->channels + bit;
+ /*
+ * Assign one channel per SQ/JSQ entry in regular/injected
+ * sequence, starting with SQ1/JSQ1.
+ */
+ i++;
+ if (i > sq_max)
+ return -EINVAL;
+
+ dev_dbg(adc->common->dev, "%s chan %d to %s%d\n",
+ __func__, chan->channel, adc->injected ? "JSQ" : "SQ",
+ i);
+
+ val = stm32_adc_readl(adc, sq[i].reg);
+ val &= ~sq[i].mask;
+ val |= (chan->channel << sq[i].shift) & sq[i].mask;
+ stm32_adc_writel(adc, sq[i].reg, val);
+ }
+
+ if (!i)
+ return -EINVAL;
+
+ /* Sequence len */
+ val = stm32_adc_readl(adc, sq[0].reg);
+ val &= ~sq[0].mask;
+ val |= ((i - 1) << sq[0].shift) & sq[0].mask;
+ stm32_adc_writel(adc, sq[0].reg, val);
+
+ return 0;
+}
+
+static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = stm32_adc_clk_sel(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Clock sel failed\n");
+ return ret;
+ }
+
+ ret = stm32_adc_enable(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to enable adc\n");
+ return ret;
+ }
+
+ ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to configure sequence\n");
+ goto err_dis;
+ }
+
+ return 0;
+
+err_dis:
+ stm32_adc_disable(adc);
+
+ return ret;
+}
+
+/**
+ * stm32_adc_get_trig_index() - Get trigger index
+ * @indio_dev: IIO device
+ * @trig: trigger
+ *
+ * Returns trigger index, if trig matches one of the triggers registered by
+ * stm32 adc driver, -EINVAL otherwise.
+ */
+static int stm32_adc_get_trig_index(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct iio_trigger *tr;
+ int i = 0;
+
+ list_for_each_entry(tr, &adc->extrig_list, alloc_list) {
+ if (tr == trig)
+ return i;
+ i++;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_set_trig() - Set a regular or injected trigger
+ * @indio_dev: IIO device
+ * @trig: IIO trigger
+ *
+ * Set trigger source/polarity (e.g. SW, or HW with polarity) :
+ * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
+ * - if HW trigger enabled, set source & polarity (trigger_pol / jtrigger_pol)
+ *
+ * Note: must be called when ADC is enabled
+ */
+static int stm32_adc_set_trig(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_trig_info *trig_info;
+ const struct stm32_adc_trig_reginfo *reginfo;
+ u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
+ unsigned long flags;
+ int ret;
+
+ if (!stm32_adc_is_enabled(adc))
+ return -EBUSY;
+
+ if (adc->injected) {
+ if (stm32_adc_injected_started(adc))
+ return -EBUSY;
+ reginfo = adc->common->data->adc_reginfo->jtrig_reginfo;
+ trig_info = adc->common->data->jext_triggers;
+ } else {
+ if (stm32_adc_regular_started(adc))
+ return -EBUSY;
+ reginfo = adc->common->data->adc_reginfo->trig_reginfo;
+ trig_info = adc->common->data->ext_triggers;
+ }
+
+ if (trig) {
+ ret = stm32_adc_get_trig_index(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* trigger source */
+ extsel = trig_info[ret].extsel;
+ exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+ }
+
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, reginfo->reg);
+ val &= ~(reginfo->exten_mask | reginfo->extsel_mask);
+ val |= (exten << reginfo->exten_shift) & reginfo->exten_mask;
+ val |= (extsel << reginfo->extsel_shift) & reginfo->extsel_mask;
+ stm32_adc_writel(adc, reginfo->reg, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Unmask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask;
+
+ if (adc->injected)
+ mask = reginfo->jeocie;
+ else
+ mask = reginfo->eocie;
+
+ stm32_adc_set_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_conv_irq_disable() - Mask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Mask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask;
+
+ if (adc->injected)
+ mask = reginfo->jeocie;
+ else
+ mask = reginfo->eocie;
+
+ stm32_adc_clr_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_single_conv() - perform a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @result: conversion result
+ *
+ * The function performs a single conversion on a given channel, by
+ * by:
+ * - creating scan mask with only one channel
+ * - using SW trigger
+ * - then start single conv
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ unsigned long *scan_mask;
+ long timeout;
+ u16 result;
+ int ret;
+
+ scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
+ GFP_KERNEL);
+ if (!scan_mask)
+ return -ENOMEM;
+
+ set_bit(chan->scan_index, scan_mask);
+
+ reinit_completion(&adc->completion);
+
+ adc->bufi = 0;
+ adc->num_conv = 1;
+ adc->buffer = &result;
+
+ ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+ if (ret)
+ goto free;
+
+ /* No HW trigger: conversion can be launched in SW */
+ ret = stm32_adc_set_trig(indio_dev, NULL);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't set SW trigger\n");
+ goto adc_disable;
+ }
+
+ stm32_adc_conv_irq_enable(adc);
+
+ ret = stm32_adc_start_conv(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to start single conv\n");
+ goto irq_disable;
+ }
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &adc->completion, STM32_ADC_TIMEOUT);
+ if (timeout == 0) {
+ dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+ ret = -ETIMEDOUT;
+ } else if (timeout < 0) {
+ dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
+ ret = -EINTR;
+ } else {
+ *val = result & STM32_RESULT_MASK;
+ ret = IIO_VAL_INT;
+ }
+
+ if (stm32_adc_stop_conv(adc))
+ dev_err(&indio_dev->dev, "stop failed\n");
+
+irq_disable:
+ stm32_adc_conv_irq_disable(adc);
+
+adc_disable:
+ stm32_adc_disable(adc);
+
+free:
+ kfree(scan_mask);
+ adc->buffer = NULL;
+
+ return ret;
+}
+
+static int stm32_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ if (chan->type == IIO_VOLTAGE)
+ ret = stm32_adc_single_conv(indio_dev, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = adc->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
+ */
+static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
+
+ if (adc->injected) {
+ mask = reginfo->jeoc;
+ clr_mask = mask;
+ } else {
+ mask = reginfo->eoc;
+ /* don't clear 'eoc' as it is cleared when reading 'dr' */
+ clr_mask = 0;
+ }
+
+ /* clear irq */
+ stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
+ status &= mask;
+
+ /* Regular data */
+ if (status & reginfo->eoc) {
+ adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
+ if (iio_buffer_enabled(indio_dev)) {
+ adc->bufi++;
+ if (adc->bufi >= adc->num_conv) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ }
+ } else {
+ complete(&adc->completion);
+ }
+ }
+
+ /* Injected data */
+ if (status & reginfo->jeoc) {
+ int i;
+
+ for (i = 0; i < adc->num_conv; i++) {
+ adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
+ adc->bufi++;
+ }
+
+ if (iio_buffer_enabled(indio_dev)) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ } else {
+ complete(&adc->completion);
+ }
+ }
+
+ /*
+ * In case end of conversion flags have been handled, this has been
+ * handled for this ADC instance
+ */
+ if (status)
+ return IRQ_HANDLED;
+
+ /* This adc instance didn't trigger this interrupt */
+ return IRQ_NONE;
+}
+
+/**
+ * stm32_adc_common_isr() - Common isr for the whole ADC block
+ *
+ * There is one IRQ for all ADCs in ADC block, check all instances.
+ */
+static irqreturn_t stm32_adc_common_isr(int irq, void *data)
+{
+ struct stm32_adc_common *common = data;
+ irqreturn_t ret = IRQ_NONE;
+ struct stm32_adc *adc;
+
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ ret |= stm32_adc_isr(adc);
+
+ return ret;
+}
+
+/**
+ * stm32_adc_validate_trigger() - validate trigger for stm32 adc
+ * @indio_dev: IIO device
+ * @trig: new trigger
+ *
+ * Returns: 0 if trig matches one of the triggers registered by stm32 adc
+ * driver, -EINVAL otherwise.
+ */
+static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
+static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 bit;
+
+ adc->num_conv = 0;
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength)
+ adc->num_conv++;
+
+ ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_debugfs_reg_access - read or write register value
+ *
+ * To read a value from an ADC register:
+ * echo [ADC reg offset] > direct_reg_access
+ * cat direct_reg_access
+ *
+ * To write a value in a ADC register:
+ * echo [ADC_reg_offset] [value] > direct_reg_access
+ */
+static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (!readval)
+ stm32_adc_writel(adc, reg, writeval);
+ else
+ *readval = stm32_adc_readl(adc, reg);
+
+ return 0;
+}
+
+static const struct iio_info stm32_adc_iio_info = {
+ .read_raw = stm32_adc_read_raw,
+ .validate_trigger = stm32_adc_validate_trigger,
+ .update_scan_mode = stm32_adc_update_scan_mode,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .of_xlate = stm32_adc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ stm32_adc_disable(adc);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &stm32_adc_buffer_postdisable,
+};
+
+static int stm32_adc_validate_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+
+ return indio != indio_dev ? -EINVAL : 0;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ if (state) {
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ /* Allocate adc buffer */
+ adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!adc->buffer)
+ return -ENOMEM;
+
+ ret = stm32_adc_set_trig(indio_dev, trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't set trigger\n");
+ goto err_buffer_free;
+ }
+
+ stm32_adc_conv_irq_enable(adc);
+
+ ret = stm32_adc_start_conv(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to start\n");
+ goto err_irq_trig_disable;
+ }
+ } else {
+ ret = stm32_adc_stop_conv(adc);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to stop\n");
+ return ret;
+ }
+
+ stm32_adc_conv_irq_disable(adc);
+
+ ret = stm32_adc_set_trig(indio_dev, NULL);
+ if (ret)
+ dev_warn(&indio_dev->dev, "Can't clear trigger\n");
+
+ kfree(adc->buffer);
+ adc->buffer = NULL;
+ }
+
+ return 0;
+
+err_irq_trig_disable:
+ stm32_adc_conv_irq_disable(adc);
+ stm32_adc_set_trig(indio_dev, NULL);
+
+err_buffer_free:
+ kfree(adc->buffer);
+ adc->buffer = NULL;
+
+ return ret;
+}
+
+static const struct iio_trigger_ops stm32_adc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .validate_device = stm32_adc_validate_device,
+ .set_trigger_state = stm32_adc_set_trigger_state,
+};
+
+static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
+
+ /* reset buffer index */
+ adc->bufi = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ /* re-enable eoc irq */
+ stm32_adc_conv_irq_enable(adc);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct iio_trigger *trig, *_t;
+
+ list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
+ iio_trigger_unregister(trig);
+ list_del(&trig->alloc_list);
+ }
+}
+
+static int stm32_adc_trig_register(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_common *common = adc->common;
+ const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
+ struct iio_trigger *trig;
+ int i, ret = 0;
+
+ if (adc->injected)
+ ext = common->data->jext_triggers;
+ else
+ ext = common->data->ext_triggers;
+
+ for (i = 0; ext && ext[i].name; i++) {
+ trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ if (!trig) {
+ dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ trig->dev.parent = common->dev;
+ trig->ops = &stm32_adc_trigger_ops;
+ iio_trigger_set_drvdata(trig, indio_dev);
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ dev_err(common->dev,
+ "trig %s_%s%d_%s register failed\n",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ goto err;
+ }
+
+ list_add_tail(&trig->alloc_list, &adc->extrig_list);
+ }
+
+ return 0;
+err:
+ stm32_adc_trig_unregister(indio_dev);
+
+ return ret;
+}
+
+static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ const struct stm32_adc_chan_spec *channel,
+ int scan_index)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ chan->type = channel->type;
+ chan->channel = channel->channel;
+ chan->datasheet_name = channel->name;
+ chan->extend_name = channel->name;
+ chan->scan_index = scan_index;
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = adc->common->data->highres;
+ chan->scan_type.storagebits = STM32_STORAGEBITS;
+ chan->scan_type.shift = 0;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
+ const struct stm32_adc_info *adc_info)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device_node *node = indio_dev->dev.of_node;
+ struct property *prop;
+ const __be32 *cur;
+ struct iio_chan_spec *channels;
+ int scan_index = 0, num_channels = 0;
+ u32 val;
+
+ of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
+ num_channels++;
+
+ channels = devm_kcalloc(&indio_dev->dev, num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ &adc_info->channels[val],
+ scan_index);
+ scan_index++;
+ }
+
+ adc->max_channels = adc_info->max_channels;
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_register(struct stm32_adc_common *common,
+ struct device_node *child)
+{
+ struct iio_dev *indio_dev;
+ struct stm32_adc *adc;
+ int i, ret;
+ u32 reg;
+
+ ret = of_property_read_u32(child, "reg", ®);
+ if (ret != 0) {
+ dev_err(common->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; common->data->adc_info[i].channels; i++)
+ if (common->data->adc_info[i].reg == reg)
+ break;
+
+ if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
+ dev_err(common->dev, "bad adc reg offset\n");
+ return -ENOENT;
+ }
+
+ indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(common->dev, "iio device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->id = i;
+ adc->offset = reg;
+ adc->common = common;
+ INIT_LIST_HEAD(&adc->extrig_list);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+
+ if (child->name)
+ indio_dev->name = child->name;
+ else
+ indio_dev->name = common->data->adc_info[i].name;
+ indio_dev->dev.parent = common->dev;
+ indio_dev->dev.of_node = child;
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (of_property_read_bool(child, "st,injected")) {
+ dev_dbg(common->dev, "%s Configured to use injected\n",
+ indio_dev->name);
+ adc->injected = true;
+ }
+
+ adc->clk = of_clk_get(child, 0);
+ if (IS_ERR(adc->clk)) {
+ adc->clk = NULL;
+ dev_dbg(common->dev, "No child clk found\n");
+ } else {
+ ret = clk_prepare_enable(adc->clk);
+ if (ret < 0)
+ goto err_clk_put;
+ }
+
+ ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
+ if (ret < 0) {
+ dev_err(common->dev, "iio channels init failed\n");
+ goto err_clk_disable;
+ }
+
+ ret = stm32_adc_trig_register(indio_dev);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ &stm32_adc_trigger_handler,
+ &iio_triggered_buffer_setup_ops);
+ if (ret) {
+ dev_err(common->dev, "buffer setup failed\n");
+ goto err_trig_unregister;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(common->dev, "iio dev register failed\n");
+ goto err_buffer_cleanup;
+ }
+
+ list_add_tail(&adc->adc_list, &common->adc_list);
+
+ return 0;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+err_trig_unregister:
+ stm32_adc_trig_unregister(indio_dev);
+
+err_clk_disable:
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+err_clk_put:
+ if (adc->clk)
+ clk_put(adc->clk);
+
+ return ret;
+}
+
+static void stm32_adc_unregister(struct stm32_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ stm32_adc_trig_unregister(indio_dev);
+ if (adc->clk) {
+ clk_disable_unprepare(adc->clk);
+ clk_put(adc->clk);
+ }
+}
+
+int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node, *child;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct stm32_adc_common *common;
+ struct stm32_adc *adc;
+ struct resource *res;
+ int ret;
+
+ match = of_match_device(dev->driver->of_match_table, &pdev->dev);
+ if (!match || !match->data) {
+ dev_err(&pdev->dev, "compatible data not provided\n");
+ return -EINVAL;
+ }
+
+ common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
+ if (!common)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ common->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(common->base))
+ return PTR_ERR(common->base);
+
+ common->data = match->data;
+ common->dev = &pdev->dev;
+ platform_set_drvdata(pdev, common);
+ mutex_init(&common->lock);
+ INIT_LIST_HEAD(&common->adc_list);
+
+ common->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(common->vref)) {
+ ret = PTR_ERR(common->vref);
+ dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(common->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref enable failed\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(common->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_regulator_disable;
+ }
+ common->vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
+
+ common->aclk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(common->aclk)) {
+ ret = PTR_ERR(common->aclk);
+ dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+ goto err_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(common->aclk);
+ if (ret < 0) {
+ dev_err(common->dev, "adc clk enable failed\n");
+ goto err_regulator_disable;
+ }
+
+ common->irq = platform_get_irq(pdev, 0);
+ if (common->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ ret = common->irq;
+ goto err_clk_disable;
+ }
+
+ ret = devm_request_irq(&pdev->dev, common->irq, stm32_adc_common_isr,
+ 0, pdev->name, common);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ goto err_clk_disable;
+ }
+
+ /* Parse adc child nodes to retrieve master/slave instances data */
+ for_each_available_child_of_node(np, child) {
+ ret = stm32_adc_register(common, child);
+ if (ret)
+ goto err_unregister;
+ }
+
+ dev_info(&pdev->dev, "registered\n");
+
+ return 0;
+
+err_unregister:
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ stm32_adc_unregister(adc);
+
+err_clk_disable:
+ clk_disable_unprepare(common->aclk);
+
+err_regulator_disable:
+ regulator_disable(common->vref);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_probe);
+
+int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = platform_get_drvdata(pdev);
+ struct stm32_adc *adc;
+
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ stm32_adc_unregister(adc);
+ clk_disable_unprepare(common->aclk);
+ regulator_disable(common->vref);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_remove);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
new file mode 100644
index 0000000..0be603c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -0,0 +1,442 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+#define STM32_ADCX_COMN_OFFSET 0x300
+#define STM32_ADC_ID_MAX 3
+#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
+#define STM32_ADC_MAX_JSQ 4 /* JSQ1..JSQ4 */
+
+/* STM32 value masks */
+#define STM32_RESULT_MASK GENMASK(15, 0)
+#define STM32_STORAGEBITS 16
+
+/* External trigger enable for regular or injected channels (exten/jexten) */
+enum stm32_adc_exten {
+ STM32_EXTEN_SWTRIG,
+ STM32_EXTEN_HWTRIG_RISING_EDGE,
+ STM32_EXTEN_HWTRIG_FALLING_EDGE,
+ STM32_EXTEN_HWTRIG_BOTH_EDGES,
+};
+
+enum stm32_adc_extsel {
+ STM32_EXT0,
+ STM32_EXT1,
+ STM32_EXT2,
+ STM32_EXT3,
+ STM32_EXT4,
+ STM32_EXT5,
+ STM32_EXT6,
+ STM32_EXT7,
+ STM32_EXT8,
+ STM32_EXT9,
+ STM32_EXT10,
+ STM32_EXT11,
+ STM32_EXT12,
+ STM32_EXT13,
+ STM32_EXT14,
+ STM32_EXT15,
+ STM32_EXT16,
+ STM32_EXT17,
+ STM32_EXT18,
+ STM32_EXT19,
+ STM32_EXT20,
+ STM32_EXT21,
+ STM32_EXT22,
+ STM32_EXT23,
+ STM32_EXT24,
+ STM32_EXT25,
+ STM32_EXT26,
+ STM32_EXT27,
+ STM32_EXT28,
+ STM32_EXT29,
+ STM32_EXT30,
+ STM32_EXT31,
+};
+
+enum stm32_adc_jextsel {
+ STM32_JEXT0,
+ STM32_JEXT1,
+ STM32_JEXT2,
+ STM32_JEXT3,
+ STM32_JEXT4,
+ STM32_JEXT5,
+ STM32_JEXT6,
+ STM32_JEXT7,
+ STM32_JEXT8,
+ STM32_JEXT9,
+ STM32_JEXT10,
+ STM32_JEXT11,
+ STM32_JEXT12,
+ STM32_JEXT13,
+ STM32_JEXT14,
+ STM32_JEXT15,
+ STM32_JEXT16,
+ STM32_JEXT17,
+ STM32_JEXT18,
+ STM32_JEXT19,
+ STM32_JEXT20,
+ STM32_JEXT21,
+ STM32_JEXT22,
+ STM32_JEXT23,
+ STM32_JEXT24,
+ STM32_JEXT25,
+ STM32_JEXT26,
+ STM32_JEXT27,
+ STM32_JEXT28,
+ STM32_JEXT29,
+ STM32_JEXT30,
+ STM32_JEXT31,
+};
+
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc_chan_spec - specification of stm32 adc channel
+ * @type: IIO channel type
+ * @channel: channel number (single ended)
+ * @name: channel name (single ended)
+ */
+struct stm32_adc_chan_spec {
+ enum iio_chan_type type;
+ int channel;
+ const char *name;
+};
+
+/**
+ * struct stm32_adc_trig_info - ADC trigger info
+ * @extsel: trigger selection for regular or injected
+ * @name: name of the trigger, corresponding to its source
+ */
+struct stm32_adc_trig_info {
+ u32 extsel;
+ const char *name;
+};
+
+/**
+ * struct stm32_adc_info - stm32 ADC, per instance config data
+ * @name: default name for this instance (like "adc1")
+ * @reg: reg offset for this instance (e.g. 0x0 for adc1...)
+ * @channels: Reference to stm32 channels spec
+ * @max_channels: Number of single ended channels
+ */
+struct stm32_adc_info {
+ const char *name;
+ u32 reg;
+ const struct stm32_adc_chan_spec *channels;
+ int max_channels;
+};
+
+/**
+ * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
+ * @reg: register offset
+ * @mask: bitfield mask
+ * @shift: left shift
+ */
+struct stm32_adc_regs {
+ int reg;
+ int mask;
+ int shift;
+};
+
+/**
+ * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
+ * @reg: trigger control register offset (exten/jexten)
+ * @exten_mask: external trigger en/polarity mask in @reg
+ * @exten_shift: external trigger en/polarity shift in @reg
+ * @extsel_mask: external trigger source mask in @reg
+ * @extsel_shift: external trigger source shift in @reg
+ */
+struct stm32_adc_trig_reginfo {
+ u32 reg;
+ u32 exten_mask;
+ u32 exten_shift;
+ u32 extsel_mask;
+ u32 extsel_shift;
+};
+
+/**
+ * struct stm32_adc_reginfo - stm32 ADC registers description
+ * @isr: interrupt status register offset
+ * @eoc: end of conversion mask in @isr
+ * @jeoc: end of injected conversion sequence mask in @isr
+ * @ier: interrupt enable register offset
+ * @eocie: end of conversion interrupt enable mask in @ier
+ * @jeocie: end of injected conversion sequence interrupt en mask
+ * @dr: data register offset
+ * @jdr: injected data registers offsets
+ * @sqr_regs: Regular sequence registers description
+ * @jsqr_reg: Injected sequence register description
+ * @trig_reginfo: regular trigger control registers description
+ * @jtrig_reginfo: injected trigger control registers description
+ */
+struct stm32_adc_reginfo {
+ u32 isr;
+ u32 eoc;
+ u32 jeoc;
+ u32 ier;
+ u32 eocie;
+ u32 jeocie;
+ u32 dr;
+ u32 jdr[4];
+ const struct stm32_adc_regs *sqr_regs;
+ const struct stm32_adc_regs *jsqr_reg;
+ const struct stm32_adc_trig_reginfo *trig_reginfo;
+ const struct stm32_adc_trig_reginfo *jtrig_reginfo;
+};
+
+struct stm32_adc;
+
+/**
+ * struct stm32_adc_ops - stm32 ADC, compatible dependent data
+ * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
+ *
+ * @adc_info: Array spec for stm32 adc master/slaves instances
+ * @ext_triggers: Reference to trigger info for regular channels
+ * @jext_triggers: Reference to trigger info for injected channels
+ * @adc_reginfo: stm32 ADC registers description
+ * @highres: Max resolution
+ * @max_clock_rate: Max input clock rate
+ * @clk_sel: routine to select common clock and prescaler
+ * @start_conv: routine to start conversions
+ * @stop_conv: routine to stop conversions
+ * @is_started: routine to get adc 'started' state
+ * @regular_started routine to check regular conversions status
+ * @injected_started routine to check injected conversions status
+ * @enable: optional routine to enable stm32 adc
+ * @disable: optional routine to disable stm32 adc
+ * @is_enabled reports enabled state
+ */
+struct stm32_adc_ops {
+ const struct stm32_adc_info *adc_info;
+ const struct stm32_adc_trig_info *ext_triggers;
+ const struct stm32_adc_trig_info *jext_triggers;
+ const struct stm32_adc_reginfo *adc_reginfo;
+ int highres;
+ unsigned long max_clock_rate;
+ int (*clk_sel)(struct stm32_adc *adc);
+ int (*start_conv)(struct stm32_adc *adc);
+ int (*stop_conv)(struct stm32_adc *adc);
+ bool (*is_started)(struct stm32_adc *adc);
+ bool (*regular_started)(struct stm32_adc *adc);
+ bool (*injected_started)(struct stm32_adc *adc);
+ int (*enable)(struct stm32_adc *adc);
+ void (*disable)(struct stm32_adc *adc);
+ bool (*is_enabled)(struct stm32_adc *adc);
+};
+
+struct stm32_adc_common;
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @adc_list: current ADC entry in common ADC list
+ * @id: ADC instance number (e.g. adc 1, 2 or 3)
+ * @offset: ADC instance register offset in ADC block
+ * @max_channels: Max channels number for this ADC.
+ * @extrig_list: External trigger list (for regular channel)
+ * @completion: end of single conversion completion
+ * @buffer: data buffer
+ * @bufi: data buffer index
+ * @num_conv: expected number of scan conversions
+ * @injected: use injected channels on this adc
+ * @lock: spinlock
+ * @clk: optional adc clock, for this adc instance
+ * @calib: optional calibration data
+ * @en: emulates enabled state on some stm32 adc
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ struct list_head adc_list;
+ int id;
+ int offset;
+ int max_channels;
+ struct list_head extrig_list;
+ struct completion completion;
+ u16 *buffer;
+ int bufi;
+ int num_conv;
+ bool injected;
+ spinlock_t lock; /* interrupt lock */
+ struct clk *clk;
+ void *calib;
+ bool en;
+};
+
+/**
+ * struct stm32_adc_common - private data of ADC driver, common to all
+ * ADC instances (ADC block)
+ * @dev: device for this controller
+ * @base: control registers base cpu addr
+ * @irq: Common irq line for all adc instances
+ * @data: STM32 dependent data from compatible
+ * @adc_list: list of all stm32 ADC in this ADC block
+ * @aclk: common clock for the analog circuitry
+ * @vref: regulator reference
+ * @vref_mv: vref voltage (mv)
+ * @lock: mutex
+ */
+struct stm32_adc_common {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ const struct stm32_adc_ops *data;
+ struct list_head adc_list;
+ struct clk *aclk;
+ struct regulator *vref;
+ int vref_mv;
+ struct mutex lock; /* read_raw lock */
+};
+
+/* Helper routines */
+static inline int stm32_adc_start_conv(struct stm32_adc *adc)
+{
+ return adc->common->data->start_conv(adc);
+}
+
+static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+ return adc->common->data->stop_conv(adc);
+}
+
+static inline bool stm32_adc_is_started(struct stm32_adc *adc)
+{
+ return adc->common->data->is_started(adc);
+}
+
+static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
+{
+ return adc->common->data->regular_started(adc);
+}
+
+static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
+{
+ return adc->common->data->injected_started(adc);
+}
+
+static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
+{
+ return adc->common->data->clk_sel(adc);
+}
+
+static inline int stm32_adc_enable(struct stm32_adc *adc)
+{
+ if (adc->common->data->enable)
+ return adc->common->data->enable(adc);
+
+ adc->en = true;
+
+ return 0;
+}
+
+static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
+{
+ if (adc->common->data->is_enabled)
+ return adc->common->data->is_enabled(adc);
+ else
+ return adc->en;
+}
+
+static inline void stm32_adc_disable(struct stm32_adc *adc)
+{
+ /* Check there is no regular or injected on-going conversions */
+ if (stm32_adc_is_started(adc))
+ return;
+
+ if (adc->common->data->disable)
+ adc->common->data->disable(adc);
+ else
+ adc->en = false;
+}
+
+/* STM32 ADC registers access routines */
+static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
+{
+ u32 val = readl_relaxed(com->base + reg);
+
+ return val;
+}
+
+static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
+ u32 reg, u32 val)
+{
+ writel_relaxed(val, com->base + reg);
+}
+
+static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
+
+ return val;
+}
+
+#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr)
+
+#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
+ readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
+ cond, sleep_us, timeout_us)
+
+static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+/* STM32 common extended attributes */
+extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_probe(struct platform_device *pdev);
+int stm32_adc_remove(struct platform_device *pdev);
+
+#endif
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
new file mode 100644
index 0000000..147fe9c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -0,0 +1,574 @@
+/*
+ * This file is part of STM32F4 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/platform_device.h>
+#include "stm32-adc.h"
+
+/*
+ * STM32F4 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADCX_SR 0x00
+#define STM32F4_ADCX_CR1 0x04
+#define STM32F4_ADCX_CR2 0x08
+#define STM32F4_ADCX_SMPR1 0x0C
+#define STM32F4_ADCX_SMPR2 0x10
+#define STM32F4_ADCX_HTR 0x24
+#define STM32F4_ADCX_LTR 0x28
+#define STM32F4_ADCX_SQR1 0x2C
+#define STM32F4_ADCX_SQR2 0x30
+#define STM32F4_ADCX_SQR3 0x34
+#define STM32F4_ADCX_JSQR 0x38
+#define STM32F4_ADCX_JDR1 0x3C
+#define STM32F4_ADCX_JDR2 0x40
+#define STM32F4_ADCX_JDR3 0x44
+#define STM32F4_ADCX_JDR4 0x48
+#define STM32F4_ADCX_DR 0x4C
+
+/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+#define STM32F4_ADC_CDR (STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32F4_ADCX_SR - bit fields */
+#define STM32F4_OVR BIT(5)
+#define STM32F4_STRT BIT(4)
+#define STM32F4_JSTRT BIT(3)
+#define STM32F4_JEOC BIT(2)
+#define STM32F4_EOC BIT(1)
+#define STM32F4_AWD BIT(0)
+
+/* STM32F4_ADCX_CR1 - bit fields */
+#define STM32F4_OVRIE BIT(26)
+#define STM32F4_RES_SHIFT 24
+#define STM32F4_RES_MASK GENMASK(25, 24)
+#define STM32F4_AWDEN BIT(23)
+#define STM32F4_JAWDEN BIT(22)
+#define STM32F4_DISCNUM_SHIFT 13
+#define STM32F4_DISCNUM_MASK GENMASK(15, 13)
+#define STM32F4_JDISCEN BIT(12)
+#define STM32F4_DISCEN BIT(11)
+#define STM32F4_JAUTO BIT(10)
+#define STM32F4_AWDSGL BIT(9)
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_JEOCIE BIT(7)
+#define STM32F4_AWDIE BIT(6)
+#define STM32F4_EOCIE BIT(5)
+#define STM32F4_AWDCH_SHIFT 0
+#define STM32F4_AWDCH_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_SHIFT 28
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EXTSEL_SHIFT 24
+#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
+#define STM32F4_JSWSTART BIT(22)
+#define STM32F4_JEXTEN_SHIFT 20
+#define STM32F4_JEXTEN_MASK GENMASK(21, 20)
+#define STM32F4_JEXTSEL_SHIFT 16
+#define STM32F4_JEXTSEL_MASK GENMASK(19, 16)
+#define STM32F4_ALIGN BIT(11)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_DDS BIT(9)
+#define STM32F4_DMA BIT(8)
+#define STM32F4_CONT BIT(1)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADCX_SMPR1 - bit fields */
+#define STM32F4_SMP18_SHIFT 24
+#define STM32F4_SMP18_MASK GENMASK(26, 24)
+#define STM32F4_SMP17_SHIFT 21
+#define STM32F4_SMP17_MASK GENMASK(23, 21)
+#define STM32F4_SMP16_SHIFT 18
+#define STM32F4_SMP16_MASK GENMASK(20, 18)
+#define STM32F4_SMP15_SHIFT 15
+#define STM32F4_SMP15_MASK GENMASK(17, 15)
+#define STM32F4_SMP14_SHIFT 12
+#define STM32F4_SMP14_MASK GENMASK(14, 12)
+#define STM32F4_SMP13_SHIFT 9
+#define STM32F4_SMP13_MASK GENMASK(11, 9)
+#define STM32F4_SMP12_SHIFT 6
+#define STM32F4_SMP12_MASK GENMASK(8, 6)
+#define STM32F4_SMP11_SHIFT 3
+#define STM32F4_SMP11_MASK GENMASK(5, 3)
+#define STM32F4_SMP10_SHIFT 0
+#define STM32F4_SMP10_MASK GENMASK(2, 0)
+
+/* STM32F4_ADCX_SMPR2 - bit fields */
+#define STM32F4_SMP9_SHIFT 27
+#define STM32F4_SMP9_MASK GENMASK(29, 27)
+#define STM32F4_SMP8_SHIFT 24
+#define STM32F4_SMP8_MASK GENMASK(26, 24)
+#define STM32F4_SMP7_SHIFT 21
+#define STM32F4_SMP7_MASK GENMASK(23, 21)
+#define STM32F4_SMP6_SHIFT 18
+#define STM32F4_SMP6_MASK GENMASK(20, 18)
+#define STM32F4_SMP5_SHIFT 15
+#define STM32F4_SMP5_MASK GENMASK(17, 15)
+#define STM32F4_SMP4_SHIFT 12
+#define STM32F4_SMP4_MASK GENMASK(14, 12)
+#define STM32F4_SMP3_SHIFT 9
+#define STM32F4_SMP3_MASK GENMASK(11, 9)
+#define STM32F4_SMP2_SHIFT 6
+#define STM32F4_SMP2_MASK GENMASK(8, 6)
+#define STM32F4_SMP1_SHIFT 3
+#define STM32F4_SMP1_MASK GENMASK(5, 3)
+#define STM32F4_SMP0_SHIFT 0
+#define STM32F4_SMP0_MASK GENMASK(2, 0)
+enum stm32f4_adc_smpr {
+ STM32F4_SMPR_3_CK_CYCLES,
+ STM32F4_SMPR_15_CK_CYCLES,
+ STM32F4_SMPR_28_CK_CYCLES,
+ STM32F4_SMPR_56_CK_CYCLES,
+ STM32F4_SMPR_84_CK_CYCLES,
+ STM32F4_SMPR_112_CK_CYCLES,
+ STM32F4_SMPR_144_CK_CYCLES,
+ STM32F4_SMPR_480_CK_CYCLES,
+};
+
+/* STM32F4_ADCX_SQR1 - bit fields */
+#define STM32F4_L_SHIFT 20
+#define STM32F4_L_MASK GENMASK(23, 20)
+#define STM32F4_SQ16_SHIFT 15
+#define STM32F4_SQ16_MASK GENMASK(19, 15)
+#define STM32F4_SQ15_SHIFT 10
+#define STM32F4_SQ15_MASK GENMASK(14, 10)
+#define STM32F4_SQ14_SHIFT 5
+#define STM32F4_SQ14_MASK GENMASK(9, 5)
+#define STM32F4_SQ13_SHIFT 0
+#define STM32F4_SQ13_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR2 - bit fields */
+#define STM32F4_SQ12_SHIFT 25
+#define STM32F4_SQ12_MASK GENMASK(29, 25)
+#define STM32F4_SQ11_SHIFT 20
+#define STM32F4_SQ11_MASK GENMASK(24, 20)
+#define STM32F4_SQ10_SHIFT 15
+#define STM32F4_SQ10_MASK GENMASK(19, 15)
+#define STM32F4_SQ9_SHIFT 10
+#define STM32F4_SQ9_MASK GENMASK(14, 10)
+#define STM32F4_SQ8_SHIFT 5
+#define STM32F4_SQ8_MASK GENMASK(9, 5)
+#define STM32F4_SQ7_SHIFT 0
+#define STM32F4_SQ7_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR3 - bit fields */
+#define STM32F4_SQ6_SHIFT 25
+#define STM32F4_SQ6_MASK GENMASK(29, 25)
+#define STM32F4_SQ5_SHIFT 20
+#define STM32F4_SQ5_MASK GENMASK(24, 20)
+#define STM32F4_SQ4_SHIFT 15
+#define STM32F4_SQ4_MASK GENMASK(19, 15)
+#define STM32F4_SQ3_SHIFT 10
+#define STM32F4_SQ3_MASK GENMASK(14, 10)
+#define STM32F4_SQ2_SHIFT 5
+#define STM32F4_SQ2_MASK GENMASK(9, 5)
+#define STM32F4_SQ1_SHIFT 0
+#define STM32F4_SQ1_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_JSQR - bit fields */
+#define STM32F4_JL_SHIFT 20
+#define STM32F4_JL_MASK GENMASK(21, 20)
+#define STM32F4_JSQ4_SHIFT 15
+#define STM32F4_JSQ4_MASK GENMASK(19, 15)
+#define STM32F4_JSQ3_SHIFT 10
+#define STM32F4_JSQ3_MASK GENMASK(14, 10)
+#define STM32F4_JSQ2_SHIFT 5
+#define STM32F4_JSQ2_MASK GENMASK(9, 5)
+#define STM32F4_JSQ1_SHIFT 0
+#define STM32F4_JSQ1_MASK GENMASK(4, 0)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/*
+ * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
+ * Define here all inputs for all ADC instances
+ */
+static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
+ /* master ADC1 */
+ { IIO_VOLTAGE, 0, "in0" },
+ { IIO_VOLTAGE, 1, "in1" },
+ { IIO_VOLTAGE, 2, "in2" },
+ { IIO_VOLTAGE, 3, "in3" },
+ { IIO_VOLTAGE, 4, "in4" },
+ { IIO_VOLTAGE, 5, "in5" },
+ { IIO_VOLTAGE, 6, "in6" },
+ { IIO_VOLTAGE, 7, "in7" },
+ { IIO_VOLTAGE, 8, "in8" },
+ { IIO_VOLTAGE, 9, "in9" },
+ { IIO_VOLTAGE, 10, "in10" },
+ { IIO_VOLTAGE, 11, "in11" },
+ { IIO_VOLTAGE, 12, "in12" },
+ { IIO_VOLTAGE, 13, "in13" },
+ { IIO_VOLTAGE, 14, "in14" },
+ { IIO_VOLTAGE, 15, "in15" },
+ /* internal analog sources available on input 16 to 18 */
+ { IIO_VOLTAGE, 16, "in16" },
+ { IIO_VOLTAGE, 17, "in17" },
+ { IIO_VOLTAGE, 18, "in18" },
+};
+
+static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
+ /* slave ADC2 / ADC3 */
+ { IIO_VOLTAGE, 0, "in0" },
+ { IIO_VOLTAGE, 1, "in1" },
+ { IIO_VOLTAGE, 2, "in2" },
+ { IIO_VOLTAGE, 3, "in3" },
+ { IIO_VOLTAGE, 4, "in4" },
+ { IIO_VOLTAGE, 5, "in5" },
+ { IIO_VOLTAGE, 6, "in6" },
+ { IIO_VOLTAGE, 7, "in7" },
+ { IIO_VOLTAGE, 8, "in8" },
+ { IIO_VOLTAGE, 9, "in9" },
+ { IIO_VOLTAGE, 10, "in10" },
+ { IIO_VOLTAGE, 11, "in11" },
+ { IIO_VOLTAGE, 12, "in12" },
+ { IIO_VOLTAGE, 13, "in13" },
+ { IIO_VOLTAGE, 14, "in14" },
+ { IIO_VOLTAGE, 15, "in15" },
+};
+
+/* Triggers for regular channels */
+static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
+ { STM32_EXT0, "TIM1_CH1" },
+ { STM32_EXT1, "TIM1_CH2" },
+ { STM32_EXT2, "TIM1_CH3" },
+ { STM32_EXT3, "TIM2_CH2" },
+ { STM32_EXT4, "TIM2_CH3" },
+ { STM32_EXT5, "TIM2_CH4" },
+ { STM32_EXT6, "TIM2_TRGO" },
+ { STM32_EXT7, "TIM3_CH1" },
+ { STM32_EXT8, "TIM3_TRGO" },
+ { STM32_EXT9, "TIM4_CH4" },
+ { STM32_EXT10, "TIM5_CH1" },
+ { STM32_EXT11, "TIM5_CH2" },
+ { STM32_EXT12, "TIM5_CH3" },
+ { STM32_EXT13, "TIM8_CH1" },
+ { STM32_EXT14, "TIM8_TRGO" },
+ { STM32_EXT15, "EXTI_11" },
+ {},
+};
+
+/* Triggers for injected channels */
+static const struct stm32_adc_trig_info stm32f4_adc_jext_triggers[] = {
+ { STM32_JEXT0, "TIM1_CH4" },
+ { STM32_JEXT1, "TIM1_TRGO" },
+ { STM32_JEXT2, "TIM2_CH1" },
+ { STM32_JEXT3, "TIM2_TRGO" },
+ { STM32_JEXT4, "TIM3_CH2" },
+ { STM32_JEXT5, "TIM3_CH4" },
+ { STM32_JEXT6, "TIM4_CH1" },
+ { STM32_JEXT7, "TIM4_CH2" },
+ { STM32_JEXT8, "TIM4_CH3" },
+ { STM32_JEXT9, "TIM4_TRGO" },
+ { STM32_JEXT10, "TIM5_CH4" },
+ { STM32_JEXT11, "TIM5_TRGO" },
+ { STM32_JEXT12, "TIM8_CH2" },
+ { STM32_JEXT13, "TIM8_CH3" },
+ { STM32_JEXT14, "TIM8_CH4" },
+ { STM32_JEXT15, "EXTI_15" },
+ {},
+};
+
+static const struct stm32_adc_info stm32f4_adc_info[] = {
+ {
+ .name = "adc1-master",
+ .reg = 0x0,
+ .channels = stm32f4_adc1_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
+ },
+ {
+ .name = "adc2-slave",
+ .reg = 0x100,
+ .channels = stm32f4_adc23_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+ },
+ {
+ .name = "adc3-slave",
+ .reg = 0x200,
+ .channels = stm32f4_adc23_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+ },
+ {},
+};
+
+/**
+ * stm32f4_sqr_regs - describe regular sequence registers
+ * - L: sequence len (register & bit field)
+ * - SQ1..SQ16: sequence entries (register & bit field)
+ */
+static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
+ /* L: len bit field description to be kept as first element */
+ { STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
+ /* SQ1..SQ16 registers & bit fields */
+ { STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
+};
+
+/**
+ * stm32f4_jsqr_reg - describe injected sequence register:
+ * - JL: injected sequence len
+ * - JSQ4..SQ1: sequence entries
+ * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
+ * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
+ * When JL == 1, ADC converts JSQ3, JSQ4
+ * When JL == 0, ADC converts JSQ4
+ */
+static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
+ /* JL: len bit field description to be kept as first element */
+ {STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
+ /* JSQ4..JSQ1 registers & bit fields */
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
+ .reg = STM32F4_ADCX_CR2,
+ .exten_mask = STM32F4_EXTEN_MASK,
+ .exten_shift = STM32F4_EXTEN_SHIFT,
+ .extsel_mask = STM32F4_EXTSEL_MASK,
+ .extsel_shift = STM32F4_EXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
+ .reg = STM32F4_ADCX_CR2,
+ .exten_mask = STM32F4_JEXTEN_MASK,
+ .exten_shift = STM32F4_JEXTEN_SHIFT,
+ .extsel_mask = STM32F4_JEXTSEL_MASK,
+ .extsel_shift = STM32F4_JEXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
+ .isr = STM32F4_ADCX_SR,
+ .eoc = STM32F4_EOC,
+ .jeoc = STM32F4_JEOC,
+ .ier = STM32F4_ADCX_CR1,
+ .eocie = STM32F4_EOCIE,
+ .jeocie = STM32F4_JEOCIE,
+ .dr = STM32F4_ADCX_DR,
+ .jdr = {
+ STM32F4_ADCX_JDR1,
+ STM32F4_ADCX_JDR2,
+ STM32F4_ADCX_JDR3,
+ STM32F4_ADCX_JDR4,
+ },
+ .sqr_regs = stm32f4_sqr_regs,
+ .jsqr_reg = stm32f4_jsqr_reg,
+ .trig_reginfo = &stm32f4_adc_trig_reginfo,
+ .jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
+};
+
+static bool stm32f4_adc_is_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
+
+ return !!val;
+}
+
+static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
+
+ return !!val;
+}
+
+static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
+
+ return !!val;
+}
+
+/**
+ * stm32f4_adc_start_conv() - Start regular or injected conversions
+ * @adc: stm32 adc instance
+ *
+ * Start single conversions for regular or injected channels.
+ */
+static int stm32f4_adc_start_conv(struct stm32_adc *adc)
+{
+ u32 trig_msk, start_msk;
+
+ dev_dbg(adc->common->dev, "%s %s\n", __func__,
+ adc->injected ? "injected" : "regular");
+
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+
+ if (!stm32f4_adc_is_started(adc)) {
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
+ STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+ }
+
+ if (adc->injected) {
+ trig_msk = STM32F4_JEXTEN_MASK;
+ start_msk = STM32F4_JSWSTART;
+ } else {
+ trig_msk = STM32F4_EXTEN_MASK;
+ start_msk = STM32F4_SWSTART;
+ }
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
+
+ return 0;
+}
+
+static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
+{
+ u32 val;
+
+ dev_dbg(adc->common->dev, "%s %s\n", __func__,
+ adc->injected ? "injected" : "regular");
+
+ /* First disable trigger for either regular or injected channels */
+ if (adc->injected) {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
+ } else {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
+ }
+
+ /* Disable adc when all triggered conversion have been disabled */
+ val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+ val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
+ if (!val) {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
+ }
+
+ return 0;
+}
+
+/* ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
+ * @adc: stm32 adc instance
+ * Select clock prescaler used for analog conversions.
+ */
+static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
+{
+ struct stm32_adc_common *common = adc->common;
+ unsigned long rate;
+ u32 val;
+ int i;
+
+ /* Common prescaler is set only once, when 1st ADC instance starts */
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ if (stm32f4_adc_is_started(adc))
+ return 0;
+
+ rate = clk_get_rate(common->aclk);
+ for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+ if ((rate / stm32f4_pclk_div[i]) <=
+ common->data->max_clock_rate)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+ return -EINVAL;
+
+ val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
+ val &= ~STM32F4_ADC_ADCPRE_MASK;
+ val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+ stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
+
+ dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
+ rate / (stm32f4_pclk_div[i] * 1000));
+
+ return 0;
+}
+
+static const struct stm32_adc_ops stm32f4_adc_ops = {
+ .adc_info = stm32f4_adc_info,
+ .ext_triggers = stm32f4_adc_ext_triggers,
+ .jext_triggers = stm32f4_adc_jext_triggers,
+ .adc_reginfo = &stm32f4_adc_reginfo,
+ .highres = 12,
+ .max_clock_rate = 36000000,
+ .clk_sel = stm32f4_adc_clk_sel,
+ .start_conv = stm32f4_adc_start_conv,
+ .stop_conv = stm32f4_adc_stop_conv,
+ .is_started = stm32f4_adc_is_started,
+ .regular_started = stm32f4_adc_regular_started,
+ .injected_started = stm32f4_adc_injected_started,
+};
+
+static const struct of_device_id stm32f4_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
+
+static struct platform_driver stm32f4_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32f4-adc",
+ .of_match_table = stm32f4_adc_of_match,
+ },
+};
+
+module_platform_driver(stm32f4_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
This patch adds documentation of device tree bindings for the STM32 ADC.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 78 ++++++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
new file mode 100644
index 0000000..a9a8b3c
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -0,0 +1,78 @@
+STMicroelectronics STM32 ADC device driver
+
+STM32 ADC is a successive approximation analog-to-digital converter.
+It has several multiplexed input channels. Conversions can be performed
+in single, continuous, scan or discontinuous mode. Result of the ADC is
+stored in a left-aligned or right-aligned 32-bit data register.
+Conversions can be launched in software or using hardware triggers.
+
+The analog watchdog feature allows the application to detect if the input
+voltage goes beyond the user-defined, higher or lower thresholds.
+
+Each STM32 ADC block can have up to 3 ADC instances.
+
+Each instance supports two contexts to manage conversions, each one has its
+own configurable sequence and trigger:
+- regular conversion can be done in sequence, running in background
+- injected conversions have higher priority, and so have the ability to
+ interrupt regular conversion sequence (either triggered in SW or HW).
+ Regular sequence is resumed, in case it has been interrupted.
+
+Required properties:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- A pinctrl state named "default" for each ADC channel may be defined to set
+ inX ADC pins in mode of operation for analog input on external pin.
+- gpios: Array of gpios that may be configured as EXTi trigger sources.
+
+Example:
+ adc: adc at 40012000 {
+ compatible = "st,stm32f4-adc";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ vref-supply = <®_vref>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ gpios = <&gpioa 11 0>,
+ <&gpioa 15 0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc1: adc1-master at 0 {
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ st,adc-channels = <8>;
+ };
+ ...
+ other adc child nodes follow...
+ };
+
+Contents of a stm32 adc child node:
+-----------------------------------
+An ADC block node should contain at least one subnode, representing an
+ADC instance available on the machine.
+
+Required properties:
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Optional properties:
+- clocks: Input clock private to this ADC instance.
+- st,injected: Use injected conversion sequence on an ADC, rather than regular.
+- dmas: Phandle to dma channel for this ADC instance, only for regular
+ conversions. See ../../dma/dma.txt for details.
+- dma-names: Must be "rx" when dmas property is being used.
--
1.9.1
^ permalink raw reply related
* [PATCH 00/10] Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
This series adds support for STM32F4 ADC into IIO framework.
STM32F4 ADC is a 12-bit successive approximation analog-to-digital
converter. It has up to 19 multiplexed input channels. Conversions can
be performed in single, continuous, scan or discontinuous mode.
Conversions can be launched in software or using hardware triggers.
This driver has been developed and tested on STM32F429 eval board.
It consist of a core driver, to ease support for other STM32 family
ADCs, and a specific driver for STM32F4 ADC.
It allows to use direct or triggered buffer modes with triggers.
Optional support for DMA and few extended attributes is included.
Fabrice Gasnier (10):
Documentation: dt-bindings: Document STM32 ADC DT bindings
iio: adc: Add stm32 support
iio: adc: stm32: add optional dma support
iio: adc: stm32: add optional support for exti gpios
iio: adc: stm32: add trigger polarity ext attr
iio: adc: stm32: add ext attrs to configure sampling time
ARM: configs: stm32: enable IIO ADC driver
ARM: dts: stm32f429: Add adc support
ARM: dts: stm32f429: enable adc on eval board
ARM: dts: stm32f429: Add dma support to adc
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 78 ++
arch/arm/boot/dts/stm32429i-eval.dts | 30 +
arch/arm/boot/dts/stm32f429.dtsi | 68 +
arch/arm/configs/stm32_defconfig | 2 +
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32/Kconfig | 36 +
drivers/iio/adc/stm32/Makefile | 4 +
drivers/iio/adc/stm32/stm32-adc.c | 1334 ++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 489 +++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 652 ++++++++++
11 files changed, 2696 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
create mode 100644 drivers/iio/adc/stm32/Kconfig
create mode 100644 drivers/iio/adc/stm32/Makefile
create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
--
1.9.1
^ permalink raw reply
* [PATCH 3/4] dt-bindings: Update domain-idle-state binding to use correct compatibles
From: Lina Iyer @ 2016-10-25 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <383a62dd-b4d7-f764-baf1-ff73691b3cbc@arm.com>
On Tue, Oct 25 2016 at 09:59 -0600, Sudeep Holla wrote:
>
>
>On 25/10/16 16:26, Lina Iyer wrote:
>>Update domain-idle-state binding to use "domain-idle-state" compatible
>>from Documentation/devicetree/bindings/arm/idle-states.txt.
>>
>>Cc: <devicetree@vger.kernel.org>
>>Cc: Rob Herring <robh@kernel.org>
>>Suggested-by: Sudeep Holla <sudeep.holla@arm.com>
>>Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>---
>> Documentation/devicetree/bindings/power/power_domain.txt | 9 +++++----
>> 1 file changed, 5 insertions(+), 4 deletions(-)
>>
>>diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
>>index e165036..6fb53a3 100644
>>--- a/Documentation/devicetree/bindings/power/power_domain.txt
>>+++ b/Documentation/devicetree/bindings/power/power_domain.txt
>>@@ -30,8 +30,9 @@ Optional properties:
>> available in the next section.
>>
>> - domain-idle-states : A phandle of an idle-state that shall be soaked into a
>>- generic domain power state. The idle state definitions are
>>- compatible with arm,idle-state specified in [1].
>>+ generic domain power state. The idle state definitions must be
>>+ compatible with "domain-idle-state"
>
>I would reword the below a bit different so that it's flexible to be
>reused without "arm,idle-state".
>
>>as well as
>>+ "arm,idle-state" as defined in [1].
>
>'Idle states that are "arm,idle-state" compatible are generally
>"domain-idle-state" compatible as well if it's a PM domain.'
>
I believe we should have both compatible strings. Per [1], any CPU that
follows the idle state compatible *must* have "arm,idle-state" as a
compatible. Since we are re-using the same compatible, its only correct
that we retain what is already spec'd up in [1] and in addition provide
this new compatible.
Thanks,
Lina
>or something like that in line with what's in patch 2/4.
>
>That would give us the scope of reuse of "domain-idle-state" in device
>for future. Also it aligns with your patch 4/4.
>
>Otherwise, it looks good.
>
>--
>Regards,
>Sudeep
^ permalink raw reply
* [PATCH/RFT v2 11/17] USB: OHCI: make ohci-da8xx a separate driver
From: David Lechner @ 2016-10-25 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKXjFTM8RwsAut8cD0n0foxueSjR4VB4k4WJizXo9SyaW3viOA@mail.gmail.com>
On 10/25/2016 11:21 AM, Axel Haslam wrote:
> On Tue, Oct 25, 2016 at 6:12 PM, David Lechner <david@lechnology.com> wrote:
>> On 10/25/2016 02:39 AM, Axel Haslam wrote:
>>>
>>> On Tue, Oct 25, 2016 at 2:38 AM, David Lechner <david@lechnology.com>
>>> wrote:
>>>>
>>>> On 10/24/2016 11:46 AM, ahaslam at baylibre.com wrote:
>>>>>
>>>>>
>>>>> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX
>>>>> -#error "This file is DA8xx bus glue. Define
>>>>> CONFIG_ARCH_DAVINCI_DA8XX."
>>>>> -#endif
>>>>> +#include "ohci.h"
>>>>> +
>>>>> +#define DRIVER_DESC "OHCI DA8XX driver"
>>>>> +
>>>>> +static const char hcd_name[] = "ohci-da8xx";
>>>>
>>>>
>>>>
>>>> why static const char instead of #define? This is only used one time in a
>>>> pr_info, so it seems kind of pointless anyway.
>>>
>>>
>>> Other drivers are using static const for the same variable.
>>> i think static const is preferred over #define because #define doet give a
>>> type.
>>> If you dont mind ill keep it static const.
>>>
>>
>> If this string was used in this file more than one place, I would agree with
>> you, but currently it is only used as the argument of a pr_info(). The
>> string "ohci-da8xx" could just be included in the fmt string instead of
>> using "%s".
>
> I think the purpose was to use it in the .name of the platform_driver
> structure, too. only that not everybody is doing that, i looked at some bad
> examples :(
>
> would you agree to keep it if we use it in .name too?
>
> -Axel
>
>>
Yes, that will make more sense. ;-)
^ 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