* [PATCH v9 4/4] soc: mediatek: Add MT2701 scpsys driver
From: Matthias Brugger @ 2016-10-30 23:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476953798-23263-5-git-send-email-jamesjj.liao@mediatek.com>
On 10/20/2016 10:56 AM, James Liao wrote:
> From: Shunli Wang <shunli.wang@mediatek.com>
>
> Add scpsys driver for MT2701.
>
> mtk-scpsys now supports MT8173 (arm64) and MT2701 (arm). So it should
> be enabled on both arm64 and arm platforms.
>
> Signed-off-by: Shunli Wang <shunli.wang@mediatek.com>
> Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
> Reviewed-by: Kevin Hilman <khilman@baylibre.com>
> ---
Applied to v4.9-next/soc
^ permalink raw reply
* [PATCH v9 3/4] soc: mediatek: Add MT2701 power dt-bindings
From: Matthias Brugger @ 2016-10-30 23:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476953798-23263-4-git-send-email-jamesjj.liao@mediatek.com>
On 10/20/2016 10:56 AM, James Liao wrote:
> From: Shunli Wang <shunli.wang@mediatek.com>
>
> Add power dt-bindings for MT2701.
>
> Signed-off-by: Shunli Wang <shunli.wang@mediatek.com>
> Signed-off-by: James Liao <jamesjj.liao@mediatek.com>
> Acked-by: Rob Herring <robh@kernel.org>
> Reviewed-by: Kevin Hilman <khilman@baylibre.com>
Applied to v4.9-next/soc
^ permalink raw reply
* [PATCH v9 1/4] soc: mediatek: Refine scpsys to support multiple platform
From: Matthias Brugger @ 2016-10-30 23:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477647408.24014.6.camel@mtksdaap41>
On 10/28/2016 11:36 AM, James Liao wrote:
> Hi Matthias,
>
> Sorry for late reply due to our email service.
>
> On Tue, 2016-10-25 at 16:04 +0200, Matthias Brugger wrote:
>> Hi James,
>>
>> On 10/20/2016 10:56 AM, James Liao wrote:
>>> -static int scpsys_probe(struct platform_device *pdev)
>>> +static void init_clks(struct platform_device *pdev, struct clk *clk[CLK_MAX])
>>
>> I prefer struct clk **clk.
>
> Okay.
>
>>> +{
>>> + int i;
>>> +
>>> + for (i = CLK_NONE + 1; i < CLK_MAX; i++)
>>> + clk[i] = devm_clk_get(&pdev->dev, clk_names[i]);
>>> +}
>>> +
>>> +static struct scp *init_scp(struct platform_device *pdev,
>>> + const struct scp_domain_data *scp_domain_data, int num)
>>> {
>>> struct genpd_onecell_data *pd_data;
>>> struct resource *res;
>>> - int i, j, ret;
>>> + int i, j;
>>> struct scp *scp;
>>> - struct clk *clk[MT8173_CLK_MAX];
>>> + struct clk *clk[CLK_MAX];
>>
>> should be *[CLK_MAX - 1] but I would prefer to define in the enum:
>> CLK_MAX = CLK_VENC_LT,
>
> After init_clks() the clk[] will have valid contents between
> clk[1]..clk[CLK_MAX-1], so it's necessary to declare clk[] with CLK_MAX
> elements.
>
>> If you are ok with it, I can fix both of my comments when applying.
>
> Yes. struct clk **clk can be applied directly. But I think clk[CLK_MAX]
> should be kept in current implementation.
>
Ok, we won't never use clk[0] but it's ok for now.
Applied to v4.9-next/soc
>
> Best regards,
>
> James
>
>
^ permalink raw reply
* [PATCH v7 REPOST 8/9] arm: add sysfs cpu_capacity attribute
From: Russell King - ARM Linux @ 2016-10-30 20:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-9-juri.lelli@arm.com>
On Mon, Oct 17, 2016 at 04:46:49PM +0100, Juri Lelli wrote:
> +#ifdef CONFIG_PROC_SYSCTL
> +#include <asm/cpu.h>
> +#include <linux/string.h>
Include files at the top of the file please. No need to ifdef them.
They're sorted alphabetically, so new additions should be alphabetical.
(That's a general rule - if something is already alphabetical, do not
make it non-alphabetical.)
> +static ssize_t show_cpu_capacity(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct cpu *cpu = container_of(dev, struct cpu, dev);
> + ssize_t rc;
> + int cpunum = cpu->dev.id;
> + unsigned long capacity = arch_scale_cpu_capacity(NULL, cpunum);
> +
> + rc = sprintf(buf, "%lu\n", capacity);
> +
> + return rc;
Way too many lines for such a simple function. This can be simplified
to just:
struct cpu *cpu = container_of(dev, struct cpu, dev);
return sprintf(buf, "%lu\n", arch_scale_cpu_capacity(NULL, cpu->dev.id);
If you don't like the last line ending on column 79, then feel free to
break it across two lines after the format string.
> +}
> +
> +static ssize_t store_cpu_capacity(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t count)
> +{
> + struct cpu *cpu = container_of(dev, struct cpu, dev);
> + int this_cpu = cpu->dev.id, i;
> + unsigned long new_capacity;
> + ssize_t ret;
> +
> + if (count) {
> + char *p = (char *) buf;
> +
> + ret = kstrtoul(p, 0, &new_capacity);
Unnecessary cast - kstrtoul takes a const char pointer, and in any case
it's really bad form to cast away the "const-ness" of any pointer. So,
just:
if (count) {
ret = kstrtoul(buf, 0, &new_capacity);
should work just fine.
> + if (ret)
> + return ret;
> + if (new_capacity > SCHED_CAPACITY_SCALE)
> + return -EINVAL;
> +
> + mutex_lock(&cpu_scale_mutex);
> + for_each_cpu(i, &cpu_topology[this_cpu].core_sibling)
> + set_capacity_scale(i, new_capacity);
> + mutex_unlock(&cpu_scale_mutex);
> + }
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR(cpu_capacity,
> + 0644,
> + show_cpu_capacity,
> + store_cpu_capacity);
There's a move to use the named DEVICE_ATTR_RW() for this kind of thing,
it'll want the functions named xxx_show() and xxx_store(). I see
there's some recent patches to do this conversion across the kernel, so
this should probably be done before submission.
> +
> +static int register_cpu_capacity_sysctl(void)
> +{
> + int i;
> + struct device *cpu;
> +
> + for_each_possible_cpu(i) {
> + cpu = get_cpu_device(i);
> + if (!cpu) {
> + pr_err("%s: too early to get CPU%d device!\n",
> + __func__, i);
> + continue;
> + }
> + device_create_file(cpu, &dev_attr_cpu_capacity);
> + }
> +
> + return 0;
> +}
> +late_initcall(register_cpu_capacity_sysctl);
Hmm, this is really weird. topology_init() in arch/arm/kernel/setup.c
is where these devices get created, and they're created at
subsys_initcall() time. By that point, the list of possible CPUs has
to be static, it's not going to change. I don't see why this has to be
done at late_initcall() - and since topology.c will be linked after
setup.c, I don't see why it shouldn't be at subsys_initcall() level to
follow on after topology_init().
--
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/RFT v2 09/17] regulator: fixed: Add over current event
From: Rob Herring @ 2016-10-30 20:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161024164634.4330-10-ahaslam@baylibre.com>
On Mon, Oct 24, 2016 at 06:46:26PM +0200, ahaslam at baylibre.com wrote:
> From: Axel Haslam <ahaslam@baylibre.com>
>
> Some regulator supplies have an over-current pin that is
> activated when the hw detects an over current condition.
> When this happens, the hardware enters a current limited
> mode.
>
> Extend the fixed regulator driver with the ability
> to handle irq's from the over-current pin and report
> an over current event to the consumers via a regulator
> notifier. Also, add device tree bindings to allow to
> pass a gpio for over current monitoring.
>
> Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
> ---
> .../bindings/regulator/fixed-regulator.txt | 4 ++
> drivers/regulator/fixed.c | 64 ++++++++++++++++++++++
> include/linux/regulator/consumer.h | 5 ++
> include/linux/regulator/fixed.h | 3 +
> 4 files changed, 76 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
> index 4fae41d..d20bf67 100644
> --- a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
> +++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
> @@ -11,6 +11,8 @@ If this property is missing, the default assumed is Active low.
> - gpio-open-drain: GPIO is open drain type.
> If this property is missing then default assumption is false.
> -vin-supply: Input supply name.
> +- oc-gpio: Input gpio that signals an over current condition
"-gpios" is the preferred form. So "oc-gpios".
> +- oc-active-high: The polarity of the over current pin is high
This should be specified in the gpio flags cell.
Rob
^ permalink raw reply
* [PATCH 5/5] irqchip: st: Remove obsolete platforms from dt binding doc
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477065443-10668-6-git-send-email-patrice.chotard@st.com>
On Fri, Oct 21, 2016 at 05:57:23PM +0200, patrice.chotard at st.com wrote:
> From: Patrice Chotard <patrice.chotard@st.com>
>
> STiH415/6 SoC support is being removed from the kernel.
> This patch updates the sti irchip and removes
> references to these obsolete platforms.
>
> Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
> Cc: <tglx@linutronix.de>
> Cc: <jason@lakedaemon.net>
> Cc: <marc.zyngier@arm.com>
> ---
> .../devicetree/bindings/interrupt-controller/st,sti-irq-syscfg.txt | 6 ++----
> 1 file changed, 2 insertions(+), 4 deletions(-)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [RFC PATCH 01/13] pinctrl: meson: Add GXL pinctrl definitions
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477060838-14164-2-git-send-email-narmstrong@baylibre.com>
On Fri, Oct 21, 2016 at 04:40:26PM +0200, Neil Armstrong wrote:
> Add support for the Amlogic Meson GXL SoC, this is a partially complete
> definition only based on the Amlogic Vendor tree.
>
> This definition differs a lot from the GXBB and needs a separate entry.
>
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> .../devicetree/bindings/pinctrl/meson,pinctrl.txt | 2 +
Acked-by: Rob Herring <robh@kernel.org>
> drivers/pinctrl/meson/Makefile | 3 +-
> drivers/pinctrl/meson/pinctrl-meson-gxl.c | 589 +++++++++++++++++++++
> drivers/pinctrl/meson/pinctrl-meson.c | 8 +
> drivers/pinctrl/meson/pinctrl-meson.h | 2 +
> include/dt-bindings/gpio/meson-gxl-gpio.h | 131 +++++
> 6 files changed, 734 insertions(+), 1 deletion(-)
> create mode 100644 drivers/pinctrl/meson/pinctrl-meson-gxl.c
> create mode 100644 include/dt-bindings/gpio/meson-gxl-gpio.h
^ permalink raw reply
* [PATCH] dt-bindings: video: exynos7-decon: Remove obsolete samsung,power-domain property
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477058754-13866-1-git-send-email-krzk@kernel.org>
On Fri, Oct 21, 2016 at 05:05:54PM +0300, Krzysztof Kozlowski wrote:
> The samsung,power-domain property is obsolete since commit 0da658704136
> ("ARM: dts: convert to generic power domain bindings for exynos DT").
> Replace it with generic one.
>
> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
> ---
> Documentation/devicetree/bindings/display/exynos/exynos7-decon.txt | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
You didn't send this To me, so I assume someone else is applying it.
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH v4 01/23] reset: Add renesas,rst DT bindings
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477055857-17936-2-git-send-email-geert+renesas@glider.be>
On Fri, Oct 21, 2016 at 03:17:15PM +0200, Geert Uytterhoeven wrote:
> Add DT bindings for the Renesas R-Car Reset Controller (R-Car Gen1
> RESET/WDT and R-Car Gen2/Gen3 and RZ/G RST).
>
> As the features provided by the hardware module differ a lot across the
> various SoC families and members, only SoC-specific compatible values
> are defined.
>
> For now we use the RST only for providing access to the state of the
> mode pins, which is needed by the clock driver.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Acked-by: Magnus Damm <damm+renesas@opensource.se>
> Acked-by: Dirk Behme <dirk.behme@de.bosch.com>
> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> v4:
> - Add Acked-by,
> - Fix comma and period in list,
> - Add RZ/G1M and RZ/G1E,
>
> v3:
> - Clarify current usage,
> - Use "renesas,<soctype>-rst" instead of "renesas,rst-<soctype>",
> - Drop "syscon" compatible value,
> - Add R-Car M3-W,
> - Add R-Car Gen1,
>
> v2:
> - Add Acked-by.
> ---
> .../devicetree/bindings/reset/renesas,rst.txt | 37 ++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/reset/renesas,rst.txt
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH 1/3] Documentation: dt: Add TI SCI clock driver
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477053961-27128-2-git-send-email-t-kristo@ti.com>
On Fri, Oct 21, 2016 at 03:45:59PM +0300, Tero Kristo wrote:
> Add a clock implementation, TI SCI clock, that will hook to the common
> clock framework, and allow each clock to be controlled via TI SCI
> protocol.
>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> .../devicetree/bindings/clock/ti,sci-clk.txt | 37 ++++++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 38 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/ti,sci-clk.txt
>
> diff --git a/Documentation/devicetree/bindings/clock/ti,sci-clk.txt b/Documentation/devicetree/bindings/clock/ti,sci-clk.txt
> new file mode 100644
> index 0000000..bfc3ca4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/ti,sci-clk.txt
> @@ -0,0 +1,37 @@
> +Texas Instruments TI-SCI Clocks
> +===============================
> +
> +All clocks on Texas Instruments' SoCs that contain a System Controller,
> +are only controlled by this entity. Communication between a host processor
> +running an OS and the System Controller happens through a protocol known
> +as TI-SCI[1]. This clock implementation plugs into the common clock
> +framework and makes use of the TI-SCI protocol on clock API requests.
> +
> +[1] Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
> +
> +Required properties:
> +-------------------
> +- compatible: Must be "ti,k2g-sci-clk"
> +- #clock-cells: Shall be 2.
> + In clock consumers, this cell represents the device ID and clock ID
> + exposed by the PM firmware. The assignments can be found in the header
> + files <dt-bindings/genpd/<soc>.h> (which covers the device IDs) and
> + <dt-bindings/clock/<soc>.h> (which covers the clock IDs), where <soc>
> + is the SoC involved, for example 'k2g'.
> +
> +Examples:
> +--------
> +
> +pmmc: pmmc {
> + compatible = "ti,k2g-sci";
> +
> + k2g_clks: k2g_clks {
Use "clocks" for node name instead.
> + compatible = "ti,k2g-sci-clk";
I'm starting to think all these child nodes for SCI are pointless. Is
there any reason why the parent node can't be the clock provider (along
with all the other providers it acks as)?
> + #clock-cells = <2>;
> + };
> +};
> +
> +uart0: serial at 2530c00 {
> + compatible = "ns16550a";
> + clocks = <&k2g_clks K2G_DEV_UART0 0>;
> +};
^ permalink raw reply
* [PATCH v2 4/9] regulator: lp873x: Add support for populating input supply
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161021103841.8044-5-lokeshvutla@ti.com>
On Fri, Oct 21, 2016 at 04:08:36PM +0530, Lokesh Vutla wrote:
> In order to have a proper topology of regulators for a platform, each
> registering regulator needs to populate supply_name field for identifying
> its supply's name. Add supply_name field for lp873x regulators.
>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Keerthy <j-keerthy@ti.com>
> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
> ---
> Documentation/devicetree/bindings/mfd/lp873x.txt | 8 ++++++++
> drivers/regulator/lp873x-regulator.c | 1 +
> 2 files changed, 9 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mfd/lp873x.txt b/Documentation/devicetree/bindings/mfd/lp873x.txt
> index 52766c2..998837a 100644
> --- a/Documentation/devicetree/bindings/mfd/lp873x.txt
> +++ b/Documentation/devicetree/bindings/mfd/lp873x.txt
> @@ -7,6 +7,9 @@ Required properties:
> - #gpio-cells: Should be two. The first cell is the pin number and
> the second cell is used to specify flags.
> See ../gpio/gpio.txt for more information.
> + - xxx-in-supply: Phandle to parent supply node of each regulator
> + populated under regulators node. xxx should match
> + the supply_name populated in driver.
The driver is irrelevant. This should reference a list in this document.
> - regulators: List of child nodes that specify the regulator
> initialization data.
> Example:
> @@ -17,6 +20,11 @@ pmic: lp8733 at 60 {
> gpio-controller;
> #gpio-cells = <2>;
>
> + buck0-in-supply = <&vsys_3v3>;
> + buck1-in-supply = <&vsys_3v3>;
> + ldo0-in-supply = <&vsys_3v3>;
> + ldo1-in-supply = <&vsys_3v3>;
> +
> regulators {
> lp8733_buck0: buck0 {
> regulator-name = "lp8733-buck0";
^ permalink raw reply
* [PATCH] net: stmmac: Add OXNAS Glue Driver
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161021084445.24989-1-narmstrong@baylibre.com>
On Fri, Oct 21, 2016 at 10:44:45AM +0200, Neil Armstrong wrote:
> Add Synopsys Designware MAC Glue layer for the Oxford Semiconductor OX820.
>
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> .../devicetree/bindings/net/oxnas-dwmac.txt | 44 +++++
It's preferred that bindings are a separate patch.
> drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 ++
> drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
> drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c | 219 +++++++++++++++++++++
> 4 files changed, 275 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/oxnas-dwmac.txt
> create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c
>
> Changes since RFC at https://patchwork.kernel.org/patch/9387257 :
> - Drop init/exit callbacks
> - Implement proper remove and PM callback
> - Call init from probe
> - Disable/Unprepare clock if stmmac probe fails
>
> diff --git a/Documentation/devicetree/bindings/net/oxnas-dwmac.txt b/Documentation/devicetree/bindings/net/oxnas-dwmac.txt
> new file mode 100644
> index 0000000..5d2696c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/oxnas-dwmac.txt
> @@ -0,0 +1,44 @@
> +* Oxford Semiconductor OXNAS DWMAC Ethernet controller
> +
> +The device inherits all the properties of the dwmac/stmmac devices
> +described in the file stmmac.txt in the current directory with the
> +following changes.
> +
> +Required properties on all platforms:
> +
> +- compatible: Depending on the platform this should be one of:
> + - "oxsemi,ox820-dwmac"
> + Additionally "snps,dwmac" and any applicable more
> + detailed version number described in net/stmmac.txt
> + should be used.
You should be explicit what version applies to ox820. "snps,dwmac"
should probably be deprecated IMO. There are so many variations of DW
h/w.
> +
> +- reg: The first register range should be the one of the DWMAC
> + controller.
This is worded like there's a 2nd range?
> +
> +- clocks: Should contain phandles to the following clocks
> +- clock-names: Should contain the following:
> + - "stmmaceth" - see stmmac.txt
> + - "gmac" - peripheral gate clock
> +
> +- oxsemi,sys-ctrl: a phandle to the system controller syscon node
> +
> +Example :
> +
> +etha: ethernet at 40400000 {
> + compatible = "oxsemi,ox820-dwmac", "snps,dwmac";
> + reg = <0x40400000 0x2000>;
> + interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-names = "macirq", "eth_wake_irq";
> + mac-address = [000000000000]; /* Filled in by U-Boot */
> + phy-mode = "rgmii";
> +
> + clocks = <&stdclk CLK_820_ETHA>, <&gmacclk>;
> + clock-names = "gmac", "stmmaceth";
> + resets = <&reset RESET_MAC>;
> +
> + /* Regmap for sys registers */
> + oxsemi,sys-ctrl = <&sys>;
> +
> + status = "disabled";
> +};
^ permalink raw reply
* [PATCH v5 3/7] drm: sunxi: add DE2 HDMI support
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <36bd5454897c8ab77749e0294e4a4ecc2450dd12.1477142934.git.moinejf@free.fr>
On Fri, Oct 21, 2016 at 10:08:06AM +0200, Jean-Francois Moine wrote:
> This patch adds a HDMI driver to the DE2 based Allwinner's SoCs
> as A83T and H3.
> Audio and video are supported.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> .../devicetree/bindings/display/sunxi/hdmi.txt | 52 ++
> drivers/gpu/drm/sunxi/Kconfig | 8 +
> drivers/gpu/drm/sunxi/Makefile | 2 +
> drivers/gpu/drm/sunxi/de2_hdmi.c | 396 +++++++++
> drivers/gpu/drm/sunxi/de2_hdmi.h | 40 +
> drivers/gpu/drm/sunxi/de2_hdmi_io.c | 927 +++++++++++++++++++++
> drivers/gpu/drm/sunxi/de2_hdmi_io.h | 25 +
> 7 files changed, 1450 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.h
>
> diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> new file mode 100644
> index 0000000..0558c07
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> @@ -0,0 +1,52 @@
> +Allwinner HDMI Transmitter
> +==========================
> +
> +The Allwinner HDMI transmitters are included in the SoCs.
> +They support audio and video.
> +
> +Required properties:
> + - #address-cells : should be <1>
> + - #size-cells : should be <0>
> + - compatible : should be
> + "allwinner,sun8i-a83t-hdmi" or
> + "allwinner,sun8i-h3-hdmi"
> + - clocks : phandles to the HDMI clocks as described in
> + Documentation/devicetree/bindings/clock/clock-bindings.txt
> + - clock-names : must be
> + "gate" : bus gate
> + "clock" : streaming clock
> + "ddc-clock" : DDC clock
> + - resets : One or two phandles to the HDMI resets
> + - reset-names : must be
> + "hdmi0" and "hdmi1"
> +
> +Required nodes:
> + - port: Audio and video input port nodes with endpoint definitions
> + as defined in Documentation/devicetree/bindings/graph.txt.
Please define which port number is audio and which is video.
Rob
^ permalink raw reply
* [PATCH v5 1/7] drm: sunxi: Add a basic DRM driver for Allwinner DE2
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8afc5e020c5767face34fe3a9ab300ce9e67ba00.1477142934.git.moinejf@free.fr>
On Fri, Oct 21, 2016 at 09:26:18AM +0200, Jean-Francois Moine wrote:
> Allwinner's recent SoCs, as A64, A83T and H3, contain a new display
> engine, DE2.
> This patch adds a DRM video driver for this device.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> .../bindings/display/sunxi/sunxi-de2.txt | 83 +++
> drivers/gpu/drm/Kconfig | 2 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/sunxi/Kconfig | 21 +
> drivers/gpu/drm/sunxi/Makefile | 7 +
> drivers/gpu/drm/sunxi/de2_crtc.c | 475 +++++++++++++++++
> drivers/gpu/drm/sunxi/de2_crtc.h | 63 +++
> drivers/gpu/drm/sunxi/de2_de.c | 591 +++++++++++++++++++++
> drivers/gpu/drm/sunxi/de2_drm.h | 47 ++
> drivers/gpu/drm/sunxi/de2_drv.c | 378 +++++++++++++
> drivers/gpu/drm/sunxi/de2_plane.c | 119 +++++
> 11 files changed, 1787 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> create mode 100644 drivers/gpu/drm/sunxi/Kconfig
> create mode 100644 drivers/gpu/drm/sunxi/Makefile
> create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_de.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_drm.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_drv.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_plane.c
>
> diff --git a/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> new file mode 100644
> index 0000000..f9cd67a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> + hdmi: hdmi at 01ee0000 {
> + ...
> + #address-cells = <1>;
> + #size-cells = <0>;
> + port {
> + type = "video";
This is proposed, but not an accepted property. Please drop.
> + hdmi_ep: endpoint {
> + remote-endpoint = <&lcd0_ep>;
> + };
> + };
> + };
^ permalink raw reply
* [PATCH v5 1/7] drm: sunxi: Add a basic DRM driver for Allwinner DE2
From: Rob Herring @ 2016-10-30 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025161441.6b248efe9229bd80e3f7a33c@free.fr>
On Tue, Oct 25, 2016 at 04:14:41PM +0200, Jean-Francois Moine wrote:
> On Mon, 24 Oct 2016 16:04:19 +0200
> Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
>
> > Hi,
>
> Hi Maxime,
>
> > On Fri, Oct 21, 2016 at 09:26:18AM +0200, Jean-Francois Moine wrote:
> > > Allwinner's recent SoCs, as A64, A83T and H3, contain a new display
> > > engine, DE2.
> > > This patch adds a DRM video driver for this device.
> > >
> > > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> >
> > Output from checkpatch:
> > total: 0 errors, 20 warnings, 83 checks, 1799 lines checked
> >
> > > ---
> > > .../bindings/display/sunxi/sunxi-de2.txt | 83 +++
> > > drivers/gpu/drm/Kconfig | 2 +
> > > drivers/gpu/drm/Makefile | 1 +
> > > drivers/gpu/drm/sunxi/Kconfig | 21 +
> > > drivers/gpu/drm/sunxi/Makefile | 7 +
> > > drivers/gpu/drm/sunxi/de2_crtc.c | 475 +++++++++++++++++
> > > drivers/gpu/drm/sunxi/de2_crtc.h | 63 +++
> > > drivers/gpu/drm/sunxi/de2_de.c | 591 +++++++++++++++++++++
> > > drivers/gpu/drm/sunxi/de2_drm.h | 47 ++
> > > drivers/gpu/drm/sunxi/de2_drv.c | 378 +++++++++++++
> > > drivers/gpu/drm/sunxi/de2_plane.c | 119 +++++
> > > 11 files changed, 1787 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> > > create mode 100644 drivers/gpu/drm/sunxi/Kconfig
> > > create mode 100644 drivers/gpu/drm/sunxi/Makefile
> > > create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.c
> > > create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.h
> > > create mode 100644 drivers/gpu/drm/sunxi/de2_de.c
> > > create mode 100644 drivers/gpu/drm/sunxi/de2_drm.h
> > > create mode 100644 drivers/gpu/drm/sunxi/de2_drv.c
> > > create mode 100644 drivers/gpu/drm/sunxi/de2_plane.c
> > >
> > > diff --git a/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> > > new file mode 100644
> > > index 0000000..f9cd67a
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
[...]
> > > +
> > > +- resets: phandle to the reset of the device
> > > +
> > > +- ports: phandle's to the LCD ports
> >
> > Please use the OF graph.
>
> These ports are references to the graph of nodes. See
> http://www.kernelhub.org/?msg=911825&p=2
I think what Maxime means is describe the DE to LCD connection with OF
graph, not just a phandle.
Rob
^ permalink raw reply
* [PATCH 1/2] ABI: rtc-ab8500: fix rtc_calibration documentation
From: Alexandre Belloni @ 2016-10-30 20:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <364a36215c44e0c2785911e9d9259cd866283cb9.1477735797.git.mchehab@s-opensource.com>
On 29/10/2016 at 08:10:02 -0200, Mauro Carvalho Chehab wrote :
> The "What:" field at the ABI should describe the location of
> the ABI, e. g. the position under a mounted sysfs.
>
> Fix it.
>
> Cc: Mark Godfrey <mark.godfrey@stericsson.com>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: Alessandro Zummo <a.zummo@towertech.it>
> Cc: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: rtc-linux at googlegroups.com
> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
> ---
> .../ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
Applied, thanks.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [RFC] fpga: Pull checks for supported operations into framework
From: Moritz Fischer @ 2016-10-30 18:12 UTC (permalink / raw)
To: linux-arm-kernel
Most of the drivers only support a subset of {PARTIAL, FULL}
reconfiguration.
Pull duplicate checks in each driver into the framework.
Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
Cc: Alan Tull <atull@opensource.altera.com>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: S?ren Brinkmann <soren.brinkmann@xilinx.com>
Cc: linux-kernel at vger.kernel.org
Cc: linux-arm-kernel at lists.infradead.org
---
Hi all,
with the new drivers (ice40, altera-ps-spi) being submitted I've noticed
we're duplicating this check over and over again,
so I figured we might as well pull it into the framework.
I'm not sure if there are gonna be other 'flags' we need to support
in the short term (we talked about byte-swapping ...)
Note: This patch goes on top of greg's char-misc-testing that already
contains Alan's latest changes to support the A10 and won't apply
to master.
Cheers,
Moritz
---
drivers/fpga/fpga-mgr.c | 12 ++++++++++++
drivers/fpga/socfpga-a10.c | 4 +++-
drivers/fpga/socfpga.c | 3 ++-
drivers/fpga/zynq-fpga.c | 3 ++-
include/linux/fpga/fpga-mgr.h | 9 +++++++--
5 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index c58b4c4..85f17d8 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -49,6 +49,11 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info,
struct device *dev = &mgr->dev;
int ret;
+ if (!(mgr->supported_flags & info->flags)) {
+ dev_err(dev, "Unsupported flags passed\n");
+ return -ENOTSUPP;
+ }
+
/*
* Call the low level driver's write_init function. This will do the
* device-specific things to get the FPGA into the state where it is
@@ -252,6 +257,7 @@ EXPORT_SYMBOL_GPL(fpga_mgr_put);
*/
int fpga_mgr_register(struct device *dev, const char *name,
const struct fpga_manager_ops *mops,
+ u32 supported_flags,
void *priv)
{
struct fpga_manager *mgr;
@@ -268,6 +274,11 @@ int fpga_mgr_register(struct device *dev, const char *name,
return -EINVAL;
}
+ if (!supported_flags) {
+ dev_err(dev, "Attempt to register with no supported flags\n");
+ return -EINVAL;
+ }
+
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return -ENOMEM;
@@ -282,6 +293,7 @@ int fpga_mgr_register(struct device *dev, const char *name,
mgr->name = name;
mgr->mops = mops;
+ mgr->supported_flags = supported_flags;
mgr->priv = priv;
/*
diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
index ccd9fb2..e7c82ff 100644
--- a/drivers/fpga/socfpga-a10.c
+++ b/drivers/fpga/socfpga-a10.c
@@ -519,7 +519,9 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
}
return fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
- &socfpga_a10_fpga_mgr_ops, priv);
+ &socfpga_a10_fpga_mgr_ops,
+ FPGA_MGR_PARTIAL_RECONFIG,
+ priv);
}
static int socfpga_a10_fpga_remove(struct platform_device *pdev)
diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
index b6672e6..3285b7d 100644
--- a/drivers/fpga/socfpga.c
+++ b/drivers/fpga/socfpga.c
@@ -582,7 +582,8 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
return ret;
return fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
- &socfpga_fpga_ops, priv);
+ &socfpga_fpga_ops, FPGA_MGR_FULL_RECONFIG,
+ priv);
}
static int socfpga_fpga_remove(struct platform_device *pdev)
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 249682e..1dabd25 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -413,6 +413,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
struct zynq_fpga_priv *priv;
struct resource *res;
int err;
+ u32 flags = FPGA_MGR_FULL_RECONFIG | FPGA_MGR_PARTIAL_RECONFIG;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -465,7 +466,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
clk_disable(priv->clk);
err = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
- &zynq_fpga_ops, priv);
+ &zynq_fpga_ops, flags, priv);
if (err) {
dev_err(dev, "unable to register FPGA manager");
clk_unprepare(priv->clk);
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 040b86d..4f4bcf2 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -64,9 +64,12 @@ enum fpga_mgr_states {
/*
* FPGA Manager flags
+ * FPGA_MGR_FULL_RECONFIG: do full reconfiguration if supported
* FPGA_MGR_PARTIAL_RECONFIG: do partial reconfiguration if supported
*/
-#define FPGA_MGR_PARTIAL_RECONFIG BIT(0)
+#define FPGA_MGR_FULL_RECONFIG BIT(0)
+#define FPGA_MGR_PARTIAL_RECONFIG BIT(1)
+#define FPGA_MGR_EXTERNAL_CONFIG BIT(2)
/**
* struct fpga_image_info - information specific to a FPGA image
@@ -119,6 +122,7 @@ struct fpga_manager {
enum fpga_mgr_states state;
const struct fpga_manager_ops *mops;
void *priv;
+ u32 supported_flags;
};
#define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
@@ -135,7 +139,8 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node);
void fpga_mgr_put(struct fpga_manager *mgr);
int fpga_mgr_register(struct device *dev, const char *name,
- const struct fpga_manager_ops *mops, void *priv);
+ const struct fpga_manager_ops *mops, u32 supported_flags,
+ void *priv);
void fpga_mgr_unregister(struct device *dev);
--
2.4.11
^ permalink raw reply related
* [PATCH V2 2/2] ARM: dts: bcm283x: fix typo in mailbox address
From: Stefan Wahren @ 2016-10-30 17:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <6b13e96d-78d1-fcab-4273-803310ae4da6@suse.de>
The address of the mailbox node in the bcm283x.dtsi also has a typo.
So fix it accordingly.
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
Reviewed-by: Andreas F?rber <afaerber@suse.de>
Fixes: 05b682b7a3b2 ("ARM: bcm2835: dt: Add the mailbox to the device tree")
---
Changes in V2:
* fixed commit message as reported by Andreas
arch/arm/boot/dts/bcm283x.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
index 46d46d8..74dd21b 100644
--- a/arch/arm/boot/dts/bcm283x.dtsi
+++ b/arch/arm/boot/dts/bcm283x.dtsi
@@ -104,7 +104,7 @@
reg = <0x7e104000 0x10>;
};
- mailbox: mailbox at 7e00b800 {
+ mailbox: mailbox at 7e00b880 {
compatible = "brcm,bcm2835-mbox";
reg = <0x7e00b880 0x40>;
interrupts = <0 1>;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/4] mfd: ti_am335x_tscadc: store physical address
From: Jonathan Cameron @ 2016-10-30 17:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161026121730.GS8574@dell>
On 26/10/16 13:17, Lee Jones wrote:
> On Fri, 30 Sep 2016, Mugunthan V N wrote:
>
>> On Wednesday 28 September 2016 01:10 AM, Lee Jones wrote:
>>> On Wed, 21 Sep 2016, Mugunthan V N wrote:
>>>
>>>> store the physical address of the device in its priv to use it
>>>> for DMA addressing in the client drivers.
>>>>
>>>> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
>>>> ---
>>>> drivers/mfd/ti_am335x_tscadc.c | 1 +
>>>> include/linux/mfd/ti_am335x_tscadc.h | 1 +
>>>> 2 files changed, 2 insertions(+)
>>>>
>>>> diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
>>>> index c8f027b..0f3fab4 100644
>>>> --- a/drivers/mfd/ti_am335x_tscadc.c
>>>> +++ b/drivers/mfd/ti_am335x_tscadc.c
>>>> @@ -183,6 +183,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
>>>> tscadc->irq = err;
>>>>
>>>> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> + tscadc->tscadc_phys_base = res->start;
>>>
>>> This is unusual. Can't you use a virt_to_phys() variant instead?
>>>
>>
>> I tried using virt_to_phys(), but its not working for me.
>> Also saw many drivers uses like this to get physical address
>> ("git grep -n " res->start;" drivers/*").
>
> Very well:
>
> For my own reference:
> Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
>
> Let me know how you wish this set to be handled.
I'm happy to pick up the whole series. There are some more mfd
header changes in patch 2 but as they only add defines, I
don't mind that much if I don't an Ack from you on those
(btw this got to V3 but as patch 1 didn't change I'll carry
your ack forwards).
Do you want an immutable branch? Seems unlikely to cause
much trouble even if there is a merge issue on all 10ish
lines of mfd code in the next merge window.
Jonathan
>
^ permalink raw reply
* [PATCH v6 2/2] iio: adc: add support for Allwinner SoCs ADC
From: Jonathan Cameron @ 2016-10-30 16:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473943444-23979-3-git-send-email-quentin.schulz@free-electrons.com>
On 15/09/16 13:44, Quentin Schulz wrote:
> The Allwinner SoCs all have an ADC that can also act as a touchscreen
> controller and a thermal sensor. This patch adds the ADC driver which is
> based on the MFD for the same SoCs ADC.
>
> This also registers the thermal adc channel in the iio map array so
> iio_hwmon could use it without modifying the Device Tree. This registers
> the driver in the thermal framework.
>
> This driver probes on three different platform_device_id to take into
> account slight differences (registers bit and temperature computation)
> between Allwinner SoCs ADCs.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
Hi Lee,
As you applied the MFD part of this series, could you pick this up as well?
Thanks,
Jonathan
> ---
>
> v6:
> - remove useless member (regs) from sun4i_gpadc_dev structure,
> - rename sun4i_gpadc_dev structure to sun4i_gpadc_iio,
> - remove regmap_update_bits used to disable hardware interrupts, it is already
> handled by devm functions,
>
> v5:
> - correct mail address,
> - correct several typos,
> - move from const to static for sunxi_gpadc_chan_select functions,
> - rename soc_specific struct to gpadc_data,
> - rename soc_specific field to data in sun4i_gpadc_dev,
> - return error code from regmap_write in case of failure in read_raws,
> - share if condition in IIO_CHAN_INFO_RAW case,
> - add comment on why we use parent device for registering in thermal,
> - reordering remove function,
>
> v4:
> - rename files and variables from sunxi* to sun4i*,
> - shorten sunxi_gpadc_soc_specific structure to soc_specific,
> - factorize sysfs ADC and temp read_raws,
> - use cached values when read_raw times out (except before a first value
> is gotten),
> - remove mutex locks and unlocks from runtime_pm functions,
> - factorize irq initializations,
> - initialize temp_data and fifo_data values to -1 (error value),
> - "impersonate" MFD to register in thermal framework,
> - deactivate hardware interrupts one by one when probe fails or when
> removing driver instead of blindly deactivating all hardware interrupts,
> - selects THERMAL_OF in Kconfig,
>
> v3:
> - correct wrapping,
> - add comment about thermal sensor inner working,
> - move defines in mfd header,
> - use structure to define SoC specific registers or behaviour,
> - attach this structure to the device according to of_device_id of the
> platform device,
> - use new mutex instead of iio_dev mutex,
> - use atomic flags to avoid race between request_irq and disable_irq in
> probe,
> - switch from processed value to raw, offset and scale values for
> temperature ADC channel,
> - remove faulty sentinel in iio_chan_spec array,
> - add pm_runtime support,
> - register thermal sensor in thermal framework (forgotten since the
> beginning whereas it is present in current sun4i-ts driver),
> - remove useless ret variables to store return value of regmap_reads,
> - move comments on thermal sensor acquisition period in code instead of
> header,
> - adding goto label to unregister iio_map_array when failing to register
> iio_dev,
>
> v2:
> - add SUNXI_GPADC_ prefixes for defines,
> - correct typo in Kconfig,
> - reorder alphabetically includes, makefile,
> - add license header,
> - fix architecture variations not being handled in interrupt handlers or
> read raw functions,
> - fix unability to return negative values from thermal sensor,
> - add gotos to reduce code repetition,
> - fix irq variable being unsigned int instead of int,
> - remove useless dev_err and dev_info,
> - deactivate all interrupts if probe fails,
> - fix iio_device_register on NULL variable,
> - deactivate ADC in the IP when probe fails or when removing driver,
>
> drivers/iio/adc/Kconfig | 13 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/sun4i-gpadc-iio.c | 522 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 536 insertions(+)
> create mode 100644 drivers/iio/adc/sun4i-gpadc-iio.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 25378c5..ea36a4f 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -384,6 +384,19 @@ config ROCKCHIP_SARADC
> To compile this driver as a module, choose M here: the
> module will be called rockchip_saradc.
>
> +config SUN4I_GPADC
> + tristate "Support for the Allwinner SoCs GPADC"
> + depends on IIO
> + depends on MFD_SUN4I_GPADC
> + select THERMAL_OF
> + help
> + Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
> + GPADC. This ADC provides 4 channels which can be used as an ADC or as
> + a touchscreen input and one channel for thermal sensor.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called sun4i-gpadc-iio.
> +
> config TI_ADC081C
> tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
> depends on I2C
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 38638d4..204372d 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
> obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
> +obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> new file mode 100644
> index 0000000..e2c5ba8
> --- /dev/null
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -0,0 +1,522 @@
> +/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
> + *
> + * Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + * The Allwinner SoCs all have an ADC that can also act as a touchscreen
> + * controller and a thermal sensor.
> + * The thermal sensor works only when the ADC acts as a touchscreen controller
> + * and is configured to throw an interrupt every fixed periods of time (let say
> + * every X seconds).
> + * One would be tempted to disable the IP on the hardware side rather than
> + * disabling interrupts to save some power but that resets the internal clock of
> + * the IP, resulting in having to wait X seconds every time we want to read the
> + * value of the thermal sensor.
> + * This is also the reason of using autosuspend in pm_runtime. If there was no
> + * autosuspend, the thermal sensor would need X seconds after every
> + * pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the
> + * thermal sensor to be requested again in a certain time span before it gets
> + * shutdown for not being used.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/thermal.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/driver.h>
> +#include <linux/iio/machine.h>
> +#include <linux/mfd/sun4i-gpadc.h>
> +
> +static unsigned int sun4i_gpadc_chan_select(unsigned int chan)
> +{
> + return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
> +}
> +
> +static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
> +{
> + return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
> +}
> +
> +struct gpadc_data {
> + int temp_offset;
> + int temp_scale;
> + unsigned int tp_mode_en;
> + unsigned int tp_adc_select;
> + unsigned int (*adc_chan_select)(unsigned int chan);
> +};
> +
> +static const struct gpadc_data sun4i_gpadc_data = {
> + .temp_offset = -1932,
> + .temp_scale = 133,
> + .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> + .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> + .adc_chan_select = &sun4i_gpadc_chan_select,
> +};
> +
> +static const struct gpadc_data sun5i_gpadc_data = {
> + .temp_offset = -1447,
> + .temp_scale = 100,
> + .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> + .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> + .adc_chan_select = &sun4i_gpadc_chan_select,
> +};
> +
> +static const struct gpadc_data sun6i_gpadc_data = {
> + .temp_offset = -1623,
> + .temp_scale = 167,
> + .tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
> + .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
> + .adc_chan_select = &sun6i_gpadc_chan_select,
> +};
> +
> +struct sun4i_gpadc_iio {
> + struct iio_dev *indio_dev;
> + struct completion completion;
> + int temp_data;
> + u32 adc_data;
> + struct regmap *regmap;
> + unsigned int fifo_data_irq;
> + atomic_t ignore_fifo_data_irq;
> + unsigned int temp_data_irq;
> + atomic_t ignore_temp_data_irq;
> + const struct gpadc_data *data;
> + /* prevents concurrent reads of temperature and ADC */
> + struct mutex mutex;
> +};
> +
> +#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .datasheet_name = _name, \
> +}
> +
> +static struct iio_map sun4i_gpadc_hwmon_maps[] = {
> + {
> + .adc_channel_label = "temp_adc",
> + .consumer_dev_name = "iio_hwmon.0",
> + },
> + { /* sentinel */ },
> +};
> +
> +static const struct iio_chan_spec sun4i_gpadc_channels[] = {
> + SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
> + SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
> + SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
> + SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
> + {
> + .type = IIO_TEMP,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE) |
> + BIT(IIO_CHAN_INFO_OFFSET),
> + .datasheet_name = "temp_adc",
> + },
> +};
> +
> +static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
> + unsigned int irq)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> + int ret = 0;
> +
> + pm_runtime_get_sync(indio_dev->dev.parent);
> + mutex_lock(&info->mutex);
> +
> + reinit_completion(&info->completion);
> +
> + ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC,
> + SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) |
> + SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH);
> + if (ret)
> + return ret;
> +
> + if (irq == info->fifo_data_irq) {
> + ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
> + info->data->tp_mode_en |
> + info->data->tp_adc_select |
> + info->data->adc_chan_select(channel));
> + } else {
> + /*
> + * The temperature sensor returns valid data only when the ADC
> + * operates in touchscreen mode.
> + */
> + ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
> + info->data->tp_mode_en);
> + }
> +
> + if (ret)
> + return ret;
> +
> + enable_irq(irq);
> +
> + if (!wait_for_completion_timeout(&info->completion,
> + msecs_to_jiffies(100))) {
> + if ((irq == info->fifo_data_irq && info->adc_data == -1) ||
> + (irq == info->temp_data_irq && info->temp_data == -1)) {
> + ret = -ETIMEDOUT;
> + goto out;
> + }
> + }
> +
> + if (irq == info->fifo_data_irq)
> + *val = info->adc_data;
> + else
> + *val = info->temp_data;
> +
> + ret = 0;
> +
> +out:
> + disable_irq(irq);
> + mutex_unlock(&info->mutex);
> + pm_runtime_mark_last_busy(indio_dev->dev.parent);
> + pm_runtime_put_autosuspend(indio_dev->dev.parent);
> +
> + return ret;
> +}
> +
> +static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
> + int *val)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> + return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
> +}
> +
> +static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> + return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
> +}
> +
> +static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> + *val = info->data->temp_offset;
> +
> + return 0;
> +}
> +
> +static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> + *val = info->data->temp_scale;
> +
> + return 0;
> +}
> +
> +static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_OFFSET:
> + ret = sun4i_gpadc_temp_offset(indio_dev, val);
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_RAW:
> + if (chan->type == IIO_VOLTAGE)
> + ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
> + val);
> + else
> + ret = sun4i_gpadc_temp_read(indio_dev, val);
> +
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + ret = sun4i_gpadc_temp_scale(indio_dev, val);
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct iio_info sun4i_gpadc_iio_info = {
> + .read_raw = sun4i_gpadc_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id)
> +{
> + struct sun4i_gpadc_iio *info = dev_id;
> +
> + if (atomic_read(&info->ignore_temp_data_irq))
> + return IRQ_HANDLED;
> +
> + if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data))
> + complete(&info->completion);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
> +{
> + struct sun4i_gpadc_iio *info = dev_id;
> +
> + if (atomic_read(&info->ignore_fifo_data_irq))
> + return IRQ_HANDLED;
> +
> + if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data))
> + complete(&info->completion);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int sun4i_gpadc_runtime_suspend(struct device *dev)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> +
> + /* Disable the ADC on IP */
> + regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
> + /* Disable temperature sensor on IP */
> + regmap_write(info->regmap, SUN4I_GPADC_TPR, 0);
> +
> + return 0;
> +}
> +
> +static int sun4i_gpadc_runtime_resume(struct device *dev)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> +
> + /* clkin = 6MHz */
> + regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
> + SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
> + SUN4I_GPADC_CTRL0_FS_DIV(7) |
> + SUN4I_GPADC_CTRL0_T_ACQ(63));
> + regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
> + regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
> + SUN4I_GPADC_CTRL3_FILTER_EN |
> + SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
> + /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~1.3s */
> + regmap_write(info->regmap, SUN4I_GPADC_TPR,
> + SUN4I_GPADC_TPR_TEMP_ENABLE |
> + SUN4I_GPADC_TPR_TEMP_PERIOD(1953));
> +
> + return 0;
> +}
> +
> +static int sun4i_gpadc_get_temp(void *data, int *temp)
> +{
> + struct sun4i_gpadc_iio *info = (struct sun4i_gpadc_iio *)data;
> + int val, scale, offset;
> +
> + /* If reading temperature times out, take stored previous value. */
> + if (sun4i_gpadc_temp_read(info->indio_dev, &val))
> + val = info->temp_data;
> + sun4i_gpadc_temp_scale(info->indio_dev, &scale);
> + sun4i_gpadc_temp_offset(info->indio_dev, &offset);
> +
> + *temp = (val + offset) * scale;
> +
> + return 0;
> +}
> +
> +static const struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
> + .get_temp = &sun4i_gpadc_get_temp,
> +};
> +
> +static const struct dev_pm_ops sun4i_gpadc_pm_ops = {
> + .runtime_suspend = &sun4i_gpadc_runtime_suspend,
> + .runtime_resume = &sun4i_gpadc_runtime_resume,
> +};
> +
> +static int sun4i_irq_init(struct platform_device *pdev, const char *name,
> + irq_handler_t handler, const char *devname,
> + unsigned int *irq, atomic_t *atomic)
> +{
> + int ret;
> + struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
> + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
> +
> + /*
> + * Once the interrupt is activated, the IP continuously performs
> + * conversions thus throws interrupts. The interrupt is activated right
> + * after being requested but we want to control when these interrupts
> + * occur thus we disable it right after being requested. However, an
> + * interrupt might occur between these two instructions and we have to
> + * make sure that does not happen, by using atomic flags. We set the
> + * flag before requesting the interrupt and unset it right after
> + * disabling the interrupt. When an interrupt occurs between these two
> + * instructions, reading the atomic flag will tell us to ignore the
> + * interrupt.
> + */
> + atomic_set(atomic, 1);
> +
> + *irq = platform_get_irq_byname(pdev, name);
> + if (*irq < 0) {
> + dev_err(&pdev->dev, "no %s interrupt registered\n", name);
> + return *irq;
> + }
> +
> + *irq = regmap_irq_get_virq(mfd_dev->regmap_irqc, *irq);
> + ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, 0,
> + devname, info);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "could not request %s interrupt: %d\n",
> + name, ret);
> + return ret;
> + }
> +
> + disable_irq(*irq);
> + atomic_set(atomic, 0);
> +
> + return 0;
> +}
> +
> +static int sun4i_gpadc_probe(struct platform_device *pdev)
> +{
> + struct sun4i_gpadc_iio *info;
> + struct iio_dev *indio_dev;
> + int ret;
> + struct sun4i_gpadc_dev *sun4i_gpadc_dev;
> + struct thermal_zone_device *tzd;
> +
> + sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + info = iio_priv(indio_dev);
> + platform_set_drvdata(pdev, indio_dev);
> +
> + mutex_init(&info->mutex);
> + info->regmap = sun4i_gpadc_dev->regmap;
> + info->indio_dev = indio_dev;
> + info->temp_data = -1;
> + info->adc_data = -1;
> + init_completion(&info->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 = &sun4i_gpadc_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
> + indio_dev->channels = sun4i_gpadc_channels;
> +
> + info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data;
> +
> + /*
> + * This driver is a child of an MFD which has a node in the DT but not
> + * its children. Therefore, the resulting devices of this driver do not
> + * have an of_node variable.
> + * However, its parent (the MFD driver) has an of_node variable and
> + * since devm_thermal_zone_of_sensor_register uses its first argument to
> + * match the phandle defined in the node of the thermal driver with the
> + * of_node of the device passed as first argument and the third argument
> + * to call ops from thermal_zone_of_device_ops, the solution is to use
> + * the parent device as first argument to match the phandle with its
> + * of_node, and the device from this driver as third argument to return
> + * the temperature.
> + */
> + tzd = devm_thermal_zone_of_sensor_register(pdev->dev.parent, 0, info,
> + &sun4i_ts_tz_ops);
> + if (IS_ERR(tzd)) {
> + dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
> + PTR_ERR(tzd));
> + return PTR_ERR(tzd);
> + }
> +
> + pm_runtime_set_autosuspend_delay(&pdev->dev,
> + SUN4I_GPADC_AUTOSUSPEND_DELAY);
> + pm_runtime_use_autosuspend(&pdev->dev);
> + pm_runtime_set_suspended(&pdev->dev);
> + pm_runtime_enable(&pdev->dev);
> +
> + ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
> + sun4i_gpadc_temp_data_irq_handler, "temp_data",
> + &info->temp_data_irq, &info->ignore_temp_data_irq);
> + if (ret < 0)
> + goto err;
> +
> + ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
> + sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
> + &info->fifo_data_irq, &info->ignore_fifo_data_irq);
> + if (ret < 0)
> + goto err;
> +
> + ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to register iio map array\n");
> + goto err;
> + }
> +
> + ret = iio_device_register(indio_dev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "could not register the device\n");
> + goto err_map;
> + }
> +
> + return 0;
> +
> +err_map:
> + iio_map_array_unregister(indio_dev);
> +
> +err:
> + pm_runtime_put(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> +
> + return ret;
> +}
> +
> +static int sun4i_gpadc_remove(struct platform_device *pdev)
> +{
> + struct sun4i_gpadc_iio *info;
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> + pm_runtime_put(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> + info = iio_priv(indio_dev);
> + iio_map_array_unregister(indio_dev);
> + iio_device_unregister(indio_dev);
> +
> + return 0;
> +}
> +
> +static const struct platform_device_id sun4i_gpadc_id[] = {
> + { "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data },
> + { "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data },
> + { "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data },
> + { /* sentinel */ },
> +};
> +
> +static struct platform_driver sun4i_gpadc_driver = {
> + .driver = {
> + .name = "sun4i-gpadc-iio",
> + .pm = &sun4i_gpadc_pm_ops,
> + },
> + .id_table = sun4i_gpadc_id,
> + .probe = sun4i_gpadc_probe,
> + .remove = sun4i_gpadc_remove,
> +};
> +
> +module_platform_driver(sun4i_gpadc_driver);
> +
> +MODULE_DESCRIPTION("ADC driver for sunxi platforms");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply
* [PATCH 2/2] ARM: dts: bcm283x: fix typo in mailbox address
From: Andreas Färber @ 2016-10-30 15:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477505640-26658-2-git-send-email-stefan.wahren@i2se.com>
Hi,
Am 26.10.2016 um 20:14 schrieb Stefan Wahren:
> The address of the mailbox node in the bcm283x.dts has also a typo.
.dts -> .dtsi
"also has" or maybe "has the same typo as in the binding example"?
> So fix it accordingly.
>
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> Fixes: 05b682b7a3b2 ("ARM: bcm2835: dt: Add the mailbox to the device tree")
> ---
> arch/arm/boot/dts/bcm283x.dtsi | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
> index 46d46d8..74dd21b 100644
> --- a/arch/arm/boot/dts/bcm283x.dtsi
> +++ b/arch/arm/boot/dts/bcm283x.dtsi
> @@ -104,7 +104,7 @@
> reg = <0x7e104000 0x10>;
> };
>
> - mailbox: mailbox at 7e00b800 {
> + mailbox: mailbox at 7e00b880 {
> compatible = "brcm,bcm2835-mbox";
> reg = <0x7e00b880 0x40>;
> interrupts = <0 1>;
Otherwise,
Reviewed-by: Andreas F?rber <afaerber@suse.de>
At one point in time dtc did complain about such mismatches...
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)
^ permalink raw reply
* [PATCH 03/10] iio: adc: stm32: add optional dma support
From: Jonathan Cameron @ 2016-10-30 15:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-4-git-send-email-fabrice.gasnier@st.com>
On 25/10/16 17:25, Fabrice Gasnier wrote:
> 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>
Few little bits inline, but looks superficially good (my dma knowledge isn't
really up to reviewing this.) Lars can you take a look (perhaps at the next
version)?
Also, you 'could' potentially use the dma buffer stuff to give a lower overhead
route for the data to userspace if the device goes quick enough to warrant
it.
Jonathan
> ---
> 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 */
invokes.
> + 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);
> +
This bit wants some explanatory comments I think.
> + 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;
> }
>
>
^ permalink raw reply
* [PATCH 02/10] iio: adc: Add stm32 support
From: Jonathan Cameron @ 2016-10-30 15:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-3-git-send-email-fabrice.gasnier@st.com>
On 25/10/16 17:25, Fabrice Gasnier wrote:
> This patch adds support for STMicroelectronics STM32 MCU's analog to
> digital converter.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Hi Fabrice,
Sometimes I hate SoC ADCs. For some reason the hardware designers seem to
try and throw everything and the kitchen sink at them. Discontinuous mode
as an example in this device. Not seen that particular piece of fun before
and glad to see you haven't 'yet' tried to support it!
Anyhow, the complexity of the hardware leads to an initially complex driver.
My first thought it that this would be easier to follow / review if we
built it up in smaller steps. Perhaps ditch the injected channel support
entirely in the first instance. I also wonder if you don't need to support
that whole thing (injected sampling) as another iio device entirely using the
same channels. That's kind of what it is from a data flow point of view
(we've had arbitary sequencers before with priorities - don't think anyone
ever decided the pain was worth supporting the complexity, but right answer
has always been multiple IIO devices).
You also have at least one layer of abstraction in here that serves no
current purpose. Please clear that out for now. It'll make the code
shorter and easier to follow. If/when other parts are introduced then
is the time to do that transistion to having the abstraction.
My first thought on the double / tripple adc handling is that you'd be better
off handling them as 3 separate devices then doing some 'unusual' trigger
handling to support the weird sequencing. Guessing you thought about that?
If so could you lay out your reasoning for the single driver instance approach.
I'm not arguing against it btw, merely want to understand your reasoning!
It would be tricky given one set of channels are selectable over 3 devices
and there are constraints to enforce (not sampling same channel on two ADCs
at the same time) but not impossible... Perhaps what you have here is
indeed simpler!
Whilst it's been a nasty job to review, I'm guessing writing it was
much worse ;) Pretty good starting point though might take a little while
to pin down the remaining questions on how best to handle this particular
monster.
Jonathan
> ---
> 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;
> + }
It's horrible but to end up in the 'obvious' state I'd disable the adc
again assuming that doesn't kill the stuff that is configured.
> +
> + 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);
This is known maximum length... I'd just avoid the complexity of allocating
it like this - a comment would do the job to say it is the right length.
> + 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);
Put it back again afterwards? Otherwise some nasty race conditions look
likely to me.. (userspace sets trigger and is about to enable the buffer
when along comes this code and changes it underneath).
> + 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
As this is kernel doc please document the parameter as well. Otherwise
we'll get a pile of warnings!
> + */
> +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);
Want to do this in the non injected case? it's a noop isn't it?
> + status &= mask;
> +
> + /* Regular data */
> + if (status & reginfo->eoc) {
Hmm.. this is a little bit of 'missuse' of the standard trigger architecture
but as long as it's restricted to just this device I don't suppose we need
to care. Only reason we need it is to provide control of 'which' hardware
trigger is being used.
Guessing the DMA will almost always be turned on and will make this oddity
effectively disappear.
> + 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);
Hmm.. ret |= is rather fragile. Preferable to make the handling of NONE
vs IRQ_HANDLED explicit.
If you were to split the driver up as I suggested might make sense above,
then this would be done with an irq chip in a top level device (effectively
a very simple mfd).
> +
> + 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)
I'm glad you kept this relatively simple compared to some of the
'fun' the hardware is capable of. Very wise!
> +{
> + 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);
This is a surprise as postdisbale should balance preenable...
Ah, you have update scan mode enabling the adc. If you can balance it
better by moving that to preenable please do as it is more 'obviously' correct.
> +
> + 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);
I'd be more cynical. It's not that big a memory allocation at worst.
Just put a big enough buffer in your adc structure and don't bother doing
it dynamically.
If you didn't want to do it, it should be in the preenable callback rather
than the trigger state one (for semantic reasons rather than because it's a
bug)
> + 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);
> + }
> +}
> +
I'd like a bit of documentation on this and a few of the other more
complex functions. Here it wasn't immediately obvious to me that it
was registering a large set of triggers. Also, silly question but
do you have any means of controlling the various timer setups from userspace?
There have been numerous discussions over the years on having a generic
timer subsystem, but if anything got written it passed me by. I have a couple
of boards where it would be handy but never had the time to do more than
talk about it ;)
> +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;
This is one of those cases where actually I'd argue just having the number
here and not under a define would be clearer! So just put 16 here.
> + chan->scan_type.shift = 0;
Should be unneeded. Shift of 0 is the obvious default so no info provided
to readers of the code either really.
> +}
> +
> +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");
How would we have instantiated this if there was not a suitable match?
As such what does this check give us? (confused!)
> + 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");
No benefit in this info being provided (it's obvious, device just turned up
in sysfs :) So drop it.
> +
> + 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
> + */
This is a big chunk of abstraction that seems excessive at the moment.
I'd rather see it introduced only just before it's actually used..
(I'm guessing it's intended for support of similar parts?)
Right now it just makes the driver harder to review.
> +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");
>
^ permalink raw reply
* [PATCH 1/2] DT: binding: bcm2835-mbox: fix address typo in example
From: Andreas Färber @ 2016-10-30 15:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477505640-26658-1-git-send-email-stefan.wahren@i2se.com>
Am 26.10.2016 um 20:13 schrieb Stefan Wahren:
> The address of the mailbox node in the example has a typo.
> So fix it accordingly.
>
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> Fixes: d4b5c782b9f4 ("dt/bindings: Add binding for the BCM2835 mailbox driver")
> ---
> .../bindings/mailbox/brcm,bcm2835-mbox.txt | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Reviewed-by: Andreas F?rber <afaerber@suse.de>
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)
^ permalink raw reply
* [PATCHv4 0/4] WX checking for arm64
From: Catalin Marinas @ 2016-10-30 15:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477585654-8908-1-git-send-email-labbott@redhat.com>
On Thu, Oct 27, 2016 at 09:27:30AM -0700, Laura Abbott wrote:
> Laura Abbott (4):
> arm64: dump: Make ptdump debugfs a separate option
> arm64: dump: Make the page table dumping seq_file optional
> arm64: dump: Remove max_addr
> arm64: dump: Add checking for writable and exectuable pages
Queued for 4.10. Thanks.
--
Catalin
^ 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