* [PATCH fpga 8/9] fpga socfpga: Use the scatterlist interface
From: atull @ 2016-11-15 15:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161115043537.GA23253@obsidianresearch.com>
On Tue, 15 Nov 2016, Jason Gunthorpe wrote:
> On Sun, Nov 13, 2016 at 10:02:00PM -0600, atull wrote:
> > > I'd add a newop 'write_fragment' and a driver must define write_sg
> > > write_fragment, if write_fragment is used then the core supplies
> > > that loop.
> >
> > Sure, but isn't that just the old op? :)
>
> I'm fine with that too, but it is semantically different since write
> is called multiple times in this new world.. All the drivers seem fine
> with that today so it is probably OK ..
Not different.
>From 'fpga-mgr.txt':
The programming sequence is:
1. .write_init
2. .write (may be called once or multiple times)
3. .write_complete
The old write was be separate from write_init and write_complete
because I figured that in the future someone may be streaming in
the bitstream and not have the whole bitstream in memory. So
that is keeping with the original intention that it could be
called multiple times. The current API doesn't do that but the
fpga manager ops are intended to support that from the start.
Alan
>
> > There may also be common code that you added to configure_init
> > that should go in the core unless it's fpga-specific.
>
> Sure, the alignment test is easy enough to pull out..
>
> Jason
>
^ permalink raw reply
* [PATCH 2/2] ARM: zynq: Fix W=1 dtc 1.4 warnings
From: Julia Cartwright @ 2016-11-15 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <90eca8dfcdcef68ce5fe39f2329c4b61d2662598.1479218844.git.michal.simek@xilinx.com>
On Tue, Nov 15, 2016 at 03:07:27PM +0100, Michal Simek wrote:
> The patch removes these warnings reported by dtc 1.4:
> Warning (unit_address_vs_reg): Node /pmu has a reg or ranges property,
> but no unit name
> Warning (unit_address_vs_reg): Node /fixedregulator at 0 has a unit name,
> but no reg property
> Warning (unit_address_vs_reg): Node /memory has a reg or ranges
> property, but no unit name
>
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Reviewed-by: Julia Cartwright <julia@ni.com>
>
> ---
>
> arch/arm/boot/dts/zynq-7000.dtsi | 4 ++--
> arch/arm/boot/dts/zynq-parallella.dts | 2 +-
> arch/arm/boot/dts/zynq-zc702.dts | 2 +-
> arch/arm/boot/dts/zynq-zc706.dts | 2 +-
> arch/arm/boot/dts/zynq-zed.dts | 2 +-
> arch/arm/boot/dts/zynq-zybo.dts | 2 +-
> 6 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
> index f47a6c1cc752..402b5bbe3b5b 100644
> --- a/arch/arm/boot/dts/zynq-7000.dtsi
> +++ b/arch/arm/boot/dts/zynq-7000.dtsi
> @@ -42,14 +42,14 @@
> };
> };
>
> - pmu {
> + pmu at f8891000 {
> compatible = "arm,cortex-a9-pmu";
> interrupts = <0 5 4>, <0 6 4>;
> interrupt-parent = <&intc>;
> reg = < 0xf8891000 0x1000 0xf8893000 0x1000 >;
Style nit: we should drop the space before/after '<' and '>'; and,
perhaps separate the entries to be a bit more readable:
reg = <0xf8891000 0x1000>,
<0xf8893000 0x1000>;
Thanks,
Julia
^ permalink raw reply
* [PATCH 3/3] dt-bindings: add Tegra186 BPMP I2C binding
From: Thierry Reding @ 2016-11-15 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160719191442.15439-3-swarren@wwwdotorg.org>
On Tue, Jul 19, 2016 at 01:14:42PM -0600, Stephen Warren wrote:
> From: Stephen Warren <swarren@nvidia.com>
>
> In Tegra186, the BPMP (Boot and Power Management Processor) owns certain
> HW devices, such as the I2C controller for the power management I2C bus.
> Software running on other CPUs must perform IPC to the BPMP in order to
> execute transactions on that I2C bus. This binding describes an I2C bus
> that is accessed in such a fashion.
>
> Signed-off-by: Stephen Warren <swarren@nvidia.com>
> ---
> .../bindings/i2c/nvidia,tegra186-bpmp-i2c.txt | 42 ++++++++++++++++++++++
> 1 file changed, 42 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/i2c/nvidia,tegra186-bpmp-i2c.txt
I ended up cherry-picking this commit from U-Boot, which already had
both of the comments addressed that were discussed here. I also took the
liberty of adding Jon's Acked-by from this thread since it's effectively
the same commit.
Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161115/27395f3b/attachment.sig>
^ permalink raw reply
* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency.
From: william.helsby at stfc.ac.uk @ 2016-11-15 15:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161110174645.GB1041@n2100.armlinux.org.uk>
The option of moving the initrd code later (after early_init_fdt_reserve_self(); early_init_fdt_scan_reserved_mem() )
was tested.
This resulted in the initrd being disabled because early_init_fdt_scan_reserved_mem
Has already reserved the initrd area.
With memblock=debug
Machine model: Xspress3 Mini in PicoZed Developement Board
bootconsole [earlycon0] enabled
memblock_add: [0x00000000000000-0x0000000fffffff] flags 0x0 arm_add_
memory+0x14c/0x174
memblock_reserve: [0x00000000100000-0x00000000b71de7] flags 0x0 arm_
memblock_init+0x1c/0x284
memblock_reserve: [0x00000000004000-0x00000000007fff] flags 0x0 arm_
memblock_init+0x20/0x284
memblock_reserve: [0x00000000000000-0x00000000003fff] flags 0x0 arm_
memblock_init+0x30/0x284
memblock_reserve: [0x0000000fa5f000-0x0000000fa6207f] flags 0x0 arm_
memblock_init+0x38/0x284
memblock_reserve: [0x0000000fa5f000-0x0000000fa61fff] flags 0x0 early_
init_fdt_scan_reserved_mem+0x54/0x78
memblock_reserve: [0x0000000fa65000-0x0000000ffff46a] flags 0x0 early_
init_fdt_scan_reserved_mem+0x54/0x78
memblock_reserve: [0x0000000e800000-0x0000000f7fffff] flags 0x0 membl
ock_alloc_range_nid+0x3c/0x50
cma: Reserved 16 MiB at 0x0e800000
INITRD: 0x0fa65000+0x0059a46b overlaps in-use memory region - disabling initrd
MEMBLOCK configuration:
With the initrd code back to its normal place
memblock_add: [0x00000000000000-0x0000000fffffff] flags 0x0 arm_add_
memory+0x14c/0x174
memblock_reserve: [0x00000000100000-0x00000000b71de7] flags 0x0 arm_m
emblock_init+0x24/0x284
memblock_reserve: [0x0000000fa65000-0x0000000fffffff] flags 0x0 arm_me
mblock_init+0x1e4/0x284
memblock_reserve: [0x00000000004000-0x00000000007fff] flags 0x0 arm_me
mblock_init+0x210/0x284
memblock_reserve: [0x00000000000000-0x00000000003fff] flags 0x0 arm_me
mblock_init+0x220/0x284
memblock_reserve: [0x0000000fa5f000-0x0000000fa6207f] flags 0x0 arm_me
mblock_init+0x224/0x284
memblock_reserve: [0x0000000fa5f000-0x0000000fa61fff] flags 0x0 early_ini
t_fdt_scan_reserved_mem+0x54/0x78
memblock_reserve: [0x0000000fa65000-0x0000000ffff46a] flags 0x0 early_in
it_fdt_scan_reserved_mem+0x54/0x78
memblock_reserve: [0x0000000e800000-0x0000000f7fffff] flags 0x0 memblo
ck_alloc_range_nid+0x3c/0x50
cma: Reserved 16 MiB at 0x0e800000
MEMBLOCK configuration:
memory size = 0x10000000 reserved size = 0x2017e68
memory.cnt = 0x1
memory[0x0] [0x00000000000000-0x0000000fffffff], 0x10000000 bytes f
lags: 0x0
reserved.cnt = 0x5
reserved[0x0] [0x00000000000000-0x00000000007fff], 0x8000 bytes fl
ags: 0x0
reserved[0x1] [0x00000000100000-0x00000000b71de7], 0xa71de8 bytes fl
ags: 0x0
reserved[0x2] [0x0000000e800000-0x0000000f7fffff], 0x1000000 bytes flags: 0x0
reserved[0x3] [0x0000000fa5f000-0x0000000fa6207f], 0x3080 bytes flag
s: 0x0
reserved[0x4] [0x0000000fa65000-0x0000000fffffff], 0x59b000 bytes fl
ags: 0x0
Memory policy: Data cache writealloc
So poison the memory all the way from the start to end I would now suggest:
--- ../linux-xlnx-4.6.0-orig/arch/arm/mm/init.c 2016-11-15 15:10:44.476001509 +0000
+++ arch/arm/mm/init.c 2016-11-15 14:05:19.640969244 +0000
@@ -49,6 +49,8 @@
static phys_addr_t phys_initrd_start __initdata = 0;
static unsigned long phys_initrd_size __initdata = 0;
+static unsigned long initrd_reservation_start __initdata = 0;
+static unsigned long initrd_reservation_end __initdata = 0;
static int __init early_initrd(char *p)
{
@@ -255,11 +257,38 @@
phys_initrd_start = phys_initrd_size = 0;
}
if (phys_initrd_size) {
- memblock_reserve(phys_initrd_start, phys_initrd_size);
+ /* try to round the initrd start and end down and up to page boundaries,
+ so when freed, whole pages can be released.
+ However the initrd image may be adjacent to something else, so check that this rounding is OK
+ */
+ /* First try rounding the start down and end up */
+ phys_addr_t phys_initrd_reservation_start = phys_initrd_start & PAGE_MASK;
+ unsigned long size_to_reserve = PAGE_ALIGN(phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start;
+ if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) ||
+ memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) {
+ /* This either does fit or overlaps something else, so try just rounding end up */
+ phys_initrd_reservation_start = phys_initrd_start;
+ size_to_reserve = PAGE_ALIGN(phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start;
+ if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) ||
+ memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) {
+ /* This either does not fit or overlaps something else, so try just rounding start down */
+ phys_initrd_reservation_start = phys_initrd_start & PAGE_MASK;
+ size_to_reserve = (phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start;
+ if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) ||
+ memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) {
+ /* This either does not fit or overlaps something else, so do not round at all */
+ phys_initrd_reservation_start = phys_initrd_start;
+ size_to_reserve = (phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start;
+ }
+ }
+ }
+ memblock_reserve(phys_initrd_reservation_start, size_to_reserve);
/* Now convert initrd to virtual addresses */
initrd_start = __phys_to_virt(phys_initrd_start);
initrd_end = initrd_start + phys_initrd_size;
+ initrd_reservation_start = __phys_to_virt(phys_initrd_reservation_start);
+ initrd_reservation_end = initrd_reservation_start + size_to_reserve;
}
#endif
@@ -325,7 +354,12 @@
*/
static inline void poison_init_mem(void *s, size_t count)
{
- u32 *p = (u32 *)s;
+ u32 *p;
+ unsigned long start = (unsigned long)s, start_align;
+ start_align = (start+3) & ~3L; // Round up to 32 bit word boundary.
+ count -= (start_align-start);
+ p = (u32 *)start_align;
+ count &= ~3L;
for (; count != 0; count -= 4)
*p++ = 0xe7fddef0;
}
@@ -771,11 +805,11 @@
{
if (!keep_initrd) {
if (start == initrd_start)
- start = round_down(start, PAGE_SIZE);
+ start = initrd_reservation_start;
if (end == initrd_end)
- end = round_up(end, PAGE_SIZE);
+ end = initrd_reservation_end;
- poison_init_mem((void *)start, PAGE_ALIGN(end) - start);
+ poison_init_mem((void *)start, end-start);
free_reserved_area((void *)start, (void *)end, -1, "initrd");
}
}
^ permalink raw reply
* [PATCH 1/2] ARM: zynq: Remove skeleton.dtsi
From: Julia Cartwright @ 2016-11-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <2d42e25d7dd03e562e12e71fd037837737115940.1479218844.git.michal.simek@xilinx.com>
On Tue, Nov 15, 2016 at 03:07:26PM +0100, Michal Simek wrote:
> Based on
> "ARM: dts: explicitly mark skeleton.dtsi as deprecated"
> (sha1: 9c0da3cc61f1233c2782e2d3d91e3d0707dd4ba5)
> skeleton.dtsi is deprecated.
> Move address and size-cells directly to zynq-7000.dtsi.
>
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Reviewed-by: Julia Cartwright <julia@ni.com>
^ permalink raw reply
* [PATCH v3 2/2] arm64: dts: rockchip: add usb2-phy otg-port support for rk3399
From: Heiko Stuebner @ 2016-11-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478520529-8869-3-git-send-email-wulf@rock-chips.com>
Am Montag, 7. November 2016, 20:08:49 CET schrieb William Wu:
> Add otg-port nodes for both u2phy0 and u2phy1. The otg-port can
> be used for USB2.0 part of USB3.0 OTG controller.
>
> Signed-off-by: William Wu <wulf@rock-chips.com>
> ---
> Changes in v3:
> - None
>
> Changes in v2:
> - None
>
> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index b65c193..ea2df51 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -1095,6 +1095,17 @@
> clock-output-names = "clk_usbphy0_480m";
> status = "disabled";
>
> + u2phy0_otg: otg-port {
> + #phy-cells = <0>;
> + interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH 0>;
> + interrupt-names = "otg-bvalid", "otg-id",
> + "linestate";
> + status = "disabled";
> + };
> +
> +
applied to my dts64 branch after removing that double empty line and also
switching host and otg sub nodes to get alphabetical sorting.
Thanks
Heiko
^ permalink raw reply
* [PATCH 2/3] dt-bindings: allow child nodes inside the Tegra BPMP
From: Thierry Reding @ 2016-11-15 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160719191442.15439-2-swarren@wwwdotorg.org>
On Tue, Jul 19, 2016 at 01:14:41PM -0600, Stephen Warren wrote:
> From: Stephen Warren <swarren@nvidia.com>
>
> The BPMP implements some services which must be represented by separate
> nodes. For example, it can provide access to certain I2C controllers, and
> the I2C bindings represent each I2C controller as a device tree node.
> Update the binding to describe how the BPMP supports this.
>
> Signed-off-by: Stephen Warren <swarren@nvidia.com>
> ---
> .../bindings/firmware/nvidia,tegra186-bpmp.txt | 23 ++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
Applied, with bpmp-i2c changed to i2c as per Rob's comment.
Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161115/3518be8e/attachment.sig>
^ permalink raw reply
* [PATCH 1/3] dt-bindings: add power domains to Tegra BPMP firmware
From: Thierry Reding @ 2016-11-15 15:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160719191442.15439-1-swarren@wwwdotorg.org>
On Tue, Jul 19, 2016 at 01:14:40PM -0600, Stephen Warren wrote:
> From: Stephen Warren <swarren@nvidia.com>
>
> The Tegra186 BPMP is also a provider of power domains. Enhance the device
> tree binding to describe this.
>
> Signed-off-by: Stephen Warren <swarren@nvidia.com>
> ---
> These patches all build on top of Joseph Lo's baseline BPMP binding patches[1]
> and enhance them to represent a few more features of the firmware.
>
> [1] https://lkml.org/lkml/2016/7/19/280
> "[PATCH V3 01/10] Documentation: dt-bindings: mailbox: tegra: Add binding for HSP mailbox"
>
> .../bindings/firmware/nvidia,tegra186-bpmp.txt | 10 ++++--
> include/dt-bindings/power/tegra186-powergate.h | 39 ++++++++++++++++++++++
> 2 files changed, 46 insertions(+), 3 deletions(-)
> create mode 100644 include/dt-bindings/power/tegra186-powergate.h
Applied, thanks.
One small comment below...
> diff --git a/include/dt-bindings/power/tegra186-powergate.h b/include/dt-bindings/power/tegra186-powergate.h
> new file mode 100644
> index 000000000000..388d6e228dc8
> --- /dev/null
> +++ b/include/dt-bindings/power/tegra186-powergate.h
> @@ -0,0 +1,39 @@
> +/*
> + * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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 _DT_BINDINGS_POWER_TEGRA186_POWERGATE_H
> +#define _DT_BINDINGS_POWER_TEGRA186_POWERGATE_H
> +
> +#define TEGRA186_POWER_DOMAIN_AUD 0
> +#define TEGRA186_POWER_DOMAIN_DFD 1
> +#define TEGRA186_POWER_DOMAIN_DISP 2
> +#define TEGRA186_POWER_DOMAIN_DISPB 3
> +#define TEGRA186_POWER_DOMAIN_DISPC 4
> +#define TEGRA186_POWER_DOMAIN_ISPA 5
> +#define TEGRA186_POWER_DOMAIN_NVDEC 6
> +#define TEGRA186_POWER_DOMAIN_NVJPG 7
> +#define TEGRA186_POWER_DOMAIN_MPE 8
> +#define TEGRA186_POWER_DOMAIN_PCX 9
> +#define TEGRA186_POWER_DOMAIN_SAX 10
> +#define TEGRA186_POWER_DOMAIN_VE 11
> +#define TEGRA186_POWER_DOMAIN_VIC 12
> +#define TEGRA186_POWER_DOMAIN_XUSBA 13
> +#define TEGRA186_POWER_DOMAIN_XUSBB 14
> +#define TEGRA186_POWER_DOMAIN_XUSBC 15
> +#define TEGRA186_POWER_DOMAIN_GPU 43
> +#define TEGRA186_POWER_DOMAIN_MAX 44
It's slightly odd that these are named TEGRA186_POWER_DOMAIN_* since
power domain is a Linuxism. All documentation that I've seen calls these
powergates.
I guess since this is now ABI there is not much we can do to rectify it.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161115/23dffb84/attachment.sig>
^ permalink raw reply
* [PATCH] arm/arm64: KVM: VGIC: limit ITARGETSR bits to number of VCPUs
From: Andre Przywara @ 2016-11-15 15:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <061bf473-4597-22fe-664a-b83dbe6b39e1@arm.com>
Hi Marc,
On 15/11/16 14:41, Marc Zyngier wrote:
> Hi Andre,
>
> On 15/11/16 14:27, Andre Przywara wrote:
>> The GICv2 spec says in section 4.3.12 that a "CPU targets field bit that
>> corresponds to an unimplemented CPU interface is RAZ/WI."
>> Currently we allow the guest to write any value in there and it can
>> read that back.
>> Mask the written value with the proper CPU mask to be spec compliant.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>> virt/kvm/arm/vgic/vgic-mmio-v2.c | 3 ++-
>> 1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c
>> index b44b359..e59d4c7 100644
>> --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
>> +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
>> @@ -129,6 +129,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
>> unsigned long val)
>> {
>> u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
>> + u8 cpu_mask = (1 << atomic_read(&vcpu->kvm->online_vcpus)) - 1;
>
> For the sake of avoiding open-coding things, how about using GENMASK?
Yes.
>
>> int i;
>>
>> /* GICD_ITARGETSR[0-7] are read-only */
>> @@ -141,7 +142,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
>>
>> spin_lock(&irq->irq_lock);
>>
>> - irq->targets = (val >> (i * 8)) & 0xff;
>> + irq->targets = ((val >> (i * 8)) & 0xff) & cpu_mask;
>
> Can't you just drop the '& 0xff' part, since cpu_mask is guaranteed to
> be more restrictive?
Well, and also irq->targets is an u8 ...
Fixed both.
Thanks!
Andre.
>> target = irq->targets ? __ffs(irq->targets) : 0;
>> irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
>>
>>
>
> Thanks,
>
> M.
>
^ permalink raw reply
* [PATCH v3 6/6] ARM: dts: stm32f429: enable adc on eval board
From: Fabrice Gasnier @ 2016-11-15 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-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 | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 6bfc595..c144735 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,14 @@
pinctrl-names = "default";
status = "okay";
};
+
+&adc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ vref-supply = <®_vref>;
+ status = "okay";
+ adc3: adc at 200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v3 5/6] ARM: dts: stm32f429: Add adc support
From: Fabrice Gasnier @ 2016-11-15 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-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 | 49 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..f198132 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -172,6 +172,49 @@
status = "disabled";
};
+ adc: adc at 40012000 {
+ compatible = "st,stm32f4-adc-core";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ adc1: adc at 0 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ status = "disabled";
+ };
+
+ adc2: adc at 100 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ interrupt-parent = <&adc>;
+ interrupts = <1>;
+ status = "disabled";
+ };
+
+ adc3: adc at 200 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ interrupt-parent = <&adc>;
+ interrupts = <2>;
+ status = "disabled";
+ };
+ };
+
syscfg: system-config at 40013800 {
compatible = "syscon";
reg = <0x40013800 0x400>;
@@ -332,6 +375,12 @@
slew-rate = <2>;
};
};
+
+ adc3_in8_pin: adc at 200 {
+ pins {
+ pinmux = <STM32F429_PF10_FUNC_ANALOG>;
+ };
+ };
};
rcc: rcc at 40023810 {
--
1.9.1
^ permalink raw reply related
* [PATCH v3 4/6] ARM: configs: stm32: enable ADC driver
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/configs/stm32_defconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 1e5ec2a..5d241e0 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -57,6 +57,9 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_DMADEVICES=y
CONFIG_STM32_DMA=y
+CONFIG_IIO=y
+CONFIG_STM32_ADC_CORE=y
+CONFIG_STM32_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 v3 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-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 | 10 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc.c | 518 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 529 insertions(+)
create mode 100644 drivers/iio/adc/stm32-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ff30239..f93b990 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -432,6 +432,16 @@ config STM32_ADC_CORE
This driver can also be built as a module. If so, the module
will be called stm32-adc-core.
+config STM32_ADC
+ tristate "STMicroelectronics STM32 adc"
+ depends on STM32_ADC_CORE
+ help
+ Say yes here to build support for 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 STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a1e8f44..8e02a94 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
new file mode 100644
index 0000000..5715e79
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc.c
@@ -0,0 +1,518 @@
+/*
+ * 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/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include "stm32-adc-core.h"
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADC_SR 0x00
+#define STM32F4_ADC_CR1 0x04
+#define STM32F4_ADC_CR2 0x08
+#define STM32F4_ADC_SMPR1 0x0C
+#define STM32F4_ADC_SMPR2 0x10
+#define STM32F4_ADC_HTR 0x24
+#define STM32F4_ADC_LTR 0x28
+#define STM32F4_ADC_SQR1 0x2C
+#define STM32F4_ADC_SQR2 0x30
+#define STM32F4_ADC_SQR3 0x34
+#define STM32F4_ADC_JSQR 0x38
+#define STM32F4_ADC_JDR1 0x3C
+#define STM32F4_ADC_JDR2 0x40
+#define STM32F4_ADC_JDR3 0x44
+#define STM32F4_ADC_JDR4 0x48
+#define STM32F4_ADC_DR 0x4C
+
+/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_STRT BIT(4)
+#define STM32F4_EOC BIT(1)
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4_ADC_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADC_SQR1 - bit fields */
+#define STM32F4_L_SHIFT 20
+#define STM32F4_L_MASK GENMASK(23, 20)
+
+/* STM32F4_ADC_SQR3 - bit fields */
+#define STM32F4_SQ1_SHIFT 0
+#define STM32F4_SQ1_MASK GENMASK(4, 0)
+
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @offset: ADC instance register offset in ADC block
+ * @completion: end of single conversion completion
+ * @buffer: data buffer
+ * @clk: clock for this adc instance
+ * @irq: interrupt for this adc instance
+ * @lock: spinlock
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ u32 offset;
+ struct completion completion;
+ u16 *buffer;
+ struct clk *clk;
+ int irq;
+ spinlock_t lock; /* interrupt lock */
+};
+
+/**
+ * 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;
+};
+
+/* Input definitions common for all STM32F4 instances */
+static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
+ { 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" },
+};
+
+/**
+ * STM32 ADC registers access routines
+ * @adc: stm32 adc instance
+ * @reg: reg offset in adc instance
+ *
+ * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
+ * for adc1, adc2 and adc3.
+ */
+static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ return readl_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
+{
+ return readw_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static 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 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_adc_conv_irq_enable() - Enable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+};
+
+/**
+ * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+}
+
+/**
+ * stm32_adc_start_conv() - Start conversions for regular channels.
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_start_conv(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
+}
+
+static void stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
+
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
+}
+
+/**
+ * stm32_adc_single_conv() - Performs a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @res: conversion result
+ *
+ * The function performs a single conversion on a given channel:
+ * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
+ * - Use SW trigger
+ * - Start conversion, then wait for interrupt completion.
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *res)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ long timeout;
+ u32 val;
+ u16 result;
+ int ret;
+
+ reinit_completion(&adc->completion);
+
+ adc->buffer = &result;
+
+ /* Program chan number in regular sequence */
+ val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
+ val &= ~STM32F4_SQ1_MASK;
+ val |= chan->channel << STM32F4_SQ1_SHIFT;
+ stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
+
+ /* Set regular sequence len (0 for 1 conversion) */
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
+
+ /* Trigger detection disabled (conversion can be launched in SW) */
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+
+ stm32_adc_conv_irq_enable(adc);
+
+ stm32_adc_start_conv(adc);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &adc->completion, STM32_ADC_TIMEOUT);
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ } else if (timeout < 0) {
+ ret = timeout;
+ } else {
+ *res = result;
+ ret = IIO_VAL_INT;
+ }
+
+ stm32_adc_stop_conv(adc);
+
+ stm32_adc_conv_irq_disable(adc);
+
+ 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;
+
+ 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);
+ else
+ ret = -EINVAL;
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = adc->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t stm32_adc_isr(int irq, void *data)
+{
+ struct stm32_adc *adc = data;
+ u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
+
+ if (status & STM32F4_EOC) {
+ *adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
+ complete(&adc->completion);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+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,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .of_xlate = stm32_adc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+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)
+{
+ chan->type = channel->type;
+ chan->channel = channel->channel;
+ chan->datasheet_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 = 12;
+ chan->scan_type.storagebits = 16;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *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;
+ u32 val;
+
+ num_channels = of_property_count_u32_elems(node, "st,adc-channels");
+ if (num_channels < 0 ||
+ num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+ return num_channels < 0 ? num_channels : -EINVAL;
+ }
+
+ 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) {
+ if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+ return -EINVAL;
+ }
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ &stm32f4_adc123_channels[val],
+ scan_index);
+ scan_index++;
+ }
+
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stm32_adc *adc;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->common = dev_get_drvdata(pdev->dev.parent);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ platform_set_drvdata(pdev, adc);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return adc->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
+ 0, pdev->name, adc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ adc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(adc->clk)) {
+ dev_err(&pdev->dev, "Can't get clock\n");
+ return PTR_ERR(adc->clk);
+ }
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk enable failed\n");
+ return ret;
+ }
+
+ ret = stm32_adc_chan_of_init(indio_dev);
+ if (ret < 0)
+ goto err_clk_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "iio dev register failed\n");
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc *adc = platform_get_drvdata(pdev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc",
+ .of_match_table = stm32_adc_of_match,
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc");
--
1.9.1
^ permalink raw reply related
* [PATCH v3 2/6] iio: adc: Add support for STM32 ADC core
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
Converter). STM32 ADC can be composed of up to 3 ADCs with shared
resources like clock prescaler, common interrupt line and analog
reference voltage.
This core driver basically manages shared resources.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 13 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc-core.c | 303 +++++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32-adc-core.h | 52 +++++++
4 files changed, 369 insertions(+)
create mode 100644 drivers/iio/adc/stm32-adc-core.c
create mode 100644 drivers/iio/adc/stm32-adc-core.h
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf32..ff30239 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -419,6 +419,19 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
+config STM32_ADC_CORE
+ tristate "STMicroelectronics STM32 adc core"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ help
+ Select this option to enable the core driver for 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-core.
+
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..a1e8f44 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
+obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
new file mode 100644
index 0000000..4214b0c
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -0,0 +1,303 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * Inspired from: fsl-imx25-tsadc
+ *
+ * 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/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include "stm32-adc-core.h"
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_EOC3 BIT(17)
+#define STM32F4_EOC2 BIT(9)
+#define STM32F4_EOC1 BIT(1)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/* STM32 F4 maximum analog clock rate (from datasheet) */
+#define STM32F4_ADC_MAX_CLK_RATE 36000000
+
+/**
+ * struct stm32_adc_priv - stm32 ADC core private data
+ * @irq: irq for ADC block
+ * @domain: irq domain reference
+ * @aclk: clock reference for the analog circuitry
+ * @vref: regulator reference
+ * @common: common data for all ADC instances
+ */
+struct stm32_adc_priv {
+ int irq;
+ struct irq_domain *domain;
+ struct clk *aclk;
+ struct regulator *vref;
+ struct stm32_adc_common common;
+};
+
+static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
+{
+ return container_of(com, struct stm32_adc_priv, common);
+}
+
+/* STM32F4 ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
+ * @priv: stm32 ADC core private data
+ * Select clock prescaler used for analog conversions, before using ADC.
+ */
+static int stm32f4_adc_clk_sel(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ unsigned long rate;
+ u32 val;
+ int i;
+
+ rate = clk_get_rate(priv->aclk);
+ for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+ if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+ return -EINVAL;
+
+ val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
+ val &= ~STM32F4_ADC_ADCPRE_MASK;
+ val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+ writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
+
+ dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
+ rate / (stm32f4_pclk_div[i] * 1000));
+
+ return 0;
+}
+
+/* ADC common interrupt for all instances */
+static void stm32_adc_irq_handler(struct irq_desc *desc)
+{
+ struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 status;
+
+ chained_irq_enter(chip, desc);
+ status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
+
+ if (status & STM32F4_EOC1)
+ generic_handle_irq(irq_find_mapping(priv->domain, 0));
+
+ if (status & STM32F4_EOC2)
+ generic_handle_irq(irq_find_mapping(priv->domain, 1));
+
+ if (status & STM32F4_EOC3)
+ generic_handle_irq(irq_find_mapping(priv->domain, 2));
+
+ chained_irq_exit(chip, desc);
+};
+
+static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops stm32_adc_domain_ops = {
+ .map = stm32_adc_domain_map,
+ .unmap = stm32_adc_domain_unmap,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int stm32_adc_irq_probe(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return priv->irq;
+ }
+
+ priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
+ &stm32_adc_domain_ops,
+ priv);
+ if (!priv->domain) {
+ dev_err(&pdev->dev, "Failed to add irq domain\n");
+ return -ENOMEM;
+ }
+
+ irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
+ irq_set_handler_data(priv->irq, priv);
+
+ return 0;
+}
+
+static void stm32_adc_irq_remove(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ int hwirq;
+
+ for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
+ irq_domain_remove(priv->domain);
+ irq_set_chained_handler(priv->irq, NULL);
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct stm32_adc_priv *priv;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->common.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->common.base))
+ return PTR_ERR(priv->common.base);
+
+ priv->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(priv->vref)) {
+ ret = PTR_ERR(priv->vref);
+ dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref enable failed\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_regulator_disable;
+ }
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+ priv->aclk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(priv->aclk)) {
+ ret = PTR_ERR(priv->aclk);
+ dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+ goto err_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "adc clk enable failed\n");
+ goto err_regulator_disable;
+ }
+
+ ret = stm32f4_adc_clk_sel(pdev, priv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "adc clk selection failed\n");
+ goto err_clk_disable;
+ }
+
+ ret = stm32_adc_irq_probe(pdev, priv);
+ if (ret < 0)
+ goto err_clk_disable;
+
+ platform_set_drvdata(pdev, &priv->common);
+
+ ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to populate DT children\n");
+ goto err_irq_remove;
+ }
+
+ return 0;
+
+err_irq_remove:
+ stm32_adc_irq_remove(pdev, priv);
+
+err_clk_disable:
+ clk_disable_unprepare(priv->aclk);
+
+err_regulator_disable:
+ regulator_disable(priv->vref);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = platform_get_drvdata(pdev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ of_platform_depopulate(&pdev->dev);
+ stm32_adc_irq_remove(pdev, priv);
+ clk_disable_unprepare(priv->aclk);
+ regulator_disable(priv->vref);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc-core" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc-core",
+ .of_match_table = stm32_adc_of_match,
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc-core");
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
new file mode 100644
index 0000000..081fa5f
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -0,0 +1,52 @@
+/*
+ * 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_ADC_MAX_ADCS 3
+#define STM32_ADCX_COMN_OFFSET 0x300
+
+/**
+ * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
+ * @base: control registers base cpu addr
+ * @vref_mv: vref voltage (mv)
+ */
+struct stm32_adc_common {
+ void __iomem *base;
+ int vref_mv;
+};
+
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v3 1/6] Documentation: dt-bindings: Document STM32 ADC DT bindings
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-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 | 83 ++++++++++++++++++++++
1 file changed, 83 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..49ed82e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -0,0 +1,83 @@
+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.
+
+Contents of a stm32 adc root node:
+-----------------------------------
+Required properties:
+- compatible: Should be "st,stm32f4-adc-core".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC block.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- interrupt-controller: Identifies the controller node as interrupt-parent
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #interrupt-cells = <1>;
+- #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.
+
+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:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- clocks: Input clock private to this ADC instance.
+- interrupt-parent: Phandle to the parent interrupt controller.
+- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc at 0, 1 for adc at 100 or
+ 2 for adc at 200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+ It can have up to 16 channels, numbered from 0 to 15 (resp. for in0..in15).
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Example:
+ adc: adc at 40012000 {
+ compatible = "st,stm32f4-adc-core";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ vref-supply = <®_vref>;
+ interrupt-controller;
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc at 0 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ st,adc-channels = <8>;
+ };
+ ...
+ other adc child nodes follow...
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v3 0/6] Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 15:30 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 with 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 manage common resources shared
between up to 3 ADC instances and an ADC driver to manage each adc
instance.
Changes in v3:
- Core driver moved to iio/adc.
- Build fix.
- Updates following Jonathan's and Lars's remarks.
- Binding: adc child clock is mandatory.
Changes in v2:
- Replace single driver model by MFD approach, to handle up to 3 ADCs
as separate devices. Each ADC device then registers a unique IIO
device.
- Make driver as simple as possible for the first instance, to ease
review. For now, I dropped complexity by removing injected support,
triggered buffer mode, dmas.
- Removed abstraction layer (indirection routines, ops) as only stm32f4
is supported.
Fabrice Gasnier (6):
Documentation: dt-bindings: Document STM32 ADC DT bindings
iio: adc: Add support for STM32 ADC core
iio: adc: Add support for STM32 ADC
ARM: configs: stm32: enable ADC driver
ARM: dts: stm32f429: Add adc support
ARM: dts: stm32f429: enable adc on eval board
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 83 ++++
arch/arm/boot/dts/stm32429i-eval.dts | 25 +
arch/arm/boot/dts/stm32f429.dtsi | 49 ++
arch/arm/configs/stm32_defconfig | 3 +
drivers/iio/adc/Kconfig | 23 +
drivers/iio/adc/Makefile | 2 +
drivers/iio/adc/stm32-adc-core.c | 303 ++++++++++++
drivers/iio/adc/stm32-adc-core.h | 52 +++
drivers/iio/adc/stm32-adc.c | 518 +++++++++++++++++++++
9 files changed, 1058 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
create mode 100644 drivers/iio/adc/stm32-adc-core.c
create mode 100644 drivers/iio/adc/stm32-adc-core.h
create mode 100644 drivers/iio/adc/stm32-adc.c
--
1.9.1
^ permalink raw reply
* [PATCH V7 1/3] tracing: add a possibility of exporting function trace to other places instead of ring buffer only
From: Steven Rostedt @ 2016-11-15 15:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAG2=9p-2ZU3Zk0musHcwAEk4nM6RR0U7JhgyjZ+zjLaVLwz1ig@mail.gmail.com>
On Tue, 15 Nov 2016 16:14:29 +0800
Chunyan Zhang <zhang.chunyan@linaro.org> wrote:
>
> > Then why have a
> >
> > if (export->write)
> >
> >
> > Is there every going to be a case where export will not have a write
> > function?
>
> There shouldn't be.
>
> I can move this if statement to the register_ftrace_export() to ensure
> users won't wrongly use it, that's saying the write() of trace_export
> has been set before being registered to 'ftrace_exports_list'.
>
Looks like it's already there:
+int register_ftrace_export(struct trace_export *export)
+{
+ if (WARN_ON_ONCE(!export->write))
+ return -1;
-- Steve
^ permalink raw reply
* [PATCH/RESEND] recordmcount: arm: Implement make_nop
From: Steven Rostedt @ 2016-11-15 15:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu-Cp775gbLxPcJQ4G2nEnwm9G_gxh5B1Sf2LGkkov_OZA@mail.gmail.com>
On Tue, 15 Nov 2016 14:19:44 +0000
Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> On 19 October 2016 at 00:42, Stephen Boyd <sboyd@codeaurora.org> wrote:
> > In similar spirit to x86 and arm64 support, add a make_nop_arm()
> > to replace calls to mcount with a nop in sections that aren't
> > traced.
> >
> > Cc: Russell King <linux@arm.linux.org.uk>
> > Acked-by: Rabin Vincent <rabin@rab.in>
> > Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> > ---
> > scripts/recordmcount.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 65 insertions(+)
> >
> > diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
> > index 5423a58d1b06..aeb34223167c 100644
> > --- a/scripts/recordmcount.c
> > +++ b/scripts/recordmcount.c
> > @@ -213,6 +213,59 @@ static int make_nop_x86(void *map, size_t const offset)
> > return 0;
> > }
> >
> > +static unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */
> > +static unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */
>
> Shouldn't you be taking the difference between BE8 and BE32 into
> account here? IIRC, BE8 uses little endian encoding for instructions.
>
I was just about to push this to linux-next (where I don't rebase). I'm
guessing I should hold off then.
Luckily, this was the last patch of my tree that I tested, and I can
just remove that one.
-- Steve
^ permalink raw reply
* [PATCH v2 2/3] efi/libstub: add random.c to ARM build
From: Ard Biesheuvel @ 2016-11-15 15:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <2189664.D28O3TYR8x@wuerfel>
On 15 November 2016 at 15:11, Arnd Bergmann <arnd@arndb.de> wrote:
> On Wednesday, November 2, 2016 9:37:13 AM CET Ard Biesheuvel wrote:
>> @@ -98,7 +100,7 @@
>> efi_memory_desc_t *md = (void *)memory_map + map_offset;
>> unsigned long slots;
>>
>> - slots = get_entry_num_slots(md, size, align);
>> + slots = get_entry_num_slots(md, size, ilog2(align));
>> MD_NUM_SLOTS(md) = slots;
>> total_slots += slots;
>> }
>> """
>>
>> This is because ARM does not have a division routine in the
>> decompressor, and the fact that the division by 'align' should always
>> involve a power of 2 is not visible to the compiler.
>>
>> If nobody objects, I will fold this in when applying
>>
>>
>
> I'm getting a link error here when building with -Os:
>
> drivers/firmware/efi/libstub/random.stub.o: In function `efi_random_alloc':
> random.c:(.text.efi_random_alloc+0x264): undefined reference to `__aeabi_llsr'
>
> If I compile this with -O2, the ilog2 gets inlined and everything
> works.
>
This is caused by the fact that 'start' and 'end are u64 rather than
unsigned long, and the stub does not have the u64 logical shift right
routines.
But interestingly, it does cover another issue with this code, i.e.,
that you cannot do allocations over 4 GB in the ARM stub, even on LPAE
capable hardware.
I will send out a patch to fix this.
Thanks,
Ard.
^ permalink raw reply
* [PATCH] ARM: ftrace: fix syscall name matching
From: Rabin Vincent @ 2016-11-15 15:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161114104008.36e9c40d@gandalf.local.home>
On Mon, Nov 14, 2016 at 10:40:08AM -0500, Steven Rostedt wrote:
> On Mon, 14 Nov 2016 13:40:17 +0000
> Russell King - ARM Linux <linux@armlinux.org.uk> wrote:
> > On Mon, Nov 14, 2016 at 02:03:45PM +0100, Rabin Vincent wrote:
> > > +static inline bool arch_syscall_match_sym_name(const char *sym,
> > > + const char *name)
> > > +{
> > > + /* Skip sys_ */
> > > + sym += 4;
> > > + name += 4;
> >
> > Is this really safe? What guarantees that we can wind forward four
> > bytes here? If it's always safe, it needs a better comment than just
> > two words.
>
> I believe it is, but a comment would do well.
I ended up just getting rid of the skip and comparing the whole name
instead. I've sent a v2.
^ permalink raw reply
* [PATCH v2 2/3] efi/libstub: add random.c to ARM build
From: Arnd Bergmann @ 2016-11-15 15:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu_=et=2zHBTYOr9thz2kS0cXHHYg96oWGRdD3D10fqXtw@mail.gmail.com>
On Wednesday, November 2, 2016 9:37:13 AM CET Ard Biesheuvel wrote:
> @@ -98,7 +100,7 @@
> efi_memory_desc_t *md = (void *)memory_map + map_offset;
> unsigned long slots;
>
> - slots = get_entry_num_slots(md, size, align);
> + slots = get_entry_num_slots(md, size, ilog2(align));
> MD_NUM_SLOTS(md) = slots;
> total_slots += slots;
> }
> """
>
> This is because ARM does not have a division routine in the
> decompressor, and the fact that the division by 'align' should always
> involve a power of 2 is not visible to the compiler.
>
> If nobody objects, I will fold this in when applying
>
>
I'm getting a link error here when building with -Os:
drivers/firmware/efi/libstub/random.stub.o: In function `efi_random_alloc':
random.c:(.text.efi_random_alloc+0x264): undefined reference to `__aeabi_llsr'
If I compile this with -O2, the ilog2 gets inlined and everything
works.
Arnd
^ permalink raw reply
* [BUG] Suspicious RCU usage: NFS client - 4.9-rc4
From: Anna Schumaker @ 2016-11-15 15:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161115150706.GW1041@n2100.armlinux.org.uk>
Hi Russel,
On 11/15/2016 10:07 AM, Russell King - ARM Linux wrote:
> While booting two of my machines, I noticed this splat on the console.
> Not sure if this is due to a RCU change or NFS change, so adding folk
> on both sides.
The patch to fix this went into rc5 :)
Thanks for reporting!
Anna
>
> ===============================
> [ INFO: suspicious RCU usage. ]
> 4.9.0-rc4+ #2050 Not tainted
> -------------------------------
> net/sunrpc/clnt.c:2773 suspicious rcu_dereference_check() usage!
>
> other info that might help us debug this:
>
>
> rcu_scheduler_active = 1, debug_locks = 0
> 1 lock held by mount.nfs/1499:
> #0:
> (
> &(&nn->nfs_client_lock)->rlock
> ){+.+...}
> , at:
> [<c026c6d4>] nfs_get_client+0xe8/0x580
>
> stack backtrace:
> CPU: 1 PID: 1499 Comm: mount.nfs Not tainted 4.9.0-rc4+ #2050
> Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
> Backtrace:
> [<c0013b84>] (dump_backtrace) from [<c0013dc4>] (show_stack+0x18/0x1c)
> r6:60010013 r5:ffffffff r4:00000000 r3:00000000
> [<c0013dac>] (show_stack) from [<c035d300>] (dump_stack+0xa4/0xdc)
> [<c035d25c>] (dump_stack) from [<c008093c>] (lockdep_rcu_suspicious+0xbc/0x11c)
> r6:00000ad5 r5:c08f4078 r4:ee58a4c0 r3:ee58a4c0
> [<c0080880>] (lockdep_rcu_suspicious) from [<c0721ba4>] (rpc_clnt_xprt_switch_has_addr+0x134/0x15c)
> r7:ef27dd80 r6:c0ae3972 r5:eeb5a110 r4:ee930400
> [<c0721a70>] (rpc_clnt_xprt_switch_has_addr) from [<c026c814>] (nfs_get_client+0x228/0x580)
> r6:ef27ddd0 r5:eeb5a110 r4:00000000
> [<c026c5ec>] (nfs_get_client) from [<c026cc20>] (nfs_create_server+0xb4/0x3d4)
> r10:edaebe6c r9:c0a84d54 r8:edaebd30 r7:edf7b480 r6:00000000 r5:ed50a000
> r4:eeb5a000
> [<c026cb6c>] (nfs_create_server) from [<c028a2f8>] (nfs3_create_server+0x10/0x28)
> r10:00000000 r9:edbea200 r8:c0a84d54 r7:edaebe6c r6:00000000 r5:00000001
> r4:eeb5a000
> [<c028a2e8>] (nfs3_create_server) from [<c02792a0>] (nfs_try_mount+0x188/0x280)
> r4:eeb5a000 r3:c028a2e8
> [<c0279118>] (nfs_try_mount) from [<c027b28c>] (nfs_fs_mount+0x420/0x8c0)
> r10:00000000 r9:00000400 r8:edbea200 r7:edbea200 r6:c0a84d54 r5:edbea206
> r4:eeb5a000
> [<c027ae6c>] (nfs_fs_mount) from [<c016eaf4>] (mount_fs+0x1c/0xa8)
> r10:ed4f5000 r9:edbea340 r8:c0a82204 r7:c019091c r6:c0a82204 r5:edbea200
> r4:ed8a3700
> [<c016ead8>] (mount_fs) from [<c018d6d0>] (vfs_kern_mount+0x5c/0x138)
> r6:00000000 r5:edbea200 r4:ed8a3700
> [<c018d674>] (vfs_kern_mount) from [<c019091c>] (do_mount+0x164/0xc58)
> r10:c0191774 r8:edbea200 r7:00000020 r6:ed4f5000 r5:c0a82204 r4:00000000
> [<c01907b8>] (do_mount) from [<c0191774>] (SyS_mount+0x7c/0xa4)
> r10:00000000 r9:edaea000 r8:0078c0f8 r7:00000000 r6:ed4f5000 r5:edbea200
> r4:edbea340
> [<c01916f8>] (SyS_mount) from [<c000fd60>] (ret_fast_syscall+0x0/0x1c)
> r8:c000ff04 r7:00000015 r6:0078dd50 r5:bed2bad0 r4:007968a8
>
>
^ permalink raw reply
* [BUG] Suspicious RCU usage: NFS client - 4.9-rc4
From: Russell King - ARM Linux @ 2016-11-15 15:07 UTC (permalink / raw)
To: linux-arm-kernel
While booting two of my machines, I noticed this splat on the console.
Not sure if this is due to a RCU change or NFS change, so adding folk
on both sides.
===============================
[ INFO: suspicious RCU usage. ]
4.9.0-rc4+ #2050 Not tainted
-------------------------------
net/sunrpc/clnt.c:2773 suspicious rcu_dereference_check() usage!
other info that might help us debug this:
rcu_scheduler_active = 1, debug_locks = 0
1 lock held by mount.nfs/1499:
#0:
(
&(&nn->nfs_client_lock)->rlock
){+.+...}
, at:
[<c026c6d4>] nfs_get_client+0xe8/0x580
stack backtrace:
CPU: 1 PID: 1499 Comm: mount.nfs Not tainted 4.9.0-rc4+ #2050
Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
Backtrace:
[<c0013b84>] (dump_backtrace) from [<c0013dc4>] (show_stack+0x18/0x1c)
r6:60010013 r5:ffffffff r4:00000000 r3:00000000
[<c0013dac>] (show_stack) from [<c035d300>] (dump_stack+0xa4/0xdc)
[<c035d25c>] (dump_stack) from [<c008093c>] (lockdep_rcu_suspicious+0xbc/0x11c)
r6:00000ad5 r5:c08f4078 r4:ee58a4c0 r3:ee58a4c0
[<c0080880>] (lockdep_rcu_suspicious) from [<c0721ba4>] (rpc_clnt_xprt_switch_has_addr+0x134/0x15c)
r7:ef27dd80 r6:c0ae3972 r5:eeb5a110 r4:ee930400
[<c0721a70>] (rpc_clnt_xprt_switch_has_addr) from [<c026c814>] (nfs_get_client+0x228/0x580)
r6:ef27ddd0 r5:eeb5a110 r4:00000000
[<c026c5ec>] (nfs_get_client) from [<c026cc20>] (nfs_create_server+0xb4/0x3d4)
r10:edaebe6c r9:c0a84d54 r8:edaebd30 r7:edf7b480 r6:00000000 r5:ed50a000
r4:eeb5a000
[<c026cb6c>] (nfs_create_server) from [<c028a2f8>] (nfs3_create_server+0x10/0x28)
r10:00000000 r9:edbea200 r8:c0a84d54 r7:edaebe6c r6:00000000 r5:00000001
r4:eeb5a000
[<c028a2e8>] (nfs3_create_server) from [<c02792a0>] (nfs_try_mount+0x188/0x280)
r4:eeb5a000 r3:c028a2e8
[<c0279118>] (nfs_try_mount) from [<c027b28c>] (nfs_fs_mount+0x420/0x8c0)
r10:00000000 r9:00000400 r8:edbea200 r7:edbea200 r6:c0a84d54 r5:edbea206
r4:eeb5a000
[<c027ae6c>] (nfs_fs_mount) from [<c016eaf4>] (mount_fs+0x1c/0xa8)
r10:ed4f5000 r9:edbea340 r8:c0a82204 r7:c019091c r6:c0a82204 r5:edbea200
r4:ed8a3700
[<c016ead8>] (mount_fs) from [<c018d6d0>] (vfs_kern_mount+0x5c/0x138)
r6:00000000 r5:edbea200 r4:ed8a3700
[<c018d674>] (vfs_kern_mount) from [<c019091c>] (do_mount+0x164/0xc58)
r10:c0191774 r8:edbea200 r7:00000020 r6:ed4f5000 r5:c0a82204 r4:00000000
[<c01907b8>] (do_mount) from [<c0191774>] (SyS_mount+0x7c/0xa4)
r10:00000000 r9:edaea000 r8:0078c0f8 r7:00000000 r6:ed4f5000 r5:edbea200
r4:edbea340
[<c01916f8>] (SyS_mount) from [<c000fd60>] (ret_fast_syscall+0x0/0x1c)
r8:c000ff04 r7:00000015 r6:0078dd50 r5:bed2bad0 r4:007968a8
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* [PATCH] ARM: fix backtrace
From: Russell King @ 2016-11-15 15:01 UTC (permalink / raw)
To: linux-arm-kernel
Recent kernels have changed their behaviour to be more inconsistent
when handling printk continuations. With todays kernels, the output
looks sane on the console, but dmesg splits individual printk()s which
do not have the KERN_CONT prefix into separate lines.
Since the assembly code is not trivial to add the KERN_CONT, and we
ideally want to avoid using KERN_CONT (as multiple printk()s can race
between different threads), convert the assembly dumping the register
values to C code, and have the C code build the output a line at a
time before dumping to the console.
This avoids the KERN_CONT issue, and also avoids situations where the
output is intermixed with other console activity.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
Noticed due to a NFS bug in 4.9-rc.
arch/arm/kernel/traps.c | 20 ++++++++++++++++++++
arch/arm/lib/backtrace.S | 37 +++----------------------------------
2 files changed, 23 insertions(+), 34 deletions(-)
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 2771ba243f36..ff2ae872d555 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -75,6 +75,26 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long
dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
}
+void dump_backtrace_stm(u32 *stack, u32 instruction)
+{
+ char str[80], *p;
+ unsigned int x;
+ int reg;
+
+ for (reg = 10, x = 0, p = str; reg >= 0; reg--) {
+ if (instruction & BIT(reg)) {
+ p += sprintf(p, " r%d:%08x", reg, *stack--);
+ if (++x == 6) {
+ x = 0;
+ p = str;
+ printk("%s\n", str);
+ }
+ }
+ }
+ if (p != str)
+ printk("%s\n", str);
+}
+
#ifndef CONFIG_ARM_UNWIND
/*
* Stack pointers should always be within the kernels view of
diff --git a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S
index fab5a50503ae..7d7952e5a3b1 100644
--- a/arch/arm/lib/backtrace.S
+++ b/arch/arm/lib/backtrace.S
@@ -10,6 +10,7 @@
* 27/03/03 Ian Molton Clean up CONFIG_CPU
*
*/
+#include <linux/kern_levels.h>
#include <linux/linkage.h>
#include <asm/assembler.h>
.text
@@ -83,13 +84,13 @@ for_each_frame: tst frame, mask @ Check for address exceptions
teq r3, r1, lsr #11
ldreq r0, [frame, #-8] @ get sp
subeq r0, r0, #4 @ point at the last arg
- bleq .Ldumpstm @ dump saved registers
+ bleq dump_backtrace_stm @ dump saved registers
1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc}
ldr r3, .Ldsi @ instruction exists,
teq r3, r1, lsr #11
subeq r0, frame, #16
- bleq .Ldumpstm @ dump saved registers
+ bleq dump_backtrace_stm @ dump saved registers
teq sv_fp, #0 @ zero saved fp means
beq no_frame @ no further frames
@@ -112,38 +113,6 @@ ENDPROC(c_backtrace)
.long 1004b, 1006b
.popsection
-#define instr r4
-#define reg r5
-#define stack r6
-
-.Ldumpstm: stmfd sp!, {instr, reg, stack, r7, lr}
- mov stack, r0
- mov instr, r1
- mov reg, #10
- mov r7, #0
-1: mov r3, #1
- ARM( tst instr, r3, lsl reg )
- THUMB( lsl r3, reg )
- THUMB( tst instr, r3 )
- beq 2f
- add r7, r7, #1
- teq r7, #6
- moveq r7, #0
- adr r3, .Lcr
- addne r3, r3, #1 @ skip newline
- ldr r2, [stack], #-4
- mov r1, reg
- adr r0, .Lfp
- bl printk
-2: subs reg, reg, #1
- bpl 1b
- teq r7, #0
- adrne r0, .Lcr
- blne printk
- ldmfd sp!, {instr, reg, stack, r7, pc}
-
-.Lfp: .asciz " r%d:%08x%s"
-.Lcr: .asciz "\n"
.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n"
.align
.Ldsi: .word 0xe92dd800 >> 11 @ stmfd sp!, {... fp, ip, lr, pc}
--
2.7.4
^ permalink raw reply related
* [RFC v2 3/8] iommu/dma: Allow MSI-only cookies
From: Robin Murphy @ 2016-11-15 14:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0af2373f-db86-e93f-b3e1-01c3076ce6fc@redhat.com>
On 14/11/16 23:23, Auger Eric wrote:
> Hi Robin,
>
> On 14/11/2016 13:36, Robin Murphy wrote:
>> On 04/11/16 11:24, Eric Auger wrote:
>>> From: Robin Murphy <robin.murphy@arm.com>
>>>
>>> IOMMU domain users such as VFIO face a similar problem to DMA API ops
>>> with regard to mapping MSI messages in systems where the MSI write is
>>> subject to IOMMU translation. With the relevant infrastructure now in
>>> place for managed DMA domains, it's actually really simple for other
>>> users to piggyback off that and reap the benefits without giving up
>>> their own IOVA management, and without having to reinvent their own
>>> wheel in the MSI layer.
>>>
>>> Allow such users to opt into automatic MSI remapping by dedicating a
>>> region of their IOVA space to a managed cookie.
>>>
>>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>
>> OK, following the discussion elsewhere I've had a go at the less stupid,
>> but more involved, version. Thoughts?
>
> Conceptually I don't have any major objection with the minimalist
> allocation scheme all the more so it follows Joerg's guidance. Maybe the
> only thing is we do not check we don't overshoot the reserved msi-region.
Yes, I thought about that and came to the conclusion that it was hard to
justify the extra complexity. Since the caller has to calculate an
appropriate region size to reserve anyway, we might as well just trust
it to be correct. And if the caller did get things wrong, then one or
other iommu_map() is going to fail on the overlapping IOVAs anyway.
>
> Besides there are 2 issues reported below.
>
>>
>> Robin.
>>
>> ----->8-----
>> From: Robin Murphy <robin.murphy@arm.com>
>> Subject: [RFC PATCH] iommu/dma: Allow MSI-only cookies
>>
>> IOMMU domain users such as VFIO face a similar problem to DMA API ops
>> with regard to mapping MSI messages in systems where the MSI write is
>> subject to IOMMU translation. With the relevant infrastructure now in
>> place for managed DMA domains, it's actually really simple for other
>> users to piggyback off that and reap the benefits without giving up
>> their own IOVA management, and without having to reinvent their own
>> wheel in the MSI layer.
>>
>> Allow such users to opt into automatic MSI remapping by dedicating a
>> region of their IOVA space to a managed cookie, and extend the mapping
>> routine to implement a trivial linear allocator in such cases, to avoid
>> the needless overhead of a full-blown IOVA domain.
>>
>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>> ---
>> drivers/iommu/dma-iommu.c | 118 ++++++++++++++++++++++++++++++++++++----------
>> include/linux/dma-iommu.h | 6 +++
>> 2 files changed, 100 insertions(+), 24 deletions(-)
>>
>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
>> index c5ab8667e6f2..33d66a8273c6 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -37,10 +37,19 @@ struct iommu_dma_msi_page {
>> phys_addr_t phys;
>> };
>>
>> +enum iommu_dma_cookie_type {
>> + IOMMU_DMA_IOVA_COOKIE,
>> + IOMMU_DMA_MSI_COOKIE,
>> +};
>> +
>> struct iommu_dma_cookie {
>> - struct iova_domain iovad;
>> - struct list_head msi_page_list;
>> - spinlock_t msi_lock;
>> + union {
>> + struct iova_domain iovad;
>> + dma_addr_t msi_iova;
>> + };
>> + struct list_head msi_page_list;
>> + spinlock_t msi_lock;
>> + enum iommu_dma_cookie_type type;
>> };
>>
>> static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain)
>> @@ -53,6 +62,19 @@ int iommu_dma_init(void)
>> return iova_cache_get();
>> }
>>
>> +static struct iommu_dma_cookie *__cookie_alloc(enum iommu_dma_cookie_type type)
>> +{
>> + struct iommu_dma_cookie *cookie;
>> +
>> + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> + if (cookie) {
>> + spin_lock_init(&cookie->msi_lock);
>> + INIT_LIST_HEAD(&cookie->msi_page_list);
>> + cookie->type = type;
>> + }
>> + return cookie;
>> +}
>> +
>> /**
>> * iommu_get_dma_cookie - Acquire DMA-API resources for a domain
>> * @domain: IOMMU domain to prepare for DMA-API usage
>> @@ -62,25 +84,53 @@ int iommu_dma_init(void)
>> */
>> int iommu_get_dma_cookie(struct iommu_domain *domain)
>> {
>> - struct iommu_dma_cookie *cookie;
>> -
>> if (domain->iova_cookie)
>> return -EEXIST;
>>
>> - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> - if (!cookie)
>> + domain->iova_cookie = __cookie_alloc(IOMMU_DMA_IOVA_COOKIE);
>> + if (!domain->iova_cookie)
>> return -ENOMEM;
>>
>> - spin_lock_init(&cookie->msi_lock);
>> - INIT_LIST_HEAD(&cookie->msi_page_list);
>> - domain->iova_cookie = cookie;
>> return 0;
>> }
>> EXPORT_SYMBOL(iommu_get_dma_cookie);
>>
>> /**
>> + * iommu_get_msi_cookie - Acquire just MSI remapping resources
>> + * @domain: IOMMU domain to prepare
>> + * @base: Start address of IOVA region for MSI mappings
>> + *
>> + * Users who manage their own IOVA allocation and do not want DMA API support,
>> + * but would still like to take advantage of automatic MSI remapping, can use
>> + * this to initialise their own domain appropriately. Users should reserve a
>> + * contiguous IOVA region, starting at @base, large enough to accommodate the
>> + * number of PAGE_SIZE mappings necessary to cover every MSI doorbell address
>> + * used by the devices attached to @domain.
>> + */
>> +int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
>> +{
>> + struct iommu_dma_cookie *cookie;
>> +
>> + if (domain->type != IOMMU_DOMAIN_UNMANAGED)
>> + return -EINVAL;
>> +
>> + if (domain->iova_cookie)
>> + return -EEXIST;
>> +
>> + cookie = __cookie_alloc(IOMMU_DMA_IOVA_COOKIE);
> must be IOMMU_DMA_MSI_COOKIE else it has bad consequences.
Oops, quite right!
>> + if (!cookie)
>> + return -ENOMEM;
>> +
>> + cookie->msi_iova = base;
>> + domain->iova_cookie = cookie;
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(iommu_get_msi_cookie);
>> +
>> +/**
>> * iommu_put_dma_cookie - Release a domain's DMA mapping resources
>> - * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
>> + * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie() or
>> + * iommu_get_msi_cookie()
>> *
>> * IOMMU drivers should normally call this from their domain_free callback.
>> */
>> @@ -92,7 +142,7 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
>> if (!cookie)
>> return;
>>
>> - if (cookie->iovad.granule)
>> + if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule)
>> put_iova_domain(&cookie->iovad);
>>
>> list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
>> @@ -137,11 +187,12 @@ static void iova_reserve_pci_windows(struct pci_dev *dev,
>> int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
>> u64 size, struct device *dev)
>> {
>> - struct iova_domain *iovad = cookie_iovad(domain);
>> + struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> + struct iova_domain *iovad = &cookie->iovad;
>> unsigned long order, base_pfn, end_pfn;
>>
>> - if (!iovad)
>> - return -ENODEV;
>> + if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
>> + return -EINVAL;
>>
>> /* Use the smallest supported page size for IOVA granularity */
>> order = __ffs(domain->pgsize_bitmap);
>> @@ -644,11 +695,21 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
>> {
>> struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> struct iommu_dma_msi_page *msi_page;
>> - struct iova_domain *iovad = &cookie->iovad;
>> + struct iova_domain *iovad;
>> struct iova *iova;
>> int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> + size_t size;
>> +
>> + if (cookie->type == IOMMU_DMA_IOVA_COOKIE) {
>> + iovad = &cookie->iovad;
>> + size = iovad->granule;
>> + } else {
>> + iovad = NULL;
>> + size = PAGE_SIZE;
>> + }
>> +
>> + msi_addr &= ~(phys_addr_t)(size - 1);
>>
>> - msi_addr &= ~(phys_addr_t)iova_mask(iovad);
>> list_for_each_entry(msi_page, &cookie->msi_page_list, list)
>> if (msi_page->phys == msi_addr)
>> return msi_page;
>> @@ -657,13 +718,18 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
>> if (!msi_page)
>> return NULL;
>>
>> - iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
>> - if (!iova)
>> - goto out_free_page;
>> -
>> msi_page->phys = msi_addr;
>> - msi_page->iova = iova_dma_addr(iovad, iova);
>> - if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot))
>> + if (iovad) {
>> + iova = __alloc_iova(domain, size, dma_get_mask(dev));
>> + if (!iova)
>> + goto out_free_page;
>> + msi_page->iova = iova_dma_addr(iovad, iova);
>> + } else {
>> + msi_page->iova = cookie->msi_iova;
>> + cookie->msi_iova += size;
>> + }
>> +
>> + if (iommu_map(domain, msi_page->iova, msi_addr, size, prot))
>> goto out_free_iova;
>>
>> INIT_LIST_HEAD(&msi_page->list);
>> @@ -671,7 +737,10 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
>> return msi_page;
>>
>> out_free_iova:
>> - __free_iova(iovad, iova);
>> + if (iovad)
>> + __free_iova(iovad, iova);
>> + else
>> + cookie->msi_iova -= size;
>> out_free_page:
>> kfree(msi_page);
>> return NULL;
>> @@ -716,3 +785,4 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
>> msg->address_lo += lower_32_bits(msi_page->iova);
>> }
>> }
>
> in iommu_dma_map_msi_msg there is another issue at:
> msg->address_lo &= iova_mask(&cookie->iovad);
> iovad might not exist
Ah yes, I'd overlooked that one, thanks - seems compile-testing isn't
that magic bullet...
Completely factoring out the alloc/free seemed like overkill when all
the IOVA stuff seemed to be in the one function, but in light of this
I'll have another go and see if I can get it any tidier - the RFC was
primarily for the simplified interface and allocator. In the meantime I
think your fixed-up version looks correct.
> Thanks
>
> Eric
>
>> +
(now, this line I had at least already taken care of. Oh well)
Thanks,
Robin.
>> diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
>> index 32c589062bd9..d69932474576 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -27,6 +27,7 @@ int iommu_dma_init(void);
>>
>> /* Domain management interface for IOMMU drivers */
>> int iommu_get_dma_cookie(struct iommu_domain *domain);
>> +int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base);
>> void iommu_put_dma_cookie(struct iommu_domain *domain);
>>
>> /* Setup call for arch DMA mapping code */
>> @@ -82,6 +83,11 @@ static inline int iommu_get_dma_cookie(struct iommu_domain *domain)
>> return -ENODEV;
>> }
>>
>> +static inline int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
>> +{
>> + return -ENODEV;
>> +}
>> +
>> static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
>> {
>> }
>>
^ 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