Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v4 5/9] ARM: dts: rockchip: disable arm-global-timer for rk3188
From: Heiko Stübner @ 2017-01-23 16:14 UTC (permalink / raw)
  To: Alexander Kochetkov
  Cc: linux-kernel, devicetree, linux-arm-kernel, linux-rockchip,
	Thomas Gleixner, Mark Rutland, Rob Herring, Russell King,
	Caesar Wang, Huang Tao, Daniel Lezcano
In-Reply-To: <1480436092-10728-6-git-send-email-al.kochet@gmail.com>

Am Dienstag, 29. November 2016, 19:14:48 CET schrieb Alexander Kochetkov:
> arm-global-timer can provide clockevents, clocksource and shed_clock. But
> on rk3188 platform it provide only clocksource and shed_clock. clockevents
> from  arm-global-timer is not used by kernel because there is another
> clockevent provider with higher rating (smp-twd).
> 
> My commit from the series implement clocksource and shed_clock using
> rockchip_timer. But sched clock from rk_timer is not selected by kernel
> due to lower frequency than arm-global-timer, and clocksource from
> rk_timer is not selected by kernel due to lower rating than
> arm-global-timer. And I don't want to increase clocksource rating
> because ratings greater 300 used for high frequency clocksources.
> 
> clocksource and shed_clock is quite unstable, because their rate depends
> on cpu frequency. So disable arm-global-timer and use clocksource and
> sched_clock from rockchip_timer.

That commit message is hard to understand in its entirety, but this last 
paragraph is essentially enough to describe the problem. So maybe you could 
shorten that somehow.


Heiko


> Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
> ---
>  arch/arm/boot/dts/rk3188.dtsi |    1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi
> index 0dc52fe..44da3d42 100644
> --- a/arch/arm/boot/dts/rk3188.dtsi
> +++ b/arch/arm/boot/dts/rk3188.dtsi
> @@ -546,6 +546,7 @@
> 
>  &global_timer {
>  	interrupts = <GIC_PPI 11 0xf04>;
> +	status = "disabled";
>  };
> 
>  &local_timer {

^ permalink raw reply

* Re: [PATCH v14 3/5] tee: add OP-TEE driver
From: Arnd Bergmann @ 2017-01-23 16:16 UTC (permalink / raw)
  To: Jens Wiklander
  Cc: Greg Kroah-Hartman, Olof Johansson, Andrew Morton, Wei Xu,
	linux-kernel, linux-arm-kernel, devicetree, Al Viro,
	valentin.manea, jean-michel.delorme, emmanuel.michel, javier,
	Jason Gunthorpe, Mark Rutland, Michal Simek, Rob Herring,
	Will Deacon, Nishanth Menon, Andrew F . Davis, broonie,
	scott.branden
In-Reply-To: <20170123090852.GB1910@jax>

On Monday, January 23, 2017 10:08:53 AM CET Jens Wiklander wrote:
> On Fri, Jan 20, 2017 at 05:57:51PM +0100, Arnd Bergmann wrote:
> > On Thursday, January 19, 2017 3:56:23 PM CET Jens Wiklander wrote:
> > > On Wed, Jan 18, 2017 at 05:28:17PM +0100, Arnd Bergmann wrote:

> > > Does the platform devices really need cleaning? I mean
> > > of_platform_default_populate_init() creates a bunch of platform devices
> > > which are just left there even if unused. Here we're doing the same
> > > thing except that we're doing it for a specific node in the DT.
> > 
> > I think it will work if you don't clean them up, but it feels wrong
> > to have a loadable module that creates devices when loaded but doesn't
> > remove them when unloaded.
> > 
> > This could be done differently by having the device creation done in
> > one driver and the the user of that device in another driver, but I
> > think just killing off the device achieves the same in a simpler way.
> 
> I see your point. My final concern here is that with device we got
> entries in sysfs and uevents that could be used to automatically start
> the correct supplicant. Different drivers are likely to require
> different supplicants. Starting the correct supplicant based on uevents
> is a quite elegant solution which I'm not sure how to support when
> skipping devices. Perhaps I could create an object below
> <sysfs>/firmware/tee ?

Putting the objects somewhere other than /sys/devices sounds good, yes.
This would also help with TEE implementations that might get probed
differently.

I think the natural place would be /sys/class/tee/, as we normally
require something in /sys/class anyway to support the character
device.

/sys/firmware/tee/ sounds less fitting, as there other TEE implementations
are not necessarily firmware based, as you point out. 
/sys/firmware/op-tee certainly makes sense for anything that is specific
to OP-TEE in particular, while /sys/class/tee would be for anything
that uses the ioctl interface. This part is particularly important to
get right from the start, just like the ioctls themselves we can't make
incompatible changes here later once there are users relying on the
upstream kernel interfaces.

> > > > > +/*
> > > > > + * Get revision of Trusted OS.
> > > > > + *
> > > > > + * Used by non-secure world to figure out which version of the Trusted OS
> > > > > + * is installed. Note that the returned revision is the revision of the
> > > > > + * Trusted OS, not of the API.
> > > > > + *
> > > > > + * Returns revision in 2 32-bit words in the same way as
> > > > > + * OPTEE_MSG_CALLS_REVISION described above.
> > > > > + */
> > > > > +#define OPTEE_MSG_OS_OPTEE_REVISION_MAJOR	1
> > > > > +#define OPTEE_MSG_OS_OPTEE_REVISION_MINOR	0
> > > > > +#define OPTEE_MSG_FUNCID_GET_OS_REVISION	0x0001
> > > > 
> > > > Just for my understanding, what is the significance of these numbers,
> > > > i.e. which code (user space, kernel driver, trusted OS) provides
> > > > the uuid and which one provides the version? The code comments almost
> > > > make sense to me, but I don't see why specific versions are listed
> > > > in this header.
> > > 
> > > You're right, OPTEE_MSG_OS_OPTEE_REVISION_* should be removed. The
> > > actual version the secure OS is of a mostly informational nature. The
> > > same goes the OS UUID, but I suppose the actual UUID used by the
> > > upstream version of OP-TEE OS could be interesting to know.
> > ...
> > > > What is the expected behavior when one side reports a version that
> > > > is unknown? Can one side claim to be backwards compatible with
> > > > a previous version, or does each new version need support on
> > > > all three sides?
> > > 
> > > The UUID and version of the message protocol are important to match
> > > correctly as otherwise it could mean that there's something unexpected
> > > in secure world that following the message protocol would be undefined
> > > behaviour. All changes to the message protocol should be backwards
> > > compatible in the sense that the driver and secure world need to
> > > negotiate eventual extensions while probing. That's what we're doing in
> > > optee_msg_exchange_capabilities().
> > 
> > Ok, then maybe the "compatible" identifier in DT should be sufficient
> > to ensure that the capability exchange works, and the rest be based
> > on that?
> > 
> > We tend to avoid version checks for APIs in the kernel because they
> > never work in practice, but the capability check should be fine.
> 
> UUID and version of the message protocol is required by ARM SMC Calling
> Convention. It will be there anyway so we could just as well check it in
> the probe function to catch eventual mismatches in configuration. Since
> we're using capabilities to manage extensions of the protocol I think
> the minor version could be ignored by probe.

Ok, makes sense.

	Arnd

^ permalink raw reply

* Re: [PATCH 1/4] dt-bindings: add multidomain support to i.MX GPC DT binding
From: Rob Herring @ 2017-01-23 16:26 UTC (permalink / raw)
  To: Lucas Stach
  Cc: Mark Rutland, devicetree, patchwork-lst, kernel, Fabio Estevam,
	Shawn Guo, linux-arm-kernel
In-Reply-To: <20170120155225.31905-2-l.stach@pengutronix.de>

On Fri, Jan 20, 2017 at 04:52:22PM +0100, Lucas Stach wrote:
> This adds a new binding for the Freescale i.MX GPC block, which allows
> to describe multiple power domains in a more natural way. The driver
> will continue to support the old binding for existing DTBs, but new
> features like the additional domains present on i.MX6SX will only be
> usable with the new binding.
>
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> ---
>  .../devicetree/bindings/power/fsl,imx-gpc.txt      | 81 ++++++++++++++--------
>  1 file changed, 54 insertions(+), 27 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
> index 65cc0345747d..e5b660018d63 100644
> --- a/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
> +++ b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
> @@ -1,22 +1,41 @@
>  Freescale i.MX General Power Controller
>  =======================================
>  
> -The i.MX6Q General Power Control (GPC) block contains DVFS load tracking
> -counters and Power Gating Control (PGC) for the CPU and PU (GPU/VPU) power
> -domains.
> +The i.MX6 General Power Control (GPC) block contains DVFS load tracking
> +counters and Power Gating Control (PGC).
>  
>  Required properties:
>  - compatible: Should be "fsl,imx6q-gpc" or "fsl,imx6sl-gpc"
>  - reg: should be register base and length as documented in the
>    datasheet
> -- interrupts: Should contain GPC interrupt request 1
> -- pu-supply: Link to the LDO regulator powering the PU power domain
> -- clocks: Clock phandles to devices in the PU power domain that need
> -	  to be enabled during domain power-up for reset propagation.
> -- #power-domain-cells: Should be 1, see below:
> +- interrupts: Should contain one interrupt specifier for the GPC interrupt
> +- clocks: Must contain an entry for each entry in clock-names.
> +  See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details.
> +- clock-names: Must include the following entries:
> +  - ipg
> +- pgc: a list of child nodes describing the SoC power domains controlled by the
> +  power gating controller.

pgc is a node, not a property. Call it 'power-domains' instead.

>  
> -The gpc node is a power-controller as documented by the generic power domain
> -bindings in Documentation/devicetree/bindings/power/power_domain.txt.
> +The power domains are generic power domain providers as documented in
> +Documentation/devicetree/bindings/power/power_domain.txt. They are described as
> +subnodes of the GPC and should contain the following
> +
> +Required properties:
> +- reg: the DOMAIN_INDEX as used by the client devices to refer to this
> +  power domain
> +  The following DOMAIN_INDEX values are valid for i.MX6Q:
> +  ARM_DOMAIN     0
> +  PU_DOMAIN      1
> +  The following additional DOMAIN_INDEX value is valid for i.MX6SL:
> +  DISPLAY_DOMAIN 2
> +
> +- #power-domain-cells: Should be 0
> +
> +Optional properties:
> +- clocks: a number of phandles to clocks that need to be enabled during domain
> +  power-up sequencing to ensure reset propagation into devices located inside
> +  this power domain
> +- domain-supply: a phandle to the regulator powering this domain

'power-supply' is more common for when we don't really have a name.

>  Example:
>  
> @@ -25,14 +44,29 @@ Example:
>  		reg = <0x020dc000 0x4000>;
>  		interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>,
>  			     <0 90 IRQ_TYPE_LEVEL_HIGH>;
> -		pu-supply = <&reg_pu>;
> -		clocks = <&clks IMX6QDL_CLK_GPU3D_CORE>,
> -			 <&clks IMX6QDL_CLK_GPU3D_SHADER>,
> -			 <&clks IMX6QDL_CLK_GPU2D_CORE>,
> -			 <&clks IMX6QDL_CLK_GPU2D_AXI>,
> -			 <&clks IMX6QDL_CLK_OPENVG_AXI>,
> -			 <&clks IMX6QDL_CLK_VPU_AXI>;
> -		#power-domain-cells = <1>;
> +		clocks = <&clks IMX6QDL_CLK_IPG>;
> +		clock-names = "ipg";
> +		
> +		pgc {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			power-domain@0 {
> +				reg = <0>;
> +				#power-domain-cells = <0>;
> +			};
> +			pd_pu: power-domain@1 {
> +				reg = <1>;
> +				#power-domain-cells = <0>;
> +				domain-supply = <&reg_pu>;
> +				clocks = <&clks IMX6QDL_CLK_GPU3D_CORE>,
> +				         <&clks IMX6QDL_CLK_GPU3D_SHADER>,
> +				         <&clks IMX6QDL_CLK_GPU2D_CORE>,
> +				         <&clks IMX6QDL_CLK_GPU2D_AXI>,
> +				         <&clks IMX6QDL_CLK_OPENVG_AXI>,
> +				         <&clks IMX6QDL_CLK_VPU_AXI>;
> +			};
> +		};
>  	};
>  
>  
> @@ -40,20 +74,13 @@ Specifying power domain for IP modules
>  ======================================
>  
>  IP cores belonging to a power domain should contain a 'power-domains' property
> -that is a phandle pointing to the gpc device node and a DOMAIN_INDEX specifying
> -the power domain the device belongs to.
> +that is a phandle pointing to the power domain the device belongs to.
>  
>  Example of a device that is part of the PU power domain:
>  
>  	vpu: vpu@02040000 {
>  		reg = <0x02040000 0x3c000>;
>  		/* ... */
> -		power-domains = <&gpc 1>;
> +		power-domains = <&pd_pu>;
>  		/* ... */
>  	};
> -
> -The following DOMAIN_INDEX values are valid for i.MX6Q:
> -ARM_DOMAIN     0
> -PU_DOMAIN      1
> -The following additional DOMAIN_INDEX value is valid for i.MX6SL:
> -DISPLAY_DOMAIN 2
> -- 
> 2.11.0
> 

^ permalink raw reply

* Re: [PATCH v4 6/9] clocksource/drivers/rockchip_timer: split bc_timer into rk_timer and rk_clock_event_device
From: Daniel Lezcano @ 2017-01-23 16:26 UTC (permalink / raw)
  To: Alexander Kochetkov
  Cc: linux-kernel, devicetree, linux-arm-kernel, linux-rockchip,
	Thomas Gleixner, Heiko Stuebner, Mark Rutland, Rob Herring,
	Russell King, Caesar Wang, Huang Tao
In-Reply-To: <1480436092-10728-7-git-send-email-al.kochet@gmail.com>

On Tue, Nov 29, 2016 at 07:14:49PM +0300, Alexander Kochetkov wrote:
> The patch move ce field out of struct bc_timer into struct
> rk_clock_event_device and rename struct bc_timer to struct rk_timer.

Why ?

^ permalink raw reply

* Re: [PATCH v3 5/7] NFC: trf7970a: Remove support for 'vin-voltage-override' DT property
From: Rob Herring @ 2017-01-23 16:26 UTC (permalink / raw)
  To: Mark Greer
  Cc: Samuel Ortiz, Lauro Ramos Venancio, Aloisio Almeida Jr,
	linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170120191745.29255-6-mgreer-luAo+O/VEmrlveNOaEYElw@public.gmane.org>

On Fri, Jan 20, 2017 at 12:17:43PM -0700, Mark Greer wrote:
> The 'vin-voltage-override' DT property is used by the trf7970a
> driver to override the voltage presented to the driver by the
> regulator subsystem.  This is unnecessary as properly specifying
> the regulator chain via DT properties will accomplish the same
> thing.  Therefore, remove support for 'vin-voltage-override'.
> 
> Signed-off-by: Mark Greer <mgreer-luAo+O/VEmrlveNOaEYElw@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/net/nfc/trf7970a.txt |  2 --
>  drivers/nfc/trf7970a.c                                 | 11 +----------
>  2 files changed, 1 insertion(+), 12 deletions(-)

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] Documentation: dt-bindings: tmp007: Add optional interrupt support
From: Manivannan Sadhasivam @ 2017-01-23 16:28 UTC (permalink / raw)
  To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	pmeerw-jW+XmwGofnusTnJN9+BGXg
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA

This patch adds optional interrupt binding support for TI TMP007 - IR thermopiler sensor

Signed-off-by: Manivannan Sadhasivam <manivannanece23-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---

Created separate patch for devicetree and driver.

 Documentation/devicetree/bindings/iio/temperature/tmp007.txt | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/temperature/tmp007.txt b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt
index 3b8f41f..b63aba9 100644
--- a/Documentation/devicetree/bindings/iio/temperature/tmp007.txt
+++ b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt
@@ -18,10 +18,18 @@ Required properties:
 		   1	 SDA	   0x46
 		   1     SCL       0x47
 
+Optional properties:
+
+  - interrupt-parent: should be the phandle for the interrupt controller
+
+  - interrupts: interrupt mapping for GPIO IRQ (level active low)
+
 Example:
 
 tmp007@40 {
         compatible = "ti,tmp007";
         reg = <0x40>;
+	interrupt-parent = <&gpio0>;
+	interrupts = <5 0x08>;
 };
 
-- 
2.7.4

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

^ permalink raw reply related

* Re: [PATCHv2] Documentation: dt-bindings: Add binding documentation for TI clkctrl clocks
From: Tony Lindgren @ 2017-01-23 16:28 UTC (permalink / raw)
  To: Tero Kristo
  Cc: Michael Turquette, Stephen Boyd, devicetree, linux-clk,
	linux-omap, Paul Walmsley, Rob Herring
In-Reply-To: <40b1f6a8-fc5c-b619-9570-bc2f9731af89@ti.com>

* Tero Kristo <t-kristo@ti.com> [170123 06:45]:
> On 18/01/17 00:53, Tony Lindgren wrote:
> > Texas Instruments omap variant SoCs starting with omap4 have a clkctrl
> > clock controller instance for each interconnect target module. The clkctrl
> > controls functional and interface clocks for the module.
> > 
> > The clkctrl clocks are currently handled by arch/arm/mach-omap2 hwmod code.
> > With this binding and a related clock device driver we can start moving the
> > clkctrl clock handling to live in drivers/clk/ti.
> > 
> > Note that this binding allows keeping the clockdomain related parts out of
> > drivers/clock. The CLKCTCTRL and DYNAMICDEP registers can be handled by
> > a separate driver in drivers/soc/ti and genpd. If the clockdomain driver
> > needs to know it's clocks, we can just set the the clkctrl device
> > instances to be children of the related clockdomain device.
> > 
> > Each clkctrl clock can have multiple optional gate clocks, and multiple
> > optional mux clocks. To represent this in device tree, it seems that
> > it is best done using four clock cells #clock-cells = <4> property.
> > 
> > The reasons for using #clock-cells = <4> are:
> > 
> > 1. We need to specify the clkctrl offset from the instance base. Otherwise
> >    we end up with a large number of device tree nodes that need to be
> >    patched when new clocks are discovered in a clkctrl clock with minor
> >    hardware revision changes for example
> > 
> > 2. On omap5 CM_L3INIT_USB_HOST_HS_CLKCTRL has ten OPTFCLKEN bits. So we
> >    need to use a separate cell for optional gate clocks to avoid address
> >    space conflicts
> > 
> > 3. Some clkctrl instances can also also optional mux clocks. To address
> >    them properly we need also a separate cell for the optional mux
> >    clock index
> > 
> > 4. The modulemode clock needs a flag passed to it for hardware or
> >    software controlled mode
> 
> Hi Tony,
> 
> I think #clock-cells = <4> is too much. I believe we only need 2:
> 
> - one entry for clkctrl offset
> - one entry for clock offset within the clkctrl entry (0 = module clock, 8+
> = opt-clocks / mux clocks / dividers)

OK the less #clock-cells the better as long as it's enough :)

> Fields 2 / 3 in your proposal are mutually exclusive, if either field is
> non-zero, the other one must be zero. And, the opt clocks / mux / divs
> always have different values for these.

OK. Just to confirm the assumptions then:

1. The optional mux clock the consumer needs to select the right
   source clock with with clk_set_parent()

2. The optional divider clock rate must be set by the consumer
   using clk_set_rate()

And in that case we again don't need to define any artificial
clock indexes, which is good if new clocks are discovered between
various SoC revisions.

> Field 4 is kind of redundant also, as the module clock must be registered at
> the clkctrl probe time, it is too late for the clock consumer to provide the
> proper setting for the clock during its own probe. It seems I need to add
> static data to driver which basically has this information in place already.

OK yeah good point, the "clocks" is a consumer property.

So in that case we must also assume that if any clock consumer needs
to change between HWSUP or SWSUP, it needs to be done with some yet
to be determined API. We have not needed that so far AFAIK though.

If there are no issues with the above, I'm naturally fine using the
#clock-cells = <2> :)

Regards,

Tony

^ permalink raw reply

* Re: [PATCH 2/3] dt-bindings: arm64 ARCH_THUNDER2 platform documentation
From: Rob Herring @ 2017-01-23 16:28 UTC (permalink / raw)
  To: Jayachandran C
  Cc: linux-arm-kernel, Catalin Marinas, Will Deacon, Arnd Bergmann,
	linux-kernel, david.daney, devicetree
In-Reply-To: <1484945201-17248-3-git-send-email-jnair@caviumnetworks.com>

On Fri, Jan 20, 2017 at 08:46:40PM +0000, Jayachandran C wrote:
> Add documentation for Cavium ThunderX2 CN99XX ARM64 processor family.
> The processor core will use cavium,thunder2 as ID and the SoC will use
> cavium,thunder-99xx
> 
> Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
> ---
>  Documentation/devicetree/bindings/arm/cavium-thunder2.txt | 5 +++++
>  Documentation/devicetree/bindings/arm/cpus.txt            | 1 +
>  2 files changed, 6 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/cavium-thunder2.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply

* Re: [PATCHv4] mfd: cpcap: Add minimal support
From: Tony Lindgren @ 2017-01-23 16:30 UTC (permalink / raw)
  To: Lee Jones, Samuel Ortiz
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Marcel Partap, Mark Rutland,
	Michael Scott
In-Reply-To: <20170106004439.12249-1-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>

Hi,

* Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> [170105 16:45]:
> Many Motorola phones like droid 4 are using a custom PMIC called CPCAP
> or 6556002. We can support it's core features quite easily with regmap_spi
> and regmap_irq.
> 
> The children of cpcap, such as regulators, ADC and USB, can be just regular
> device drivers and defined in the dts file. They get probed as we call
> of_platform_populate() at the end of our probe, and then the children
> can just call dev_get_regmap(dev.parent, NULL) to get the regmap.
> 
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: Marcel Partap <mpartap-hi6Y0CQ0nG0@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Cc: Michael Scott <michael.scott-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
> ---
> Changes from v2:
> 
> - Fix typos in binding documentation for #size-cells and spi-cs-high
> - Add ack from Rob
> 
> Changes from v3:
> 
> - Fix comments from Lee, mostly to simplify cpcap_init_irq_chip()

Lee, it's been few weeks now, do you have any more comments on
this one?

I'd like to get this out of the way for v4.11 to be able to start
sending the CPCAP regulator and USB PHY driver patches for v4.12 :)

Cheers,

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

^ permalink raw reply

* [PATCH 0/7] Add STM32 DFSDM support
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o

This patch-set handles the Digital Filter for Sigma Delta modulators IP on STM32 platforms.
DFSDM IP allows PDM microphone capture and sigma delta ADC conversion.

Those two features are mixed into the registers of the same hardware block which lead to introduce a multifunction 
driver on the top of them to be able to share the registers.

for This, MFD exports an API based on  get/configure/start/stop/release mechanisms.

Two kind of resources are handled by the MFD driver.
- The filters: 
	- 4 instances available for L4 and H7
        - composed with a SInC filter and an integrator 
- The transceivers or channels
	- 8 instances available for L4 and H7
        - can be connected to
        	serial transceivers (SPI or Manchester mode)
                internal parallel transceivers (from memory or internal ADC)
        -  associated  monitoring: analog watchdog, short circuit detector, extreme detector  
           (monitoring support will be part of a specific patch-set)) 

Principle OF the DFSDM IP is to connect one or several channels to a filter to process Sigma delta ADC or PDM microphone data.

Two types of child devices can de declared:
IIO for the management of Sigma Delta ADC conversion.
ASoC for PDM microphone audio capture.

For details on IP and use case examples please refer to this document:
http://www.st.com/content/ccc/resource/training/technical/product_training/96/b6/2b/ea/72/3f/4e/d5/STM32L4_System_DFSDM.pdf/files/STM32L4_System_DFSDM.pdf/jcr:content/translations/en.STM32L4_System_DFSDM.pdf

Remark:
    For audio part a patch is proposed to update core part to add copy support in soc_dmaengine_pcm.
    Rational is that output data register in DFSDM contains audio sample on 24 MSB + channel ID on the 8 LSB.
    Proposal is to allow to register a copy ops in soc_dmaengine_pcm to be able to perform post-processing.
    Back up if not accepted can be to create a pcm_engine in stm32 driver.
 
Not part of this patch-set, but should come as add-on patches:
- IIO management of Analog watchdog, short-circuit detection and clock absence detector, with associated IRQs management.
- IIO trigger and buffer management.  

Arnaud Pouliquen (6):
  MFD: add bindings for STM32 DFSDM driver
  MFD: add STM32 DFSDM support
  IIO: add bindings for STM32 DFSDM ADC driver
  IIO: add STM32 DFSDM ADC support
  ASoC: add bindings for STM32 DFSDM driver
  ASoC: add STM32 DFSDM support

olivier moysan (1):
  ASoC: dmaengine_pcm: add copy support

 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        |   60 ++
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        |   68 ++
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   |   84 ++
 drivers/iio/adc/Kconfig                            |    9 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/stm32-dfsdm-adc.c                  |  676 +++++++++++++
 drivers/mfd/Kconfig                                |   11 +
 drivers/mfd/Makefile                               |    2 +
 drivers/mfd/stm32-dfsdm-reg.h                      |  220 +++++
 drivers/mfd/stm32-dfsdm.c                          | 1044 ++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h                    |  324 ++++++
 include/sound/dmaengine_pcm.h                      |    3 +
 sound/soc/Kconfig                                  |    1 +
 sound/soc/Makefile                                 |    1 +
 sound/soc/soc-generic-dmaengine-pcm.c              |   37 +-
 sound/soc/stm/Kconfig                              |   10 +
 sound/soc/stm/Makefile                             |    2 +
 sound/soc/stm/stm32_adfsdm.c                       |  686 +++++++++++++
 18 files changed, 3237 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

-- 
1.9.1

^ permalink raw reply

* [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin
In-Reply-To: <1485189145-29576-1-git-send-email-arnaud.pouliquen@st.com>

Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt

diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
new file mode 100644
index 0000000..e0b45ee
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
@@ -0,0 +1,68 @@
+STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
+ulti-function device.
+
+The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
+
+The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
+For this, a pool of m filters can be connected to a pool of n channels.
+For STM32H7 : m = 4, n = 8.
+
+Each channel n is assigned to the SPI or Manchester interface n or n + 1.
+Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
+Filtering result is stored in a left aligned register, with 8 LSB reserved for
+the input channel ID.
+
+Each filter instance supports two contexts to manage conversions, each one has
+its own configurable sequence and trigger:
+- regular conversion: used for single or continuous conversion.
+- injected conversions: used for triggered conversion.
+
+Interfaces supported:
+- sigma delta ADCs trough IIO framework.
+- PDM microphones through ASoC framework.
+
+Required properties:
+- compatible:	Must be "st,stm32h7-dfsdm".
+- reg:		Specifies the DFSDM block register address and length.
+- interrupts:	IRQ lines connected to each DFSDM filter instance.
+- clocks:	IP and serial interfaces clocking. Should be set according
+		to rcc clock ID and "clock-names".
+- clock-names:	Input clock name "dfsdm_clk" must be defined,
+		"audio_clk" is optional. If defined CLKOUT is based on the audio
+		clock, else "dfsdm_clk" is used.
+
+Optional properties:
+- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
+		  to "clock" property. Frequency must be a multiple of the rcc
+		  clock frequency. If not, clkout frequency will not be
+		  accurate.
+- pinctrl-names:  set to "default".
+- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
+		  module.
+		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
+Example :
+	dfsdm: dfsdm@4400D000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		interrupts = <110>, <111>, <112>, <113>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm_clk";
+		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
+		pinctrl-names = "default";
+		st,clkout-freq = <2480000>;
+
+		iio_dfsdm0: iio-dfsdm@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			status = "disabled";
+		};
+		dai_dfsdm0: dfsdm-audio@0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			status = "disabled";
+		};
+	};
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/7] MFD: add STM32 DFSDM support
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o
In-Reply-To: <1485189145-29576-1-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

DFSDM hardware IP can be used at the same time for ADC sigma delta
conversion and audio PDM microphone.
MFD driver is in charge of configuring IP registers and managing IP clocks.
For this it exports an API to handles filters and channels resources.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 drivers/mfd/Kconfig             |   11 +
 drivers/mfd/Makefile            |    2 +
 drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
 drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
 5 files changed, 1601 insertions(+)
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..4bb660b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1607,6 +1607,17 @@ config MFD_STW481X
 	  in various ST Microelectronics and ST-Ericsson embedded
 	  Nomadik series.
 
+config MFD_STM32_DFSDM
+	tristate "ST Microelectronics STM32 DFSDM"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select MFD_CORE
+	select REGMAP
+	select REGMAP_MMIO
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..1f095e5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
 
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
+
+obj-$(CONFIG_MFD_STM32_DFSDM)	+= stm32-dfsdm.o
\ No newline at end of file
diff --git a/drivers/mfd/stm32-dfsdm-reg.h b/drivers/mfd/stm32-dfsdm-reg.h
new file mode 100644
index 0000000..05ff702
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm-reg.h
@@ -0,0 +1,220 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * 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.
+ */
+
+#ifndef MDF_STM32_DFSDM_REG_H
+#define MDF_STM32_DFSDM_REG_H
+
+#include <linux/bitfield.h>
+/*
+ * Channels definitions
+ */
+#define DFSDM_CHCFGR1(y)  ((y) * 0x20 + 0x00)
+#define DFSDM_CHCFGR2(y)  ((y) * 0x20 + 0x04)
+#define DFSDM_AWSCDR(y)   ((y) * 0x20 + 0x08)
+#define DFSDM_CHWDATR(y)  ((y) * 0x20 + 0x0C)
+#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
+
+/* CHCFGR1: Channel configuration register 1 */
+#define DFSDM_CHCFGR1_SITP_MASK     GENMASK(1, 0)
+#define DFSDM_CHCFGR1_SITP(v)       FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
+#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
+#define DFSDM_CHCFGR1_SPICKSEL(v)   FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
+#define DFSDM_CHCFGR1_SCDEN_MASK    BIT(5)
+#define DFSDM_CHCFGR1_SCDEN(v)      FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
+#define DFSDM_CHCFGR1_CKABEN_MASK   BIT(6)
+#define DFSDM_CHCFGR1_CKABEN(v)     FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
+#define DFSDM_CHCFGR1_CHEN_MASK     BIT(7)
+#define DFSDM_CHCFGR1_CHEN(v)       FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
+#define DFSDM_CHCFGR1_CHINSEL_MASK  BIT(8)
+#define DFSDM_CHCFGR1_CHINSEL(v)    FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
+#define DFSDM_CHCFGR1_DATMPX_MASK   GENMASK(13, 12)
+#define DFSDM_CHCFGR1_DATMPX(v)     FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
+#define DFSDM_CHCFGR1_DATPACK_MASK  GENMASK(15, 14)
+#define DFSDM_CHCFGR1_DATPACK(v)    FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
+#define DFSDM_CHCFGR1_CKOUTDIV(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
+#define DFSDM_CHCFGR1_CKOUTSRC(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
+#define DFSDM_CHCFGR1_DFSDMEN_MASK  BIT(31)
+#define DFSDM_CHCFGR1_DFSDMEN(v)    FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
+
+/* CHCFGR2: Channel configuration register 2 */
+#define DFSDM_CHCFGR2_DTRBS_MASK    GENMASK(7, 3)
+#define DFSDM_CHCFGR2_DTRBS(v)      FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
+#define DFSDM_CHCFGR2_OFFSET_MASK   GENMASK(31, 8)
+#define DFSDM_CHCFGR2_OFFSET(v)     FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
+
+/* AWSCDR: Channel analog watchdog and short circuit detector */
+#define DFSDM_AWSCDR_SCDT_MASK    GENMASK(7, 0)
+#define DFSDM_AWSCDR_SCDT(v)      FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
+#define DFSDM_AWSCDR_BKSCD_MASK   GENMASK(15, 12)
+#define DFSDM_AWSCDR_BKSCD(v)	  FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
+#define DFSDM_AWSCDR_AWFOSR_MASK  GENMASK(20, 16)
+#define DFSDM_AWSCDR_AWFOSR(v)    FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
+#define DFSDM_AWSCDR_AWFORD_MASK  GENMASK(23, 22)
+#define DFSDM_AWSCDR_AWFORD(v)    FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
+
+/*
+ * Filters definitions
+ */
+#define DFSDM_FILTER_BASE_ADR		0x100
+#define DFSDM_FILTER_REG_MASK		0x7F
+#define DFSDM_FILTER_X_BASE_ADR(x)	((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
+
+#define DFSDM_CR1(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x00)
+#define DFSDM_CR2(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x04)
+#define DFSDM_ISR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x08)
+#define DFSDM_ICR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x0C)
+#define DFSDM_JCHGR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x10)
+#define DFSDM_FCR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x14)
+#define DFSDM_JDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x18)
+#define DFSDM_RDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x1C)
+#define DFSDM_AWHTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x20)
+#define DFSDM_AWLTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x24)
+#define DFSDM_AWSR(x)    (DFSDM_FILTER_X_BASE_ADR(x)  + 0x28)
+#define DFSDM_AWCFR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x2C)
+#define DFSDM_EXMAX(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x30)
+#define DFSDM_EXMIN(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x34)
+#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x)  + 0x38)
+
+/* CR1 Control register 1 */
+#define DFSDM_CR1_DFEN_MASK	BIT(0)
+#define DFSDM_CR1_DFEN(v)	FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
+#define DFSDM_CR1_JSWSTART_MASK	BIT(1)
+#define DFSDM_CR1_JSWSTART(v)	FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
+#define DFSDM_CR1_JSYNC_MASK	BIT(3)
+#define DFSDM_CR1_JSYNC(v)	FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
+#define DFSDM_CR1_JSCAN_MASK	BIT(4)
+#define DFSDM_CR1_JSCAN(v)	FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
+#define DFSDM_CR1_JDMAEN_MASK	BIT(5)
+#define DFSDM_CR1_JDMAEN(v)	FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
+#define DFSDM_CR1_JEXTSEL_MASK	GENMASK(12, 8)
+#define DFSDM_CR1_JEXTSEL(v)	FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
+#define DFSDM_CR1_JEXTEN_MASK	GENMASK(14, 13)
+#define DFSDM_CR1_JEXTEN(v)	FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
+#define DFSDM_CR1_RSWSTART_MASK	BIT(17)
+#define DFSDM_CR1_RSWSTART(v)	FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
+#define DFSDM_CR1_RCONT_MASK	BIT(18)
+#define DFSDM_CR1_RCONT(v)	FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
+#define DFSDM_CR1_RSYNC_MASK	BIT(19)
+#define DFSDM_CR1_RSYNC(v)	FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
+#define DFSDM_CR1_RDMAEN_MASK	BIT(21)
+#define DFSDM_CR1_RDMAEN(v)	FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
+#define DFSDM_CR1_RCH_MASK	GENMASK(26, 24)
+#define DFSDM_CR1_RCH(v)	FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
+#define DFSDM_CR1_FAST_MASK	BIT(29)
+#define DFSDM_CR1_FAST(v)	FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
+#define DFSDM_CR1_AWFSEL_MASK	BIT(30)
+#define DFSDM_CR1_AWFSEL(v)	FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
+
+/* CR2: Control register 2 */
+#define DFSDM_CR2_IE_MASK	GENMASK(6, 0)
+#define DFSDM_CR2_IE(v)		FIELD_PREP(DFSDM_CR2_IE_MASK, v)
+#define DFSDM_CR2_JEOCIE_MASK	BIT(0)
+#define DFSDM_CR2_JEOCIE(v)	FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
+#define DFSDM_CR2_REOCIE_MASK	BIT(1)
+#define DFSDM_CR2_REOCIE(v)	FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
+#define DFSDM_CR2_JOVRIE_MASK	BIT(2)
+#define DFSDM_CR2_JOVRIE(v)	FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
+#define DFSDM_CR2_ROVRIE_MASK	BIT(3)
+#define DFSDM_CR2_ROVRIE(v)	FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
+#define DFSDM_CR2_AWDIE_MASK	BIT(4)
+#define DFSDM_CR2_AWDIE(v)	FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
+#define DFSDM_CR2_SCDIE_MASK	BIT(5)
+#define DFSDM_CR2_SCDIE(v)	FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
+#define DFSDM_CR2_CKABIE_MASK	BIT(6)
+#define DFSDM_CR2_CKABIE(v)	FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
+#define DFSDM_CR2_EXCH_MASK	GENMASK(15, 8)
+#define DFSDM_CR2_EXCH(v)	FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
+#define DFSDM_CR2_AWDCH_MASK	GENMASK(23, 16)
+#define DFSDM_CR2_AWDCH(v)	FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
+
+/* ISR: Interrupt status register */
+#define DFSDM_ISR_JEOCF_MASK	BIT(0)
+#define DFSDM_ISR_JEOCF(v)	FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
+#define DFSDM_ISR_REOCF_MASK	BIT(1)
+#define DFSDM_ISR_REOCF(v)	FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
+#define DFSDM_ISR_JOVRF_MASK	BIT(2)
+#define DFSDM_ISR_JOVRF(v)	FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
+#define DFSDM_ISR_ROVRF_MASK	BIT(3)
+#define DFSDM_ISR_ROVRF(v)	FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
+#define DFSDM_ISR_AWDF_MASK	BIT(4)
+#define DFSDM_ISR_AWDF(v)	FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
+#define DFSDM_ISR_JCIP_MASK	BIT(13)
+#define DFSDM_ISR_JCIP(v)	FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
+#define DFSDM_ISR_RCIP_MASK	BIT(14)
+#define DFSDM_ISR_RCIP(v)	FIELD_PREP(DFSDM_ISR_RCIP, v)
+#define DFSDM_ISR_CKABF_MASK	GENMASK(23, 16)
+#define DFSDM_ISR_CKABF(v)	FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
+#define DFSDM_ISR_SCDF_MASK	GENMASK(31, 24)
+#define DFSDM_ISR_SCDF(v)	FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
+
+/* ICR: Interrupt flag clear register */
+#define DFSDM_ICR_CLRJOVRF_MASK	      BIT(2)
+#define DFSDM_ICR_CLRJOVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
+#define DFSDM_ICR_CLRROVRF_MASK	      BIT(3)
+#define DFSDM_ICR_CLRROVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_MASK	      GENMASK(23, 16)
+#define DFSDM_ICR_CLRCKABF(v)	      FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
+#define DFSDM_ICR_CLRCKABF_CH(v, y)   \
+			   (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
+#define DFSDM_ICR_CLRSCDF_MASK	      GENMASK(31, 24)
+#define DFSDM_ICR_CLRSCDF(v)	      FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
+#define DFSDM_ICR_CLRSCDF_CH_MASK(y)  BIT(24 + (y))
+#define DFSDM_ICR_CLRSCDF_CH(v, y)    \
+			       (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
+
+/* FCR: Filter control register */
+#define DFSDM_FCR_IOSR_MASK	GENMASK(7, 0)
+#define DFSDM_FCR_IOSR(v)	FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
+#define DFSDM_FCR_FOSR_MASK	GENMASK(25, 16)
+#define DFSDM_FCR_FOSR(v)	FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
+#define DFSDM_FCR_FORD_MASK	GENMASK(31, 29)
+#define DFSDM_FCR_FORD(v)	FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
+
+/* RDATAR: Filter data register for regular channel */
+#define DFSDM_DATAR_CH_MASK	GENMASK(2, 0)
+#define DFSDM_DATAR_DATA_OFFSET 8
+#define DFSDM_DATAR_DATA_MASK	GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
+
+/* AWLTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWLTR_BKAWL_MASK	GENMASK(3, 0)
+#define DFSDM_AWLTR_BKAWL(v)	FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
+#define DFSDM_AWLTR_AWLT_MASK	GENMASK(31, 8)
+#define DFSDM_AWLTR_AWLT(v)	FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
+
+/* AWHTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWHTR_BKAWH_MASK	GENMASK(3, 0)
+#define DFSDM_AWHTR_BKAWH(v)	FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
+#define DFSDM_AWHTR_AWHT_MASK	GENMASK(31, 8)
+#define DFSDM_AWHTR_AWHT(v)	FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
+
+/* AWSR: Filter watchdog status register */
+#define DFSDM_AWSR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWSR_AWLTF(v)	FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
+#define DFSDM_AWSR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWSR_AWHTF(v)	FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
+
+/* AWCFR: Filter watchdog status register */
+#define DFSDM_AWCFR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWCFR_AWLTF(v)	FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
+#define DFSDM_AWCFR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWCFR_AWHTF(v)	FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
+
+#endif
diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
new file mode 100644
index 0000000..81ca29c
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm.c
@@ -0,0 +1,1044 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#include "stm32-dfsdm-reg.h"
+
+#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
+		WARN_ON(regmap_update_bits(regm, reg, mask, val))
+
+#define DFSDM_REG_READ(regm, reg, val) \
+		WARN_ON(regmap_read(regm, reg, val))
+
+#define DFSDM_REG_WRITE(regm, reg, val) \
+		WARN_ON(regmap_write(regm, reg, val))
+
+#define STM32H7_DFSDM_NUM_FILTERS	4
+#define STM32H7_DFSDM_NUM_INPUTS	8
+
+enum dfsdm_clkout_src {
+	DFSDM_CLK,
+	AUDIO_CLK
+};
+
+struct stm32_dev_data {
+	const struct stm32_dfsdm dfsdm;
+	const struct regmap_config *regmap_cfg;
+};
+
+struct dfsdm_priv;
+
+struct filter_params {
+	unsigned int id;
+	int irq;
+	struct stm32_dfsdm_fl_event event;
+	u32 event_mask;
+	struct dfsdm_priv *priv; /* Cross ref for context */
+	unsigned int ext_ch_mask;
+	unsigned int scan_ch;
+};
+
+struct ch_params {
+	struct stm32_dfsdm_channel ch;
+};
+
+struct dfsdm_priv {
+	struct platform_device *pdev;
+	struct stm32_dfsdm dfsdm;
+
+	spinlock_t lock; /* Used for resource sharing & interrupt lock */
+
+	/* Filters */
+	struct filter_params *filters;
+	unsigned int free_filter_mask;
+	unsigned int scd_filter_mask;
+	unsigned int ckab_filter_mask;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *channels;
+	unsigned int free_channel_mask;
+	atomic_t n_active_ch;
+
+	/* Clock */
+	struct clk *clk;
+	struct clk *aclk;
+	unsigned int clkout_div;
+	unsigned int clkout_freq_req;
+
+	/* Registers*/
+	void __iomem *base;
+	struct regmap *regmap;
+	phys_addr_t phys_base;
+};
+
+/*
+ * Common
+ */
+static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+	if (reg < DFSDM_FILTER_BASE_ADR)
+		return false;
+
+	/*
+	 * Mask is done on register to avoid to list registers of all them
+	 * filter instances.
+	 */
+	switch (reg & DFSDM_FILTER_REG_MASK) {
+	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
+		return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = sizeof(u32),
+	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
+	.volatile_reg = stm32_dfsdm_volatile_reg,
+	.fast_io = true,
+};
+
+static const struct stm32_dev_data stm32h7_data = {
+	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
+	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
+	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+static int stm32_dfsdm_start_dfsdm(struct dfsdm_priv *priv)
+{
+	int ret;
+	struct device *dev = &priv->pdev->dev;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		ret = clk_prepare_enable(priv->clk);
+		if (ret < 0) {
+			dev_err(dev, "Failed to start clock\n");
+			return ret;
+		}
+		if (priv->aclk) {
+			ret = clk_prepare_enable(priv->aclk);
+			if (ret < 0) {
+				dev_err(dev, "Failed to start audio clock\n");
+				return ret;
+			}
+		}
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(priv->clkout_div));
+
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(1));
+	}
+
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+}
+
+static void stm32_dfsdm_stop_dfsdm(struct dfsdm_priv *priv)
+{
+	if (atomic_dec_and_test(&priv->n_active_ch)) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(0));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(0));
+		clk_disable_unprepare(priv->clk);
+		if (priv->aclk)
+			clk_disable_unprepare(priv->aclk);
+	}
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+}
+
+static unsigned int stm32_dfsdm_get_clkout_divider(struct dfsdm_priv *priv,
+						   unsigned long rate)
+{
+	unsigned int delta, div;
+
+	/* div = 0 disables the clockout */
+	if (!priv->clkout_freq_req)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(rate, priv->clkout_freq_req);
+
+	delta = rate - (priv->clkout_freq_req * div);
+	if (delta)
+		dev_warn(&priv->pdev->dev,
+			 "clkout not accurate. delta (Hz): %d\n", delta);
+
+	dev_dbg(&priv->pdev->dev, "%s: clk: %lu (Hz), div %u\n",
+		__func__, rate, div);
+
+	return (div - 1);
+}
+
+/*
+ * Filters
+ */
+
+static int stm32_dfsdm_clear_event(struct dfsdm_priv *priv, unsigned int fl_id,
+				   unsigned int event, int mask)
+{
+	int val;
+
+	switch (event) {
+	case DFSDM_EVENT_INJ_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_JDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_REG_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_RDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_INJ_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRJOVRF_MASK,
+				  DFSDM_ICR_CLRJOVRF_MASK);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRROVRF_MASK,
+				  DFSDM_ICR_CLRROVRF_MASK);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct filter_params *params = arg;
+	unsigned int status;
+	struct dfsdm_priv *priv = params->priv;
+	unsigned int event_mask = params->event_mask;
+
+	DFSDM_REG_READ(priv->regmap, DFSDM_ISR(params->id), &status);
+
+	if (status & DFSDM_ISR_JOVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_INJ_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_REG_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_JEOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_INJ_EOC, 0);
+	}
+
+	if (status & DFSDM_ISR_REOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_REG_EOC, 0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_configure_reg_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_regular *params)
+{
+	unsigned int ch_id = params->ch_src;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
+			  DFSDM_CR1_RCH(ch_id));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_FAST_MASK,
+			  DFSDM_CR1_FAST(params->fast_mode));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCONT_MASK,
+			  DFSDM_CR1_RCONT(params->cont_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RDMAEN_MASK,
+			  DFSDM_CR1_RDMAEN(params->dma_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSYNC_MASK,
+			  DFSDM_CR1_RSYNC(params->sync_mode));
+
+	priv->filters[fl_id].scan_ch = BIT(ch_id);
+}
+
+static void stm32_dfsdm_configure_inj_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_injected *params)
+{
+	int val;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSCAN_MASK,
+			  DFSDM_CR1_JSCAN(params->scan_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JDMAEN_MASK,
+			  DFSDM_CR1_JDMAEN(params->dma_mode));
+
+	val = (params->trigger == DFSDM_FILTER_EXT_TRIGGER) ?
+	      params->trig_src : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+			  DFSDM_CR1_JEXTSEL_MASK,
+			  DFSDM_CR1_JEXTSEL(val));
+
+	val = (params->trigger == DFSDM_FILTER_SYNC_TRIGGER) ? 1 : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSYNC_MASK,
+			  DFSDM_CR1_JSYNC(val));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JEXTEN_MASK,
+			  DFSDM_CR1_JEXTEN(params->trig_pol));
+	priv->filters[fl_id].scan_ch = params->ch_group;
+
+	DFSDM_REG_WRITE(priv->regmap, DFSDM_JCHGR(fl_id), params->ch_group);
+}
+
+/**
+ * stm32_dfsdm_configure_filter - Configure filter.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *fl_cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct stm32_dfsdm_sinc_filter *sparams = &fl_cfg->sinc_params;
+
+	dev_dbg(&priv->pdev->dev, "%s:config filter %d\n", __func__, fl_id);
+
+	/* Average integrator oversampling */
+	if ((!fl_cfg->int_oversampling) ||
+	    (fl_cfg->int_oversampling > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid integrator oversampling %d\n",
+			fl_cfg->int_oversampling);
+		return -EINVAL;
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+			  DFSDM_FCR_IOSR((fl_cfg->int_oversampling - 1)));
+
+	/* Oversamplings and filter*/
+	if ((!sparams->oversampling) ||
+	    (sparams->oversampling > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid oversampling %d\n",
+			sparams->oversampling);
+		return -EINVAL;
+	}
+
+	if (sparams->order > DFSDM_SINC5_ORDER) {
+		dev_err(&priv->pdev->dev, "invalid filter order %d\n",
+			sparams->order);
+		return -EINVAL;
+	}
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
+			  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
+			  DFSDM_FCR_FORD(sparams->order));
+
+	/* Conversion */
+	if (fl_cfg->inj_params)
+		stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
+	else if (fl_cfg->reg_params)
+		stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
+
+	priv->filters[fl_id].event = fl_cfg->event;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
+
+/**
+ * stm32_dfsdm_start_filter - Start filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:start filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(1));
+
+	if (conv == DFSDM_FILTER_REG_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_RSWSTART_MASK,
+				  DFSDM_CR1_RSWSTART(1));
+	} else if (conv == DFSDM_FILTER_SW_INJ_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_JSWSTART_MASK,
+				  DFSDM_CR1_JSWSTART(1));
+	}
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_filter);
+
+/**
+ * stm32_dfsdm_stop_filter - Stop filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ */
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:stop filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(0));
+	priv->filters[fl_id].scan_ch = 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
+
+/**
+ * stm32_dfsdm_read_fl_conv - Read filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @type: Regular or injected conversion.
+ */
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	int reg_v, offset;
+
+	if (type == DFSDM_FILTER_REG_CONV)
+		offset = DFSDM_RDATAR(fl_id);
+	else
+		offset = DFSDM_JDATAR(fl_id);
+
+	DFSDM_REG_READ(priv->regmap, offset, &reg_v);
+
+	*ch_id = reg_v & DFSDM_DATAR_CH_MASK;
+	*val = reg_v & DFSDM_DATAR_DATA_MASK;
+}
+EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
+
+/**
+ * stm32_dfsdm_get_filter - Get filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter instance to reserve.
+ *
+ * Reserves a DFSDM filter resource.
+ */
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct device *dev = &priv->pdev->dev;
+
+	spin_lock(&priv->lock);
+	if (!(priv->free_filter_mask & BIT(fl_id))) {
+		spin_unlock(&priv->lock);
+		dev_err(dev, "filter resource %d available\n", fl_id);
+		return -EBUSY;
+	}
+	priv->free_filter_mask &= ~BIT(fl_id);
+
+	spin_unlock(&priv->lock);
+
+	dev_dbg(dev, "%s: new mask %#x\n", __func__, priv->free_filter_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_filter);
+
+/**
+ * stm32_dfsdm_release_filter - Release filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ *
+ * Free the DFSDM filter resource.
+ */
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_filter_mask |= BIT(fl_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_filter);
+
+/**
+ * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type.
+ */
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	if (conv == DFSDM_FILTER_REG_CONV)
+		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
+	else
+		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
+}
+
+/**
+ * stm32_dfsdm_register_fl_event - Register filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function enables associated IRQ.
+ */
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int ret, i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	/* Clear interrupt before enable them */
+	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
+	if (ret < 0)
+		return ret;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Enable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(1));
+		}
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(1));
+		priv->scd_filter_mask |= BIT(fl_id);
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(1));
+		}
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(1));
+		priv->ckab_filter_mask |= BIT(fl_id);
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
+	}
+	priv->filters[fl_id].event_mask |= event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
+
+/**
+ * stm32_dfsdm_unregister_fl_event - Unregister filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function disables associated IRQ.
+ */
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Disable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(0));
+		}
+		priv->scd_filter_mask &= ~BIT(fl_id);
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(0));
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(0));
+		}
+		priv->ckab_filter_mask &= ~BIT(fl_id);
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(0));
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
+	}
+
+	priv->filters[fl_id].event_mask &= ~event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
+
+/*
+ * Channels
+ */
+static void stm32_dfsdm_init_channel(struct dfsdm_priv *priv,
+				     struct stm32_dfsdm_channel *ch)
+{
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATMPX_MASK,
+			  DFSDM_CHCFGR1_DATMPX(ch->type.source));
+	if (ch->type.source == DFSDM_CHANNEL_EXTERNAL_INPUTS) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SITP_MASK,
+				  DFSDM_CHCFGR1_SITP(ch->serial_if.type));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SPICKSEL_MASK,
+				DFSDM_CHCFGR1_SPICKSEL(ch->serial_if.spi_clk));
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATPACK_MASK,
+			  DFSDM_CHCFGR1_DATPACK(ch->type.DataPacking));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_CHINSEL_MASK,
+			  DFSDM_CHCFGR1_CHINSEL(ch->serial_if.pins));
+}
+
+/**
+ * stm32_dfsdm_start_channel - Configure and activate DFSDM channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch: Filter id.
+ * @cfg: Filter configuration.
+ */
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct regmap *reg = priv->regmap;
+	int ret;
+
+	dev_dbg(&priv->pdev->dev, "%s: for channel %d\n", __func__, ch_id);
+
+	ret = stm32_dfsdm_start_dfsdm(priv);
+	if (ret < 0)
+		return ret;
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_DTRBS_MASK,
+			  DFSDM_CHCFGR2_DTRBS(cfg->right_bit_shift));
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_OFFSET_MASK,
+			  DFSDM_CHCFGR2_OFFSET(cfg->offset));
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR1(ch_id), DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(1));
+
+	/* Clear absence detection IRQ */
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(0),
+			  DFSDM_ICR_CLRCKABF_CH_MASK(ch_id),
+			  DFSDM_ICR_CLRCKABF_CH(1, ch_id));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_channel);
+
+/**
+ * stm32_dfsdm_stop_channel - Deactivate channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ */
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:for channel %d\n", __func__, ch_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CKABEN_MASK, DFSDM_CHCFGR1_CKABEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_SCDEN_MASK, DFSDM_CHCFGR1_SCDEN(0));
+
+	stm32_dfsdm_stop_dfsdm(priv);
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
+
+/**
+ * stm32_dfsdm_get_channel - Get channel instance.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @ch: DFSDM channel hardware parameters.
+ *
+ * Reserve DFSDM channel resource.
+ */
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned int id = ch->id;
+
+	dev_dbg(&priv->pdev->dev, "%s:get channel %d\n", __func__, id);
+
+	if (id >= priv->dfsdm.max_channels) {
+		dev_err(&priv->pdev->dev, "channel (%d) is not valid\n", id);
+		return -EINVAL;
+	}
+
+	if ((ch->type.source != DFSDM_CHANNEL_EXTERNAL_INPUTS) &
+	    (ch->serial_if.spi_clk != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) &
+	    (!priv->clkout_freq_req)) {
+		dev_err(&priv->pdev->dev, "clkout not present\n");
+		return -EINVAL;
+	}
+
+	spin_lock(&priv->lock);
+	if (!(BIT(id) & priv->free_channel_mask)) {
+		spin_unlock(&priv->lock);
+		dev_err(&priv->pdev->dev, "channel (%d) already in use\n", id);
+		return -EBUSY;
+	}
+
+	priv->free_channel_mask &= ~BIT(id);
+	priv->channels[id] = *ch;
+	spin_unlock(&priv->lock);
+
+	dev_dbg(&priv->pdev->dev, "%s: new mask %#x\n", __func__,
+		priv->free_channel_mask);
+
+	/**
+	 * Check clock constrainst between clkout and either
+	 * dfsdm/audio clock:
+	 * - In SPI mode (clkout is used): Fclk >= 4 * Fclkout
+	 *   (e.g. CKOUTDIV >= 3)
+	 * - In mancherster mode: Fclk >= 6 * Fclkout
+	 */
+	switch (ch->serial_if.type) {
+	case DFSDM_CHANNEL_SPI_RISING:
+	case DFSDM_CHANNEL_SPI_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 3)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 3\n");
+		break;
+	case DFSDM_CHANNEL_MANCHESTER_RISING:
+	case DFSDM_CHANNEL_MANCHESTER_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 5)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 5\n");
+		break;
+	}
+
+	stm32_dfsdm_init_channel(priv, ch);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_channel);
+
+/**
+ * stm32_dfsdm_release_channel - Release channel instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ *
+ * Free the DFSDM channel resource.
+ */
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_channel_mask |= BIT(ch_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_channel);
+
+/**
+ * stm32_dfsdm_get_clk_out_rate - get clkout frequency.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @rate: clock out rate in Hz.
+ *
+ * Provide output frequency used for external ADC.
+ * return EINVAL if clockout is not used else return 0.
+ */
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm, unsigned long *rate)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long int clk_rate;
+
+	if (!priv->clkout_div)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(priv->aclk ? priv->aclk : priv->clk);
+	*rate = clk_rate / (priv->clkout_div + 1);
+	dev_dbg(&priv->pdev->dev, "%s: clkout: %ld (Hz)\n", __func__, *rate);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_clk_out_rate);
+
+static int stm32_dfsdm_parse_of(struct platform_device *pdev,
+				struct dfsdm_priv *priv)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct resource *res;
+	int ret, val;
+
+	if (!node)
+		return -EINVAL;
+
+	/* Get resources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get memory resource\n");
+		return -ENODEV;
+	}
+	priv->phys_base = res->start;
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	ret = of_property_read_u32(node, "st,clkout-freq", &val);
+	if (!ret) {
+		if (!val) {
+			dev_err(&priv->pdev->dev,
+				"st,clkout-freq cannot be 0\n");
+			return -EINVAL;
+		}
+		priv->clkout_freq_req = val;
+	} else if (ret != -EINVAL) {
+		dev_err(&priv->pdev->dev, "Failed to get st,clkout-freq\n");
+		return ret;
+	}
+
+	/* Source clock */
+	priv->clk = devm_clk_get(&pdev->dev, "dfsdm_clk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
+		return -EINVAL;
+	}
+
+	priv->aclk = devm_clk_get(&pdev->dev, "audio_clk");
+	if (IS_ERR(priv->aclk))
+		priv->aclk = NULL;
+
+	return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+	{
+		.compatible = "st,stm32h7-dfsdm",
+		.data = &stm32h7_data
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
+
+static int stm32_dfsdm_remove(struct platform_device *pdev)
+{
+	of_platform_depopulate(&pdev->dev);
+
+	return 0;
+}
+
+static int stm32_dfsdm_probe(struct platform_device *pdev)
+{
+	struct dfsdm_priv *priv;
+	struct device_node *pnode = pdev->dev.of_node;
+	const struct of_device_id *of_id;
+	const struct stm32_dev_data *dev_data;
+	enum dfsdm_clkout_src clk_src;
+	int ret, i;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdev = pdev;
+
+	/* Populate data structure depending on compatibility */
+	of_id = of_match_node(stm32_dfsdm_of_match, pnode);
+	if (!of_id->data) {
+		dev_err(&pdev->dev, "Data associated to device is missing\n");
+		return -EINVAL;
+	}
+
+	dev_data = (const struct stm32_dev_data *)of_id->data;
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
+					    dev_data->regmap_cfg);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	priv->dfsdm = dev_data->dfsdm;
+
+	priv->filters = devm_kcalloc(&pdev->dev, dev_data->dfsdm.max_filters,
+				     sizeof(*priv->filters), GFP_KERNEL);
+	if (IS_ERR(priv->filters)) {
+		ret = PTR_ERR(priv->filters);
+		goto probe_err;
+	}
+
+	for (i = 0; i < dev_data->dfsdm.max_filters; i++) {
+		struct filter_params *params = &priv->filters[i];
+
+		params->id = i;
+		params->irq = platform_get_irq(pdev, i);
+		if (params->irq < 0) {
+			dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+			ret = params->irq;
+			goto probe_err;
+		}
+
+		ret = devm_request_irq(&pdev->dev, params->irq, stm32_dfsdm_irq,
+				       0, dev_name(&pdev->dev), params);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to register interrupt\n");
+			goto probe_err;
+		}
+
+		params->priv = priv;
+	}
+
+	priv->channels = devm_kcalloc(&pdev->dev, priv->dfsdm.max_channels,
+				      sizeof(*priv->channels), GFP_KERNEL);
+	if (IS_ERR(priv->channels)) {
+		ret = PTR_ERR(priv->channels);
+		goto probe_err;
+	}
+	priv->free_filter_mask = BIT(priv->dfsdm.max_filters) - 1;
+	priv->free_channel_mask = BIT(priv->dfsdm.max_channels) - 1;
+
+	platform_set_drvdata(pdev, &priv->dfsdm);
+	spin_lock_init(&priv->lock);
+
+	priv->clkout_div = stm32_dfsdm_get_clkout_divider(priv,
+						    clk_get_rate(priv->clk));
+
+	ret = of_platform_populate(pnode, NULL, NULL, &pdev->dev);
+	if (ret < 0)
+		goto probe_err;
+
+	clk_src = priv->aclk ? AUDIO_CLK : DFSDM_CLK;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+			  DFSDM_CHCFGR1_CKOUTSRC_MASK,
+			  DFSDM_CHCFGR1_CKOUTSRC(clk_src));
+	return 0;
+
+probe_err:
+	return ret;
+}
+
+static struct platform_driver stm32_dfsdm_driver = {
+	.probe = stm32_dfsdm_probe,
+	.remove = stm32_dfsdm_remove,
+	.driver = {
+		.name = "stm32-dfsdm",
+		.of_match_table = stm32_dfsdm_of_match,
+	},
+};
+
+module_platform_driver(stm32_dfsdm_driver);
+
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/stm32-dfsdm.h b/include/linux/mfd/stm32-dfsdm.h
new file mode 100644
index 0000000..f6eb788
--- /dev/null
+++ b/include/linux/mfd/stm32-dfsdm.h
@@ -0,0 +1,324 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver API
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * 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.
+ */
+#ifndef MDF_STM32_DFSDM_H
+#define MDF_STM32_DFSDM_H
+
+/*
+ * Channel definitions
+ */
+#define DFSDM_CHANNEL_0    BIT(0)
+#define DFSDM_CHANNEL_1    BIT(1)
+#define DFSDM_CHANNEL_2    BIT(2)
+#define DFSDM_CHANNEL_3    BIT(3)
+#define DFSDM_CHANNEL_4    BIT(4)
+#define DFSDM_CHANNEL_5    BIT(5)
+#define DFSDM_CHANNEL_6    BIT(6)
+#define DFSDM_CHANNEL_7    BIT(7)
+
+/* DFSDM channel input data packing */
+enum stm32_dfsdm_data_packing {
+	DFSDM_CHANNEL_STANDARD_MODE,    /* Standard data packing mode */
+	DFSDM_CHANNEL_INTERLEAVED_MODE, /* Interleaved data packing mode */
+	DFSDM_CHANNEL_DUAL_MODE         /* Dual data packing mode */
+};
+
+/* DFSDM channel input multiplexer */
+enum stm32_dfsdm_input_multiplexer {
+	DFSDM_CHANNEL_EXTERNAL_INPUTS,    /* Data taken from external inputs */
+	DFSDM_CHANNEL_INTERNAL_ADC,       /* Data taken from internal ADC */
+	DFSDM_CHANNEL_INTERNAL_REGISTER,  /* Data taken from register */
+};
+
+/* DFSDM channel serial interface type */
+enum stm32_dfsdm_serial_in_type {
+	DFSDM_CHANNEL_SPI_RISING,         /* SPI with rising edge */
+	DFSDM_CHANNEL_SPI_FALLING,        /* SPI with falling edge */
+	DFSDM_CHANNEL_MANCHESTER_RISING,  /* Manchester with rising edge */
+	DFSDM_CHANNEL_MANCHESTER_FALLING, /* Manchester with falling edge */
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+	/* External SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+	/* Internal SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+	/* Internal SPI clock divided by 2, falling edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+	/* Internal SPI clock divided by 2, rising edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+/* DFSDM channel input pins */
+enum stm32_dfsdm_serial_in_select {
+	/* Serial input taken from pins of the same channel (y) */
+	DFSDM_CHANNEL_SAME_CHANNEL_PINS,
+	/* Serial input taken from pins of the following channel (y + 1)*/
+	DFSDM_CHANNEL_NEXT_CHANNEL_PINS,
+};
+
+/**
+ * struct stm32_dfsdm_input_type - DFSDM channel init structure definition.
+ * @DataPacking: Standard, interleaved or dual mode for internal register.
+ * @source: channel source: internal DAC, serial input or memory.
+ */
+struct stm32_dfsdm_input_type {
+	enum stm32_dfsdm_data_packing DataPacking;
+	enum stm32_dfsdm_input_multiplexer source;
+};
+
+/**
+ * struct stm32_dfsdm_serial_if - DFSDM serial interface parameters.
+ * @type:	Serial interface type.
+ * @spi_clk:	SPI clock source.
+ * @pins:	select serial interface associated to the channel
+ */
+struct stm32_dfsdm_serial_if {
+	enum stm32_dfsdm_serial_in_type type;
+	enum stm32_dfsdm_spi_clk_src spi_clk;
+	enum stm32_dfsdm_serial_in_select pins;
+};
+
+/**
+ * struct stm32_dfsdm_channel - DFSDM channel hardware parameters.
+ * @id:		DFSDM channel identifier.
+ * @type:	DFSDM channel input parameters.
+ * @serial_if:	DFSDM channel serial interface parameters.
+ *		Mandatory for DFSDM_CHANNEL_EXTERNAL_INPUTS.
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;
+	struct stm32_dfsdm_input_type type;
+	struct stm32_dfsdm_serial_if serial_if;
+};
+
+/**
+ * struct stm32_dfsdm_ch_cfg - DFSDM channel config.
+ * @offset:		DFSDM channel 24 bit calibration offset.
+ * @right_bit_shift:	DFSDM channel right bit shift of the data result.
+ */
+struct stm32_dfsdm_ch_cfg {
+	unsigned int offset;
+	unsigned int right_bit_shift;
+};
+
+/*
+ * Filter definitions
+ */
+
+#define DFSDM_MIN_INT_OVERSAMPLING 1
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+#define DFSDM_MIN_FL_OVERSAMPLING 1
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+enum stm32_dfsdm_events {
+	DFSDM_EVENT_INJ_EOC =	BIT(0), /* Injected end of conversion event */
+	DFSDM_EVENT_REG_EOC =	BIT(1), /* Regular end of conversion event */
+	DFSDM_EVENT_INJ_XRUN =	BIT(2), /* Injected conversion overrun event */
+	DFSDM_EVENT_REG_XRUN =	BIT(3), /* Regular conversion overrun event */
+	DFSDM_EVENT_AWD =	BIT(4), /* Analog watchdog event */
+	DFSDM_EVENT_SCD =	BIT(5), /* Short circuit detector event */
+	DFSDM_EVENT_CKA =	BIT(6), /* Clock abscence detection event */
+};
+
+#define STM32_DFSDM_EVENT_MASK 0x3F
+
+/* DFSDM filter order  */
+enum stm32_dfsdm_sinc_order {
+	DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
+	DFSDM_SINC1_ORDER,    /* Sinc 1 filter type */
+	DFSDM_SINC2_ORDER,    /* Sinc 2 filter type */
+	DFSDM_SINC3_ORDER,    /* Sinc 3 filter type */
+	DFSDM_SINC4_ORDER,    /* Sinc 4 filter type (N.A. for watchdog) */
+	DFSDM_SINC5_ORDER,    /* Sinc 5 filter type (N.A. for watchdog) */
+	DFSDM_NB_SINC_ORDER,
+};
+
+/* DFSDM filter order */
+enum stm32_dfsdm_state {
+	DFSDM_DISABLE,
+	DFSDM_ENABLE,
+};
+
+/**
+ * struct stm32_dfsdm_sinc_filter - DFSDM Sinc filter structure definition
+ * @order: DFSM filter order.
+ * @oversampling: DFSDM filter oversampling:
+ *		  post processing filter: min = 1, max = 1024.
+ */
+struct stm32_dfsdm_sinc_filter {
+	enum stm32_dfsdm_sinc_order order;
+	unsigned int oversampling;
+};
+
+/* DFSDM filter conversion trigger */
+enum stm32_dfsdm_trigger {
+	DFSDM_FILTER_SW_TRIGGER,   /* Software trigger */
+	DFSDM_FILTER_SYNC_TRIGGER, /* Synchronous with DFSDM0 */
+	DFSDM_FILTER_EXT_TRIGGER,  /* External trigger (only for injected) */
+};
+
+/* DFSDM filter external trigger polarity */
+enum stm32_dfsdm_filter_ext_trigger_pol {
+	DFSDM_FILTER_EXT_TRIG_NO_TRIG,      /* Trigger disable */
+	DFSDM_FILTER_EXT_TRIG_RISING_EDGE,  /* Rising edge */
+	DFSDM_FILTER_EXT_TRIG_FALLING_EDGE, /* Falling edge */
+	DFSDM_FILTER_EXT_TRIG_BOTH_EDGES,   /* Rising and falling edges */
+};
+
+/* DFSDM filter conversion type */
+enum stm32_dfsdm_conv_type {
+	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
+	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
+	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
+};
+
+/* DFSDM filter regular synchronous mode */
+enum stm32_dfsdm_conv_rsync {
+	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
+	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
+};
+
+/**
+ * struct stm32_dfsdm_regular - DFSDM filter conversion parameters structure
+ * @ch_src:	Channel source from 0 to 7.
+ * @fast_mode:	Enable/disable fast mode for regular conversion.
+ * @dma_mode:	Enable/disable dma mode.
+ * @cont_mode	Enable/disable continuous conversion.
+ * @sync_mode	Enable/disable synchro mode.
+ */
+struct stm32_dfsdm_regular {
+	unsigned int ch_src;
+	bool fast_mode;
+	bool dma_mode;
+	bool cont_mode;
+	bool sync_mode;
+};
+
+/**
+ * struct stm32_dfsdm_injected - DFSDM filter  conversion parameters structure
+ * @trigger:	Trigger used to start injected conversion.
+ * @trig_src:	External trigger, 0 to 30 (refer to datasheet for details).
+ * @trig_pol:	External trigger edge: software, rising, falling or both.
+ * @scan_mode:	Enable/disable scan mode for injected conversion.
+ * @ch_group:	mask containing channels to scan ( set bit y to scan
+ *		channel y).
+ * @dma_mode:	DFSDM channel input parameters.
+ */
+struct stm32_dfsdm_injected {
+	enum stm32_dfsdm_trigger trigger;
+	unsigned int trig_src;
+	enum stm32_dfsdm_filter_ext_trigger_pol trig_pol;
+	bool scan_mode;
+	unsigned int ch_group;
+	bool dma_mode;
+};
+
+struct stm32_dfsdm;
+
+/**
+ * struct stm32_dfsdm_fl_event - DFSDM filters event
+ * @cb:	User event callback with parameters. be carful this function
+ *		is called under threaded IRQ context:
+ *			struct stm32_dfsdm *dfsdm: dfsdm handle,
+ *			unsigned int fl_id: filter id,
+ *			num stm32_dfsdm_events flag: event,
+ *			param: parameter associated to the event,
+ *			void *context: user context provided on registration.
+ * @context: User param to retrieve context.
+ */
+struct stm32_dfsdm_fl_event {
+	void (*cb)(struct stm32_dfsdm *, int, enum stm32_dfsdm_events,
+		   unsigned int, void *);
+	void *context;
+};
+
+/**
+ * struct stm32_dfsdm_filter - DFSDM filter  conversion parameters structure
+ * @reg_params:		DFSDM regular conversion parameters.
+ *			this param is optional and not taken into account if
+ *			@inj_params is defined.
+ * @inj_params:		DFSDM injected conversion parameters (optional).
+ * @filter_params:	DFSDM filter parameters.
+ * @event:		Events callback.
+ * @int_oversampling:	Integrator oversampling ratio for average purpose
+ *			(range from 1 to 256).
+ * @ext_det_ch_mask:	Extreme detector mask for channel selection
+ *			mask generated using DFSDM_CHANNEL_0 to
+ *			DFSDM_CHANNEL_7. If 0 feature is disable.
+ */
+struct stm32_dfsdm_filter {
+	struct stm32_dfsdm_regular *reg_params;
+	struct stm32_dfsdm_injected *inj_params;
+	struct stm32_dfsdm_sinc_filter sinc_params;
+	struct stm32_dfsdm_fl_event event;
+	unsigned int int_oversampling;
+};
+
+/**
+ * struct stm32_dfsdm - DFSDM context structure.
+ *
+ * @trig_info: Trigger name and id available last member name is null.
+ * @max_channels: max number of channels available.
+ * @max_filters: max number of filters available.
+ *
+ * Notice That structure is filled by mdf driver and must not be updated by
+ * user.
+ */
+struct stm32_dfsdm {
+	unsigned int max_channels;
+	unsigned int max_filters;
+};
+
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv);
+
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *filter);
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv);
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type);
+
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int ch_mask);
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int ch_mask);
+
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch);
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg);
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm,
+				 unsigned long *rate);
+
+#endif
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o
In-Reply-To: <1485189145-29576-1-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

This patch adds documentation of device tree bindings for the
STM32 DFSDM ADC.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
new file mode 100644
index 0000000..c156bcb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
@@ -0,0 +1,60 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It has several multiplexed input channels. Conversions can be performed
+in single, scan or discontinuous mode. Conversions can be launched in software
+or using hardware triggers.
+Each instance of the driver uses one filter instance handle by the DFSDM mfd
+driver.
+
+DFSDM also offers extra features:
+-The analog watchdog feature allows the application to detect if the
+ input voltage goes beyond the user-defined, higher or lower thresholds.
+-The short circuit detection allows allows the application to detect if the
+ input is in CC.
+-The clock absence detection allows application to detect if SPI input is clocked.
+
+Required properties:
+- compatible:	Must be "st,stm32-dfsdm-adc".
+- reg:		Specifies the DFSDM filter instance.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+- st,adc-channels:	List of single-ended channels muxed for this ADC.
+- st,adc-channel-names:	List of single-ended channels Name.
+
+Optional properties:
+- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
+			- 0: SPI with data on rising edge (default)
+			- 1: SPI with data on falling edge
+			- 2: manchester codec, rising edage = logic 0
+			- 3: manchester codec, rising edage = logic 1
+- st,adc-channel-clk-src: Conversion clock source. default value is 1.
+			  - 0: External SPI clocl (CLKIN x)
+			  - 1: internal SPI clock (CLKOUT) (default)
+			  - 2: internal SPI clock divided by 2 (falling edge).
+			  - 2: internal SPI clock divided by 2 (rising edge).
+- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
+			  input.
+			  If not set channel n is connected to SPI input n.
+			  If set channel n is connected to SPI input n + 1.
+
+Example:
+	dfsdm: dfsdm@4400D000 {
+		iio_dfsdm0: iio-dfsdm@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in0";
+		};
+		iio_dfsdm1: iio-dfsdm@1 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <1>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in1";
+			st,adc-channel-types = <1>;
+			st,adc-alt-channel = <1>;
+		};
+	};
-- 
1.9.1

^ permalink raw reply related

* [PATCH 4/7] IIO: add STM32 DFSDM ADC support
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin
In-Reply-To: <1485189145-29576-1-git-send-email-arnaud.pouliquen@st.com>

Add driver to handle Sigma Delta ADC conversion for ADC
connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig           |   9 +
 drivers/iio/adc/Makefile          |   1 +
 drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 686 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..4b2b886 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -441,6 +441,15 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 DFSDM ADC driver"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	help
+	  Say yes here to build the driver for the STMicroelectronics
+	  STM32 analog-to-digital converter with Digital filter.
+	  This driver can also be built as a module. If so, the module
+	  will be called stm32_dfsdm_adc.
+
 config STX104
 	tristate "Apex Embedded Systems STX104 driver"
 	depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8e02a94..aed42c6 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
 obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
new file mode 100644
index 0000000..727d6b1
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,676 @@
+/*
+ * This file is part of STM32 DFSDM ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@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/irq_work.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#define DFSDM_ADC_MAX_RESOLUTION 24
+#define DFSDM_ADC_STORAGE_BITS   32
+
+#define DFSDM_MAX_CH_OFFSET BIT(24)
+#define DFSDM_MAX_CH_SHIFT 24
+
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)
+#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
+
+struct stm32_dfsdm_adc {
+	struct device *dev;
+	struct stm32_dfsdm *dfsdm;
+	struct list_head adc_list;
+
+	/* Filter */
+	unsigned int fl_id;
+	struct stm32_dfsdm_sinc_filter sinc;
+	unsigned int int_oversampling;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *inputs;
+	struct stm32_dfsdm_ch_cfg *inputs_cfg;
+
+	/* Raw mode*/
+	struct completion completion;
+	struct stm32_dfsdm_regular reg_params;
+	u32 *buffer;
+};
+
+static const char * const stm32_dfsdm_adc_sinc_order[] = {
+	[0] = "FastSinc",
+	[1] = "Sinc1",
+	[2] = "Sinc2",
+	[3] = "Sinc3",
+	[4] = "Sinc4",
+	[5] = "Sinc5",
+};
+
+static inline const struct iio_chan_spec *get_ch_from_id(
+					struct iio_dev *indio_dev, int ch_id)
+{
+	int i;
+
+	for (i = 0; i < indio_dev->num_channels; i++) {
+		if (ch_id == indio_dev->channels[i].channel)
+			return &indio_dev->channels[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Filter attributes
+ */
+
+static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan,
+				    unsigned int val)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	adc->sinc.order = val;
+
+	return 0;
+}
+
+static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	return adc->sinc.order;
+}
+
+static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
+	.items = stm32_dfsdm_adc_sinc_order,
+	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
+	.get = stm32_dfsdm_adc_get_sinc,
+	.set = stm32_dfsdm_adc_set_sinc,
+};
+
+static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_INT_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->int_oversampling = val;
+
+	return len;
+}
+
+static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					const struct iio_chan_spec *chan,
+					const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_FL_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->sinc.oversampling = val;
+
+	return len;
+}
+
+/*
+ * Data bit shifting attribute
+ */
+static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
+}
+
+static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if (val > DFSDM_MAX_CH_SHIFT) {
+		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
+			DFSDM_MAX_CH_SHIFT);
+		return -EINVAL;
+	}
+	ch_cfg->right_bit_shift = val;
+
+	return len;
+}
+
+static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
+	/* sinc_filter_order: Configure Sinc filter order */
+	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
+		 &stm32_dfsdm_adc_fl_sinc_order),
+	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
+	/* filter oversampling: Post filter oversampling ratio */
+	{
+		.name = "sinc_filter_oversampling_ratio",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_fl_os,
+		.write = stm32_dfsdm_adc_set_fl_os,
+	},
+	/* data_right_bit_shift : Filter output data shifting */
+	{
+		.name = "data_right_bit_shift",
+		.shared = IIO_SEPARATE,
+		.read = stm32_dfsdm_adc_get_shift,
+		.write = stm32_dfsdm_adc_set_shift,
+	},
+
+	/*
+	 * averaging_length : Mean windows of data from filter.
+	 * Defines how many filter data will be summed to one data output
+	 */
+	{
+		.name = "integrator_oversampling",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_int_os,
+		.write = stm32_dfsdm_adc_set_int_os,
+	},
+	{},
+};
+
+/*
+ * Filter event routine called under IRQ context
+ */
+static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
+				 enum stm32_dfsdm_events ev, unsigned int param,
+				 void *context)
+{
+	struct stm32_dfsdm_adc *adc = context;
+	unsigned int ch_id;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	switch (ev) {
+	case DFSDM_EVENT_REG_EOC:
+		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
+					 &ch_id, DFSDM_FILTER_REG_CONV);
+		complete(&adc->completion);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
+			__func__, flt_id);
+		break;
+	default:
+		dev_err(adc->dev, "%s: event %#x not implemented\n",
+			__func__, ev);
+		break;
+	}
+}
+
+static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
+					     u32 channel_mask,
+					     struct stm32_dfsdm_filter *filter)
+{
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	filter->event.cb = stm32_dfsdm_event_cb;
+	filter->event.context = adc;
+
+	filter->sinc_params = adc->sinc;
+
+	filter->int_oversampling = adc->int_oversampling;
+}
+
+static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+	int ret;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	memset(&filter, 0, sizeof(filter));
+	filter.reg_params = &adc->reg_params;
+
+	if (!filter.reg_params)
+		return -ENOMEM;
+
+	filter.reg_params->ch_src = ch_id;
+
+	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
+
+	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
+	if (ret < 0) {
+		dev_err(adc->dev, "Failed to configure filter\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
+}
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan,
+				   u32 *result)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
+
+	reinit_completion(&adc->completion);
+
+	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
+					    DFSDM_EVENT_REG_EOC, 0);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "Failed to register event\n");
+		return ret;
+	}
+
+	adc->buffer = result;
+	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to start conversion\n");
+		goto free_event;
+	}
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    DFSDM_TIMEOUT);
+	if (timeout == 0) {
+		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+		ret = -ETIMEDOUT;
+	} else if (timeout < 0) {
+		ret = timeout;
+	} else {
+		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
+		ret = IIO_VAL_INT;
+	}
+
+	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
+
+free_event:
+	adc->buffer = NULL;
+	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
+					DFSDM_EVENT_REG_EOC, 0);
+
+	return ret;
+}
+
+static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan, int *val,
+				int *val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret = -EINVAL;
+
+	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+		if (!ret)
+			ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = ch_cfg->offset;
+		ret = IIO_VAL_INT;
+		break;
+	}
+
+	return ret;
+}
+
+static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan, int val,
+				 int val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OFFSET:
+		if (val > DFSDM_MAX_CH_OFFSET) {
+			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
+				DFSDM_MAX_CH_OFFSET);
+			return -EINVAL;
+		}
+		ch_cfg->offset = val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info stm32_dfsdm_iio_info = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int chan_idx)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	int ret;
+	unsigned int alt_ch = 0;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channels", chan_idx,
+					 &ch->channel);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channels' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-names", chan_idx,
+					    &ch->datasheet_name);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channel-names' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ch->extend_name = ch->datasheet_name;
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = chan_idx;
+	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				 BIT(IIO_CHAN_INFO_OFFSET);
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
+	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
+	ch->scan_type.shift = 8;
+
+	ch->ext_info = stm32_dfsdm_adc_ext_info;
+
+	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
+				   chan_idx, &alt_ch);
+	/* Select the previous channel if alternate field is defined*/
+	if (alt_ch) {
+		if (!ch->channel)
+			ch->channel = adc->dfsdm->max_channels;
+		ch->channel -= 1;
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+	} else {
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+	}
+	dfsdm_ch->id = ch->channel;
+
+	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+
+	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-types",
+					 chan_idx, &dfsdm_ch->serial_if.type);
+	if (ret < 0)
+		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-clk-src",
+					 chan_idx,
+					 &dfsdm_ch->serial_if.spi_clk);
+
+	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
+	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||
+	    (ret < 0))
+		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
+}
+
+static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	unsigned int num_ch;
+	struct iio_chan_spec *channels;
+	int ret, chan_idx;
+
+	num_ch = of_property_count_strings(indio_dev->dev.of_node,
+					   "st,adc-channel-names");
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
+				   sizeof(*adc->inputs), GFP_KERNEL);
+	if (!adc->inputs)
+		return -ENOMEM;
+
+	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
+				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
+	if (!adc->inputs_cfg)
+		return -ENOMEM;
+
+	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
+		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
+						    chan_idx);
+		if (ret < 0)
+			goto ch_error;
+	}
+
+	indio_dev->num_channels = num_ch;
+	indio_dev->channels = channels;
+
+	return 0;
+
+ch_error:
+	for (chan_idx--; chan_idx >= 0; chan_idx--)
+		stm32_dfsdm_release_channel(adc->dfsdm,
+					    adc->inputs[chan_idx].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev;
+	int ret, i;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(indio_dev)) {
+		dev_err(dev, "%s: failed to allocate iio", __func__);
+		return PTR_ERR(indio_dev);
+	}
+
+	indio_dev->name = np->name;
+	indio_dev->dev.parent = dev;
+	indio_dev->dev.of_node = np;
+	indio_dev->info = &stm32_dfsdm_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	adc = iio_priv(indio_dev);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: failed to allocate adc", __func__);
+		return PTR_ERR(adc);
+	}
+
+	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	adc->dev = &indio_dev->dev;
+	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	ret = stm32_dfsdm_adc_chan_init(indio_dev);
+	if (ret < 0) {
+		dev_err(dev, "iio channels init failed\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
+	if (ret < 0)
+		goto get_fl_err;
+
+	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
+	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
+
+	init_completion(&adc->completion);
+
+	ret = devm_iio_device_register(dev, indio_dev);
+	if (ret) {
+		dev_err(adc->dev, "failed to register iio device\n");
+		goto register_err;
+	}
+
+	platform_set_drvdata(pdev, adc);
+
+	return 0;
+
+register_err:
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+get_fl_err:
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
+	int i;
+
+	indio_dev = iio_priv_to_dev(adc);
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+	return 0;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc"},
+	{}
+};
+
+static struct platform_driver stm32_dfsdm_adc_driver = {
+	.driver = {
+		.name = "stm32-dfsdm-adc",
+		.of_match_table = stm32_dfsdm_adc_match,
+	},
+	.probe = stm32_dfsdm_adc_probe,
+	.remove = stm32_dfsdm_adc_remove,
+};
+module_platform_driver(stm32_dfsdm_adc_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta ADC");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related

* [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin
In-Reply-To: <1485189145-29576-1-git-send-email-arnaud.pouliquen@st.com>

From: olivier moysan <olivier.moysan@st.com>

Add copy support in pcm damengine operations.
This allows to pre/post process samples (apply shift, mask ...)
for playback/capture to support hardware contrains.

Signed-off-by: olivier moysan <omoysan.stm32@gmail.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 include/sound/dmaengine_pcm.h         |  3 +++
 sound/soc/soc-generic-dmaengine-pcm.c | 37 +++++++++++++++++++++++++++++++++--
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 67be244..9d7bce8 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -137,6 +137,9 @@ struct snd_dmaengine_pcm_config {
 	int (*prepare_slave_config)(struct snd_pcm_substream *substream,
 			struct snd_pcm_hw_params *params,
 			struct dma_slave_config *slave_config);
+	int (*copy)(struct snd_pcm_substream *substream, int channel,
+		    snd_pcm_uframes_t pos,
+		    void __user *buf, snd_pcm_uframes_t count);
 	struct dma_chan *(*compat_request_channel)(
 			struct snd_soc_pcm_runtime *rtd,
 			struct snd_pcm_substream *substream);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 6cef397..bd8332ce 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -329,6 +329,16 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 		return snd_dmaengine_pcm_pointer(substream);
 }
 
+int dmaengine_pcm_copy(struct snd_pcm_substream *substream, int channel,
+		       snd_pcm_uframes_t pos, void __user *buf,
+		       snd_pcm_uframes_t count)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
+
+	return pcm->config->copy(substream, channel, pos, buf, count);
+}
+
 static const struct snd_pcm_ops dmaengine_pcm_ops = {
 	.open		= dmaengine_pcm_open,
 	.close		= snd_dmaengine_pcm_close,
@@ -339,6 +349,17 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pointer	= dmaengine_pcm_pointer,
 };
 
+static const struct snd_pcm_ops dmaengine_pcm_ops_with_cpy = {
+	.open		= dmaengine_pcm_open,
+	.close		= snd_dmaengine_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= dmaengine_pcm_hw_params,
+	.hw_free	= snd_pcm_lib_free_pages,
+	.trigger	= snd_dmaengine_pcm_trigger,
+	.pointer	= dmaengine_pcm_pointer,
+	.copy		= dmaengine_pcm_copy,
+};
+
 static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
 	.component_driver = {
 		.probe_order = SND_SOC_COMP_ORDER_LATE,
@@ -347,6 +368,14 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pcm_new	= dmaengine_pcm_new,
 };
 
+static const struct snd_soc_platform_driver dmaengine_pcm_platform_with_cpy = {
+	.component_driver = {
+		.probe_order = SND_SOC_COMP_ORDER_LATE,
+	},
+	.ops		= &dmaengine_pcm_ops_with_cpy,
+	.pcm_new	= dmaengine_pcm_new,
+};
+
 static const char * const dmaengine_pcm_dma_channel_names[] = {
 	[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
@@ -439,8 +468,12 @@ int snd_dmaengine_pcm_register(struct device *dev,
 	if (ret)
 		goto err_free_dma;
 
-	ret = snd_soc_add_platform(dev, &pcm->platform,
-		&dmaengine_pcm_platform);
+	if (config && config->copy)
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform_with_cpy);
+	else
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform);
 	if (ret)
 		goto err_free_dma;
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o
In-Reply-To: <1485189145-29576-1-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

This patch adds documentation of device tree bindings for the
STM32 DFSDM ASoC driver.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt

diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
new file mode 100644
index 0000000..a1d27b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
@@ -0,0 +1,84 @@
+STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
+
+STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It offers possibility to record several mono microphones, with an option to
+synchronize all microphones on a main one (that must be piped to filter 0)
+Audio interface can be exposed through the generic ASoC simple card.
+
+2 Dmics can be connected on one SPI interface instance n.
+Convention is that the DMIC that strobes data on rising edge is connected to the
+corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
+is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
+is used to specify that microphone strobes data on falling edge.
+
+SPI interface allows to be scheduled by an external SPI clock. To use it
+simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
+have to be defined in dai-link node.
+
+Required properties:
+- compatible: 	Must be "st,stm32-dfsdm-audio",
+- reg:		Specifies the DFSDM filter instance.
+- interrupts: 	DFSDM filter instance interrupt line.
+- dma:		DMA controller phandle and DMA request line associated to the
+		filter instance ( specified by the field "reg")
+- dma-names: 	must be "rx"
+
+- st,input-id:		Id of the SPI/Manchester interface used.
+- st,dai-filter-order:  SinC filter order from 0 to 5.
+			0: FastSinC
+			[1-5]: order 1 to 5.
+			For audio purpose it is recommended to use order 3 to 5.
+
+Optional properties:
+ - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
+
+Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
+
+	dfsdm: dfsdm@4400D000 {
+		dai_dfsdm0: dfsdm-audio@0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai-filter-order = <5>;
+		};
+		dai_dfsdm1: dfsdm-audio@1 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai0-synchronized = <1>;
+			st,dai-filter-order = <5>;
+		};
+	};
+	sound_dfsdm_pdm {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "dfsdm_pdm";
+		status = "okay";
+
+		dfsdm0_mic0: simple-audio-card,dai-link@0 {
+			format = "pdm";
+			cpu {
+				sound-dai = <&dai_dfsdm0>;
+			};
+			dmic0_codec: codec {
+				sound-dai = <&dmic0>;
+			};
+		};
+		dfsdm0_mic1: simple-audio-card,dai-link@1 {
+			format = "pdm";
+			bitclock-inversion = <1>;
+			cpu {
+				sound-dai = <&dai_dfsdm1>;
+			};
+			codec {
+				sound-dai = <&dmic1>;
+			};
+		};
+	};
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH 7/7] ASoC: add STM32 DFSDM support
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o
In-Reply-To: <1485189145-29576-1-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>

Add driver to handle PDM microphones connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 +
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 686 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 700 insertions(+)
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 182d92e..3836ebe 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -63,6 +63,7 @@ source "sound/soc/sh/Kconfig"
 source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
 source "sound/soc/sunxi/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21..5440cf7 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= sti/
+obj-$(CONFIG_SND_SOC)	+= stm/
 obj-$(CONFIG_SND_SOC)	+= sunxi/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
new file mode 100644
index 0000000..79aee4e
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,10 @@
+menuconfig SND_SOC_STM32_DFSDM
+	tristate "SoC Audio support for STM32 DFSDM"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select SND_SOC_DMIC
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series for digital microphone capture.
\ No newline at end of file
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..ea90240
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,2 @@
+#DFSDM
+obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
new file mode 100644
index 0000000..9d34bb7
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,686 @@
+/*
+ * This file is part of STM32 DFSDM ASoC DAI driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
+ *          Olivier Moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org>
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/dmaengine_pcm.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+/*
+ * Set data output resolution to 23 bits max to keep 1 extra bit for sign,
+ * as filter output is symmetric +/-2^(n-1).
+ */
+#define STM32_ADFSDM_DATA_RES BIT(23)
+#define STM32_ADFSDM_MAX_RES BIT(31)
+#define STM32_ADFSDM_DATAR_DATA_MASK	GENMASK(31, 8)
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+	unsigned int fosr;	/* filter over sampling ratio */
+	unsigned int iosr;	/* integrator over sampling ratio */
+	unsigned int fast;	/* filter fast mode */
+	unsigned long res;	/* output data resolution */
+	int shift;		/* shift on data output */
+	bool h_res_found;	/* preferred resolution higher than expected */
+};
+
+static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
+	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
+	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
+	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
+};
+
+static const unsigned int stm32_dfsdm_sr_val[] = {
+	8000,
+	16000,
+	32000,
+};
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai;
+	struct snd_dmaengine_dai_dma_data dma_data;
+	struct snd_pcm_substream *substream;
+	struct stm32_dfsdm_sinc_filter fl;
+	struct stm32_dfsdm_channel channel;
+	struct stm32_dfsdm_ch_cfg ch_cfg;
+	struct stm32_dfsdm *dfsdm;
+	struct stm32_adfsdm_data *f_param;
+	struct device *dev;
+	struct snd_pcm_hw_constraint_list rates_const;
+	unsigned long dmic_clk;
+	unsigned int input_id;
+	unsigned int fl_id;
+	unsigned int order; /* filter order */
+	int synchro;
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+	    SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+
+	.rate_min = 8000,
+	.rate_max = 48000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = 48,
+
+	.period_bytes_min = 40, /* 8 khz 5 ms */
+	.period_bytes_max = 4 * PAGE_SIZE,
+	.buffer_bytes_max = 16 * PAGE_SIZE
+};
+
+static inline void stm32_adfsdm_get_param(struct stm32_adfsdm_priv *priv,
+					  unsigned int rate,
+					  struct stm32_adfsdm_data **fparam)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		if (rate == priv->f_param[i].freq) {
+			*fparam = &priv->f_param[i];
+			break;
+		}
+	}
+}
+
+static int stm32_adfsdm_compute_shift(struct stm32_adfsdm_priv *priv,
+				      struct stm32_adfsdm_data *param)
+{
+	int shift = 0;
+	u32 r = param->res;
+
+	if (!r) {
+		dev_err(priv->dev, "%s: resolution undefined\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * If filter resolution is higher than data output resolution
+	 * compute right shift required to match data resolution.
+	 * Otherwise compute left shift to align MSB on data resolution.
+	 */
+	if (r >= STM32_ADFSDM_DATA_RES)
+		while ((r >> -shift) >= STM32_ADFSDM_DATA_RES)
+			shift--;
+	else
+		while ((r << shift) < STM32_ADFSDM_DATA_RES)
+			shift++;
+
+	param->shift = shift;
+	dev_dbg(priv->dev, "%s: output shift: %d\n", __func__, shift);
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_best_osr(struct stm32_adfsdm_priv *priv,
+				     unsigned int decim, bool fast,
+				     struct stm32_adfsdm_data *param)
+{
+	unsigned int i, d, fosr, iosr;
+	u64 res;
+	s64 delta;
+	unsigned int m = 1;	/* multiplication factor */
+	unsigned int p = priv->order;	/* filter order (ford) */
+
+	/*
+	 * Decimation d depends on the filter order and the oversampling ratios.
+	 * ford: filter order
+	 * fosr: filter over sampling ratio
+	 * iosr: integrator over sampling ratio
+	 */
+	dev_dbg(priv->dev, "%s: decim = %d fast = %d\n", __func__, decim, fast);
+	if (priv->order == DFSDM_FASTSINC_ORDER) {
+		m = 2;
+		p = 2;
+	}
+
+	/*
+	 * Looks for filter and integrator oversampling ratios which allow
+	 * to reach 24 bits data output resolution.
+	 * Leave at once if exact resolution if reached.
+	 * Otherwise the higher resolution below 32 bits is kept.
+	 */
+	for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
+		for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
+			if (fast)
+				d = fosr * iosr;
+			else if (priv->order == DFSDM_FASTSINC_ORDER)
+				d = fosr * (iosr + 3) + 2;
+			else
+				d = fosr * (iosr - 1 + p) + p;
+
+			if (d > decim)
+				break;
+			else if (d != decim)
+				continue;
+			/*
+			 * Check resolution (limited to signed 32 bits)
+			 *   res <= 2^31
+			 * Sincx filters:
+			 *   res = m * fosr^p x iosr (with m=1, p=ford)
+			 * FastSinc filter
+			 *   res = m * fosr^p x iosr (with m=2, p=2)
+			 */
+			res = fosr;
+			for (i = p - 1; i > 0; i--) {
+				res = res * (u64)fosr;
+				if (res > STM32_ADFSDM_MAX_RES)
+					break;
+			}
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+			res = res * (u64)m * (u64)iosr;
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+
+			delta = res - STM32_ADFSDM_DATA_RES;
+
+			if (res >= param->res) {
+				param->res = res;
+				param->fosr = fosr;
+				param->iosr = iosr;
+				param->fast = fast;
+			}
+
+			if (!delta)
+				return 0;
+		}
+	}
+
+	if (!param->fosr)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_supported_rates(struct stm32_adfsdm_priv *priv,
+					    unsigned int *rates)
+{
+	unsigned long fs = priv->dmic_clk;
+	unsigned int i, decim;
+	int ret;
+
+	*rates = 0;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		/* check that clkout_freq is compatible */
+		if ((fs % priv->f_param[i].freq) != 0)
+			continue;
+
+		decim = fs / priv->f_param[i].freq;
+
+		/*
+		 * Try to find one solution for filter and integrator
+		 * oversampling ratio with fast mode ON or OFF.
+		 * Fast mode on is the preferred solution.
+		 */
+		ret = stm32_adfsdm_get_best_osr(priv, decim, 0,
+						&priv->f_param[i]);
+		ret &= stm32_adfsdm_get_best_osr(priv, decim, 1,
+						 &priv->f_param[i]);
+		if (!ret) {
+			ret = stm32_adfsdm_compute_shift(priv,
+							 &priv->f_param[i]);
+			if (ret)
+				continue;
+
+			*rates |= 1 << i;
+			dev_dbg(priv->dev, "%s: %d rate supported\n", __func__,
+				priv->f_param[i].freq);
+		}
+	}
+
+	if (!*rates) {
+		dev_err(priv->dev, "%s: no matched rate found\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void stm32_dfsdm_xrun(struct stm32_dfsdm *dfsdm, int flt_id,
+			     enum stm32_dfsdm_events ev, unsigned int param,
+			     void *context)
+{
+	struct stm32_adfsdm_priv *priv = context;
+
+	snd_pcm_stream_lock(priv->substream);
+	dev_err(priv->dev, "%s:unexpected underrun\n", __func__);
+	/* Stop the player */
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
+	snd_pcm_stream_unlock(priv->substream);
+}
+
+static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
+			     snd_pcm_uframes_t pos,
+			     void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct stm32_adfsdm_data *f_param;
+	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
+	ssize_t bytes = frames_to_bytes(runtime, count);
+	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	/*
+	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
+	 * We need to mask 8 LSB control bits...
+	 * Additionnaly precision depends on decimation and can need shift
+	 * to be aligned on 32-bit word MSB.
+	 */
+	if (f_param->shift > 0) {
+		do {
+			*ptr <<= f_param->shift & STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	} else {
+		do {
+			*ptr &= STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	}
+
+	return copy_to_user(buf, hwbuf, bytes);
+}
+
+static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = substream;
+
+	/* Fix available rate depending on CLKOUT or CKIN value */
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &priv->rates_const);
+}
+
+static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = NULL;
+}
+
+static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = 1;
+
+	return 0;
+}
+
+static int stm32_adfsdm_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_regular params;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	memset(&filter, 0, sizeof(filter));
+	memset(&params, 0, sizeof(params));
+
+	params.ch_src = priv->channel.id;
+	params.dma_mode = 1;
+	params.cont_mode = 1;
+	params.fast_mode = f_param->fast;
+	params.sync_mode = priv->synchro ?
+	    DFSDM_FILTER_RSYNC_ON : DFSDM_FILTER_RSYNC_OFF;
+	filter.reg_params = &params;
+	filter.sinc_params.order = priv->order;
+	filter.sinc_params.oversampling = f_param->fosr;
+	filter.int_oversampling = f_param->iosr;
+
+	filter.event.cb = stm32_dfsdm_xrun;
+	filter.event.context = priv;
+
+	ret = stm32_dfsdm_configure_filter(priv->dfsdm, priv->fl_id, &filter);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_register_fl_event(priv->dfsdm, priv->fl_id,
+					    DFSDM_EVENT_REG_XRUN, 0);
+	if (ret < 0)
+		dev_err(priv->dev, "Failed to register xrun event\n");
+
+	return ret;
+}
+
+static int stm32_adfsdm_start(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+	if (f_param->shift < 0)
+		priv->ch_cfg.right_bit_shift = -f_param->shift;
+
+	ret = stm32_dfsdm_start_channel(priv->dfsdm, priv->channel.id,
+					&priv->ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(priv->dfsdm, priv->fl_id,
+				 DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_adfsdm_stop(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	stm32_dfsdm_stop_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_stop_channel(priv->dfsdm, priv->channel.id);
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return stm32_adfsdm_start(substream, dai);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		stm32_adfsdm_stop(substream, dai);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
+	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	/* DAI clock strobing */
+	if ((inv == SND_SOC_DAIFMT_IB_NF) || (inv == SND_SOC_DAIFMT_IB_IF)) {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_FALLING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+		/*
+		 * if data on falling egde SPI connected to channel n - 1.
+		 * if data on rising egde  SPI connected to channel n.
+		 */
+		if (priv->input_id)
+			priv->channel.id = priv->input_id - 1;
+		else
+			priv->channel.id = priv->dfsdm->max_channels - 1;
+	} else {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+		priv->channel.id = priv->input_id;
+	}
+
+	dev_dbg(dai->dev, "%s: channel %d on input %d\n", __func__,
+		priv->channel.id, priv->input_id);
+
+	if ((cb == SND_SOC_DAIFMT_CBS_CFM) || (cb == SND_SOC_DAIFMT_CBS_CFS)) {
+		/* Digital microphone is clocked by CLKOUT */
+		stm32_dfsdm_get_clk_out_rate(priv->dfsdm, &priv->dmic_clk);
+	} else {
+		/* Digital microphone is clocked by external clock */
+		if (!priv->dmic_clk) {
+			dev_err(priv->dev,
+				"system-clock-frequency not defined\n");
+			return -EINVAL;
+		}
+	}
+
+	priv->rates_const.count = ARRAY_SIZE(stm32_dfsdm_sr_val);
+	priv->rates_const.list = stm32_dfsdm_sr_val;
+	ret = stm32_adfsdm_get_supported_rates(priv, &priv->rates_const.mask);
+	if (ret < 0)
+		return ret;
+
+	return stm32_dfsdm_get_channel(priv->dfsdm, &priv->channel);
+}
+
+static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				   unsigned int freq, int dir)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+	if (dir == SND_SOC_CLOCK_IN)
+		priv->dmic_clk = freq;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.startup = stm32_adfsdm_startup,
+	.shutdown = stm32_adfsdm_shutdown,
+	.hw_params = stm32_adfsdm_dai_hw_params,
+	.set_fmt = stm32_adfsdm_set_dai_fmt,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+	.prepare = stm32_adfsdm_prepare,
+	.trigger = stm32_adfsdm_trigger,
+};
+
+static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	/* filter settings */
+	ret = stm32_dfsdm_get_filter(priv->dfsdm, priv->fl_id);
+	if (ret < 0)
+		return -EBUSY;
+
+	/* DMA settings */
+	snd_soc_dai_init_dma_data(dai, NULL, dma);
+	dma->addr = stm32_dfsdm_get_filter_dma_phy_addr(priv->dfsdm,
+							priv->fl_id,
+							DFSDM_FILTER_REG_CONV);
+	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	return 0;
+}
+
+static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	stm32_dfsdm_release_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_release_channel(priv->dfsdm, priv->channel.id);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S24_LE |
+			       SNDRV_PCM_FMTBIT_S32_LE,
+		    .rates = SNDRV_PCM_RATE_8000_48000,
+		    },
+	.probe = stm32_adfsdm_dai_probe,
+	.remove = stm32_adfsdm_dai_remove,
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "sti_cpu_dai",
+};
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+	.pcm_hardware = &stm32_adfsdm_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.copy = stm32_adfsdm_copy,
+};
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	struct device_node *np = pdev->dev.of_node;
+	char *_name;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: enter for node %s\n", __func__,
+		np->name);
+
+	if (!np) {
+		dev_err(&pdev->dev, "No DT found\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+	priv->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	if (of_property_read_u32(np, "reg", &priv->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "st,dai-filter-order", &priv->order);
+	if (ret < 0) {
+		dev_warn(&pdev->dev, "Default filter order selected\n");
+		priv->order = DFSDM_SINC5_ORDER;
+	}
+
+	ret = of_property_read_u32(np, "st,input-id", &priv->input_id);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "st,input-id property missing\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "st,dai0-synchronized", &priv->synchro);
+	if (ret < 0)
+		/* default case if property not defined */
+		priv->synchro = 0;
+
+	priv->channel.type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+	priv->channel.type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	priv->channel.serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	/* DAI settings */
+	_name = devm_kzalloc(&pdev->dev, sizeof("dfsdm_pdm_0"), GFP_KERNEL);
+	if (!_name)
+		return -ENOMEM;
+
+	priv->dai = stm32_adfsdm_dai;
+
+	priv->f_param = devm_kcalloc(&pdev->dev,
+				     ARRAY_SIZE(stm32_dfsdm_filter),
+				     sizeof(stm32_dfsdm_filter[0]), GFP_KERNEL);
+	if (!priv->f_param)
+		return -ENOMEM;
+
+	memcpy(priv->f_param, stm32_dfsdm_filter,
+	       ARRAY_SIZE(stm32_dfsdm_filter) * sizeof(stm32_dfsdm_filter[0]));
+
+	snprintf(_name, sizeof("dfsdm_pdm_0"), "dfsdm_pdm_%i", priv->fl_id);
+	priv->dai.name = _name;
+	priv->dai.capture.stream_name = _name;
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_adfsdm_dai_component,
+					      &priv->dai, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &dmaengine_pcm_config, 0);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register dma pcm config\n");
+
+	return ret;
+}
+
+static const struct of_device_id snd_soc_dfsdm_match[] = {
+	{.compatible = "st,stm32-dfsdm-audio"},
+	{},
+};
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.driver = {
+		   .name = "stm32-dfsdm-audio",
+		   .of_match_table = snd_soc_dfsdm_match,
+		   },
+	.probe = stm32_adfsdm_probe,
+};
+
+module_platform_driver(stm32_adfsdm_driver);
+
+MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

^ permalink raw reply related

* Re: [PATCH v4 3/3] watchdog: zx2967: add watchdog controller driver for ZTE's zx2967 family
From: Mathieu Poirier @ 2017-01-23 16:42 UTC (permalink / raw)
  To: Baoyou Xie
  Cc: jun.nie-QSEj5FYQhm4dnm+yROfE0A, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-0h96xk9xTtrk1uMJSBkQmQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
	xie.baoyou-Th6q7B73Y6EnDS1+zs4M5A,
	chen.chaokai-Th6q7B73Y6EnDS1+zs4M5A,
	wang.qiang01-Th6q7B73Y6EnDS1+zs4M5A
In-Reply-To: <1485073008-26699-3-git-send-email-baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

On Sun, Jan 22, 2017 at 04:16:48PM +0800, Baoyou Xie wrote:
> This patch adds watchdog controller driver for ZTE's zx2967 family.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Hello Baoyou,

Please see comments below.

Thanks,
Mathieu

> ---
>  drivers/watchdog/Kconfig      |  10 ++
>  drivers/watchdog/Makefile     |   1 +
>  drivers/watchdog/zx2967_wdt.c | 282 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 293 insertions(+)
>  create mode 100644 drivers/watchdog/zx2967_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index acb00b5..05093a2 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -714,6 +714,16 @@ config ASPEED_WATCHDOG
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called aspeed_wdt.
>  
> +config ZX2967_WATCHDOG
> +	tristate "ZTE zx2967 SoCs watchdog support"
> +	depends on ARCH_ZX
> +	select WATCHDOG_CORE
> +	help
> +	  Say Y here to include support for the watchdog timer
> +	  in ZTE zx2967 SoCs.
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called zx2967_wdt.
> +
>  # AVR32 Architecture
>  
>  config AT32AP700X_WDT
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 0c3d35e..bf2d296 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -82,6 +82,7 @@ obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
>  obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
>  obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
>  obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
> +obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
>  
>  # AVR32 Architecture
>  obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
> diff --git a/drivers/watchdog/zx2967_wdt.c b/drivers/watchdog/zx2967_wdt.c
> new file mode 100644
> index 0000000..7340605
> --- /dev/null
> +++ b/drivers/watchdog/zx2967_wdt.c
> @@ -0,0 +1,282 @@
> +/*
> + * watchdog driver for ZTE's zx2967 family
> + *
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> + *
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/watchdog.h>
> +
> +#define ZX2967_WDT_CFG_REG			0x4
> +#define ZX2967_WDT_LOAD_REG			0x8
> +#define ZX2967_WDT_REFRESH_REG			0x18
> +#define ZX2967_WDT_START_REG			0x1c
> +
> +#define ZX2967_WDT_REFRESH_MASK			0x3f
> +
> +#define ZX2967_WDT_CFG_DIV(n)			((((n) & 0xff) - 1) << 8)
> +#define ZX2967_WDT_START_EN			0x1
> +
> +#define ZX2967_WDT_WRITEKEY			0x12340000
> +
> +#define ZX2967_WDT_DIV_DEFAULT			16
> +#define ZX2967_WDT_DEFAULT_TIMEOUT		32
> +#define ZX2967_WDT_MIN_TIMEOUT			1
> +#define ZX2967_WDT_MAX_TIMEOUT			500
> +#define ZX2967_WDT_MAX_COUNT			0xffff
> +
> +#define ZX2967_WDT_CLK_FREQ			0x8000
> +
> +#define ZX2967_WDT_FLAG_REBOOT_MON		BIT(0)
> +

Please use proper kernel documentation for structures.

> +struct zx2967_wdt {
> +	struct clk		*clock;
> +	void __iomem		*reg_base;
> +	unsigned int		conf;
> +	unsigned int		load;
> +	unsigned int		flags;
> +	struct watchdog_device	wdt_device;
> +	struct notifier_block	restart_handler;
> +};
> +
> +static inline u32 zx2967_wdt_readl(struct zx2967_wdt *wdt, u16 reg)
> +{
> +	return readl_relaxed(wdt->reg_base + reg);
> +}
> +
> +static inline void zx2967_wdt_writel(struct zx2967_wdt *wdt, u16 reg, u32 val)
> +{
> +	writel_relaxed(val | ZX2967_WDT_WRITEKEY, wdt->reg_base + reg);
> +}
> +
> +static void zx2967_wdt_refresh(struct zx2967_wdt *wdt)
> +{
> +	u32 val;
> +
> +	val = zx2967_wdt_readl(wdt, ZX2967_WDT_REFRESH_REG);
> +	val ^= ZX2967_WDT_REFRESH_MASK;
> +	zx2967_wdt_writel(wdt, ZX2967_WDT_REFRESH_REG, val);
> +}
> +
> +static int
> +zx2967_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
> +{
> +	struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +	unsigned int divisor = ZX2967_WDT_DIV_DEFAULT;
> +	unsigned int count;
> +
> +	count = timeout * ZX2967_WDT_CLK_FREQ;
> +	if (count > divisor * ZX2967_WDT_MAX_COUNT)
> +		divisor = DIV_ROUND_UP(count, ZX2967_WDT_MAX_COUNT);
> +	count = DIV_ROUND_UP(count, divisor);
> +	zx2967_wdt_writel(wdt, ZX2967_WDT_CFG_REG, ZX2967_WDT_CFG_DIV(divisor));
> +	zx2967_wdt_writel(wdt, ZX2967_WDT_LOAD_REG, count);
> +	zx2967_wdt_refresh(wdt);
> +	wdt->load = count;
> +	wdd->timeout =  (count * divisor) / ZX2967_WDT_CLK_FREQ;
> +
> +	return 0;
> +}
> +
> +static void __zx2967_wdt_start(struct zx2967_wdt *wdt)
> +{
> +	u32 val;
> +
> +	val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG);
> +	val |= ZX2967_WDT_START_EN;
> +	zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, val);
> +}
> +
> +static void __zx2967_wdt_stop(struct zx2967_wdt *wdt)
> +{
> +	u32 val;
> +
> +	val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG);
> +	val &= ~ZX2967_WDT_START_EN;
> +	zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, val);
> +}
> +
> +static int zx2967_wdt_start(struct watchdog_device *wdd)
> +{
> +	struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	zx2967_wdt_set_timeout(wdd, wdd->timeout);
> +	__zx2967_wdt_start(wdt);
> +
> +	return 0;
> +}
> +
> +static int zx2967_wdt_stop(struct watchdog_device *wdd)
> +{
> +	struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	__zx2967_wdt_stop(wdt);
> +
> +	return 0;
> +}
> +
> +static int zx2967_wdt_keepalive(struct watchdog_device *wdd)
> +{
> +	struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	zx2967_wdt_refresh(wdt);
> +
> +	return 0;
> +}
> +
> +#define ZX2967_WDT_OPTIONS \
> +	(WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
> +static const struct watchdog_info zx2967_wdt_ident = {
> +	.options          =     ZX2967_WDT_OPTIONS,
> +	.firmware_version =	0,
> +	.identity         =	"zx2967 watchdog",
> +};
> +
> +static struct watchdog_ops zx2967_wdt_ops = {
> +	.owner = THIS_MODULE,
> +	.start = zx2967_wdt_start,
> +	.stop = zx2967_wdt_stop,
> +	.ping = zx2967_wdt_keepalive,
> +	.set_timeout = zx2967_wdt_set_timeout,
> +};
> +
> +static void zx2967_wdt_reset_sysctrl(struct device *dev)
> +{
> +	int ret;
> +	struct device_node *np = NULL;
> +	void __iomem *regmap;
> +	unsigned int offset, mask, config;
> +	struct of_phandle_args out_args;
> +
> +	ret = of_parse_phandle_with_fixed_args(dev->of_node,
> +			"zte,wdt-reset-sysctrl", 3, 0, &out_args);
> +	if (ret)
> +		return;
> +
> +	offset = out_args.args[0];
> +	config = out_args.args[1];
> +	mask = out_args.args[2];
> +
> +	regmap = syscon_node_to_regmap(out_args.np);
> +	if (IS_ERR(regmap))
> +		goto out;
> +
> +	regmap_update_bits(regmap, offset, mask, config);
> +out:
> +	of_node_put(np);

Unless I'm missing something variable 'np' is always NULL.  Given the comment
[1] for function of_parse_phandle_with_fixed_args(), I am guessing your
intention was to do something with &out_args->np.

[1]. http://lxr.free-electrons.com/source/drivers/of/base.c#L1786 


> +}
> +
> +static int zx2967_wdt_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct zx2967_wdt *wdt;
> +	struct resource *base;
> +	int ret;
> +	struct reset_control *rstc;
> +
> +	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
> +	if (!wdt)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, wdt);
> +
> +	wdt->wdt_device.info = &zx2967_wdt_ident;
> +	wdt->wdt_device.ops = &zx2967_wdt_ops;
> +	wdt->wdt_device.timeout = ZX2967_WDT_DEFAULT_TIMEOUT;
> +	wdt->wdt_device.max_timeout = ZX2967_WDT_MAX_TIMEOUT;
> +	wdt->wdt_device.min_timeout = ZX2967_WDT_MIN_TIMEOUT;
> +	wdt->wdt_device.parent = &pdev->dev;
> +
> +	base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	wdt->reg_base = devm_ioremap_resource(dev, base);
> +	if (IS_ERR(wdt->reg_base)) {
> +		dev_err(dev, "ioremap failed\n");
> +		return PTR_ERR(wdt->reg_base);
> +	}
> +
> +	zx2967_wdt_reset_sysctrl(dev);
> +
> +	wdt->clock = devm_clk_get(dev, NULL);
> +	if (IS_ERR(wdt->clock)) {
> +		dev_err(dev, "failed to find watchdog clock source\n");
> +		return PTR_ERR(wdt->clock);
> +	}
> +
> +	ret = clk_prepare_enable(wdt->clock);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to enable clock\n");
> +		return ret;
> +	}
> +	clk_set_rate(wdt->clock, ZX2967_WDT_CLK_FREQ);
> +
> +	rstc = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(rstc)) {
> +		dev_err(dev, "failed to get rstc");
> +		ret = PTR_ERR(rstc);
> +		goto fail_get_reset_control;

Shouldn't you be going to "fail_register:" ?

> +	}
> +
> +	reset_control_assert(rstc);
> +	mdelay(10);

We can't do better than this?  There is no register to probe?

> +	reset_control_deassert(rstc);
> +
> +	watchdog_set_drvdata(&wdt->wdt_device, wdt);
> +	watchdog_set_nowayout(&wdt->wdt_device, WATCHDOG_NOWAYOUT);
> +
> +	ret = watchdog_register_device(&wdt->wdt_device);
> +	if (ret)
> +		goto fail_register;
> +
> +	dev_info(dev, "watchdog enabled (timeout=%d sec, nowayout=%d)",
> +		 wdt->wdt_device.timeout, WATCHDOG_NOWAYOUT);
> +
> +	return 0;
> +
> +fail_get_reset_control:
> +	watchdog_unregister_device(&wdt->wdt_device);

Not sure you need the above.

> +fail_register:
> +	clk_disable_unprepare(wdt->clock);
> +	return ret;
> +}
> +
> +static int zx2967_wdt_remove(struct platform_device *pdev)
> +{
> +	struct zx2967_wdt *wdt = platform_get_drvdata(pdev);
> +
> +	watchdog_unregister_device(&wdt->wdt_device);
> +	clk_disable_unprepare(wdt->clock);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id zx2967_wdt_match[] = {
> +	{ .compatible = "zte,zx296718-wdt", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, zx2967_wdt_match);
> +
> +static struct platform_driver zx2967_wdt_driver = {
> +	.probe		= zx2967_wdt_probe,
> +	.remove		= zx2967_wdt_remove,
> +	.driver		= {
> +		.name	= "zx2967-wdt",
> +		.of_match_table	= of_match_ptr(zx2967_wdt_match),
> +	},
> +};
> +module_platform_driver(zx2967_wdt_driver);
> +
> +MODULE_AUTHOR("Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
> +MODULE_DESCRIPTION("ZTE zx2967 Watchdog Device Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v4 6/9] clocksource/drivers/rockchip_timer: split bc_timer into rk_timer and rk_clock_event_device
From: Alexander Kochetkov @ 2017-01-23 16:45 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: LKML, devicetree, LAK, linux-rockchip, Thomas Gleixner,
	Heiko Stuebner, Mark Rutland, Rob Herring, Russell King,
	Caesar Wang, Huang Tao
In-Reply-To: <20170123162624.GG2166@mai>

Daniel, thanks for reviewing patches!

> 23 янв. 2017 г., в 19:26, Daniel Lezcano <daniel.lezcano@linaro.org> написал(а):
> 
> On Tue, Nov 29, 2016 at 07:14:49PM +0300, Alexander Kochetkov wrote:
>> The patch move ce field out of struct bc_timer into struct
>> rk_clock_event_device and rename struct bc_timer to struct rk_timer.
> 
> Why ?

Single rockchip timer can be either broadcast timer/clockevent (current implementation) or
clocksource (the one I’ve implemented in series). Both implementations based on
low level timer routines (rk_timer_disable, rk_timer_enable, rk_timer_update_counter, 
rk_timer_counter_read and so on). Both implementations use timers in different modes
(interrupts vs free running). In order to distinguish this concepts I tried to split
low level timer routines from bc_timer.

Otherwise I can place all needed clocksource filelds into 'struct bc_timer’ and rename 'struct bc_timer’
into 'struct rk_timer’. If this is more clear solution I can rewrite patches.

Alexander.

^ permalink raw reply

* Re: [PATCH V2 1/2] dt-bindings: leds: document new led-triggers property
From: Rob Herring @ 2017-01-23 16:45 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Rafał Miłecki, Greg Kroah-Hartman,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-leds-u79uwXL29TY76Z2rM5mHXA, Richard Purdie, Pavel Machek,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
	Rafał Miłecki
In-Reply-To: <29f27028-20f3-3eb9-502f-1b51958640f9-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Fri, Jan 20, 2017 at 11:35:20PM +0100, Jacek Anaszewski wrote:
> Hi Rafał,
> 
> On 01/20/2017 10:56 PM, Rafał Miłecki wrote:
> > From: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
> > 
> > Some LEDs can be related to particular devices described in DT. This
> > property allows specifying such relations. E.g. USB LED should usually
> > be used to indicate some USB port(s) state.
> > 
> > Signed-off-by: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
> > ---
> > V2: Replace "usb-ports" with "led-triggers" property which is more generic and
> >     allows specifying other devices as well.
> > 
> > When bindings patch is related to some followup implementation, they usually go
> > through the same tree.
> > 
> > Greg: this patch is based on top of e64b8cc72bf9 ("DT: leds: Improve examples by
> > adding some context") from kernel/git/j.anaszewski/linux-leds.git . Is there any
> > way to solve this dependency issue? Or should this patch wait until 3.11 is
> > released?
> > ---
> >  Documentation/devicetree/bindings/leds/common.txt | 16 ++++++++++++++++
> >  1 file changed, 16 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt
> > index 24b656014089..17632a041196 100644
> > --- a/Documentation/devicetree/bindings/leds/common.txt
> > +++ b/Documentation/devicetree/bindings/leds/common.txt
> > @@ -49,6 +49,17 @@ Optional properties for child nodes:
> >  - panic-indicator : This property specifies that the LED should be used,
> >  		    if at all possible, as a panic indicator.
> >  
> > +- led-triggers : List of devices that should trigger this LED activity. Some
> > +		 LEDs can be related to a specific device and should somehow
> > +		 indicate its state. E.g. USB 2.0 LED may react to device(s) in
> > +		 a USB 2.0 port(s). Another common example is switch or router
> > +		 with multiple Ethernet ports each of them having its own LED
> > +		 assigned (assuminled-trigger-usbportg they are not hardwired).
> > +		 In such cases this property should contain phandle(s) of
> > +		 related device(s). In many cases LED can be related to more
> > +		 than one device (e.g. one USB LED vs. multiple USB ports) so a
> > +		 list of entries can be specified.
> > +
> 
> This implies that it is possible to define multiple triggers for
> a LED class device but it is not supported by LED Trigger core.

Not really relevant for designing (and future proofing) a binding.

> There is linux,default-trigger property which allows to define one
> trigger that will be initially assigned.
> 
> I am aware that this is renamed usb-ports property from v1,
> that attempts to address Rob's comment, but we can't do that this way.
> Maybe usb-ports property could be documented in led-trigger-usbport's
> specific bindings and a reference to it could be added next to the
> related entry on the list of the available LED triggers (which is
> actually missing) in Documentation/devicetree/bindings/leds/common.txt.

I'm not going to accept something specific to USB. So the choice is make 
the existing property work for USB or design something more flexible and 
generic.

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

^ permalink raw reply

* Re: [PATCH 01/18] Documentation: devicetree: amlogic: Reorder boards
From: Rob Herring @ 2017-01-23 16:48 UTC (permalink / raw)
  To: Andreas Färber
  Cc: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Carlo Caione,
	Kevin Hilman, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170120222118.21476-2-afaerber-l3A5Bk7waGM@public.gmane.org>

On Fri, Jan 20, 2017 at 11:21:01PM +0100, Andreas Färber wrote:
> Enforce groupment by SoCs and order alphabetically within the group
> (with some exceptions). This should facilitate adding new boards.
> 
> Signed-off-by: Andreas Färber <afaerber-l3A5Bk7waGM@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/arm/amlogic.txt | 19 +++++++++++++------
>  1 file changed, 13 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/arm/amlogic.txt b/Documentation/devicetree/bindings/arm/amlogic.txt
> index c246cd2730d9..b03d23c9ea78 100644
> --- a/Documentation/devicetree/bindings/arm/amlogic.txt
> +++ b/Documentation/devicetree/bindings/arm/amlogic.txt
> @@ -29,23 +29,30 @@ Boards with the Amlogic Meson GXM S912 SoC shall have the following properties:
>    Required root node property:
>      compatible: "amlogic,s912", "amlogic,meson-gxm";
>  
> -Board compatible values:
> +Board compatible values (grouped by SoC):

...and alphabetical order within each SoC.

> +
>    - "geniatech,atv1200" (Meson6)
> +
>    - "minix,neo-x8" (Meson8)
> -  - "tronfy,mxq" (Meson8b)
> +
>    - "hardkernel,odroid-c1" (Meson8b)
> +  - "tronfy,mxq" (Meson8b)
> +
> +  - "amlogic,p200" (Meson gxbb)
> +  - "amlogic,p201" (Meson gxbb)
> +  - "hardkernel,odroid-c2" (Meson gxbb)
> +  - "nexbox,a95x" (Meson gxbb or Meson gxl s905x)
>    - "tronsmart,vega-s95-pro", "tronsmart,vega-s95" (Meson gxbb)
>    - "tronsmart,vega-s95-meta", "tronsmart,vega-s95" (Meson gxbb)
>    - "tronsmart,vega-s95-telos", "tronsmart,vega-s95" (Meson gxbb)
> -  - "hardkernel,odroid-c2" (Meson gxbb)
> -  - "amlogic,p200" (Meson gxbb)
> -  - "amlogic,p201" (Meson gxbb)
>    - "wetek,hub" (Meson gxbb)
>    - "wetek,play2" (Meson gxbb)
> +
>    - "amlogic,p212" (Meson gxl s905x)
> +
>    - "amlogic,p230" (Meson gxl s905d)
>    - "amlogic,p231" (Meson gxl s905d)
> +
>    - "amlogic,q200" (Meson gxm s912)
>    - "amlogic,q201" (Meson gxm s912)
> -  - "nexbox,a95x" (Meson gxbb or Meson gxl s905x)
>    - "nexbox,a1" (Meson gxm s912)
> -- 
> 2.10.2
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 04/18] ARM64: dts: meson-gxbb-p20x: Fix Wifi node name
From: Rob Herring @ 2017-01-23 16:50 UTC (permalink / raw)
  To: Andreas Färber
  Cc: linux-amlogic, Carlo Caione, Kevin Hilman, linux-arm-kernel,
	linux-kernel, Neil Armstrong, Mark Rutland, Catalin Marinas,
	Will Deacon, devicetree
In-Reply-To: <20170120222118.21476-5-afaerber@suse.de>

On Fri, Jan 20, 2017 at 11:21:04PM +0100, Andreas Färber wrote:
> bcrmf -> brcmf
> 
> Fixes: ab3943fe57a2 ("ARM64: dts: meson-gxbb: Add P20x Wifi SDIO support")
> Cc: Neil Armstrong <narmstrong@baylibre.com>
> Signed-off-by: Andreas Färber <afaerber@suse.de>
> ---
>  arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
> index 4a96e0f6f926..5183c5ce90c6 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
> @@ -193,7 +193,7 @@
>  	vmmc-supply = <&vddao_3v3>;
>  	vqmmc-supply = <&vddio_boot>;
>  
> -	brcmf: bcrmf@1 {
> +	brcmf: brcmf@1 {

wifi@1

>  		reg = <1>;
>  		compatible = "brcm,bcm4329-fmac";
>  	};
> -- 
> 2.10.2
> 

^ permalink raw reply

* Re: [PATCH 10/18] ARM64: dts: meson-gxbb: Reorder nodes
From: Rob Herring @ 2017-01-23 16:52 UTC (permalink / raw)
  To: Andreas Färber
  Cc: linux-amlogic, Carlo Caione, Kevin Hilman, linux-arm-kernel,
	linux-kernel, Mark Rutland, Catalin Marinas, Will Deacon,
	devicetree
In-Reply-To: <20170120222118.21476-11-afaerber@suse.de>

On Fri, Jan 20, 2017 at 11:21:10PM +0100, Andreas Färber wrote:
> Sort nodes referenced by label alphabetically.

Seems to be pointless churn.

> 
> Signed-off-by: Andreas Färber <afaerber@suse.de>
> ---
>  arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 76 ++++++++++++++---------------
>  1 file changed, 38 insertions(+), 38 deletions(-)

^ permalink raw reply

* Re: [PATCH v5 2/3] mmc: sh_mobile_sdhi: explain clock bindings
From: Rob Herring @ 2017-01-23 16:55 UTC (permalink / raw)
  To: Chris Brandt
  Cc: Ulf Hansson, Mark Rutland, Simon Horman, Wolfram Sang,
	Geert Uytterhoeven, devicetree, linux-mmc, linux-renesas-soc
In-Reply-To: <20170121030604.7672-3-chris.brandt@renesas.com>

On Fri, Jan 20, 2017 at 10:06:03PM -0500, Chris Brandt wrote:
> In the case of a single clock source, you don't need names. However,
> if the controller has 2 clock sources, you need to name them correctly
> so the driver can find the 2nd one. The 2nd clock is for the internal
> card detect logic.
> 
> Signed-off-by: Chris Brandt <chris.brandt@renesas.com>
> ---
> v4:
> * just explain there might be 2 clocks, don't explain how
>   we will use them in the driver
> v3:
> * add more clarification about why there are sometimes 2 clocks
>   and what you should do with them.
> * remove 'status = "disabled"' from example
> v2:
> * fix spelling and change wording
> * changed clock name from "carddetect" to "cd"
> ---
>  Documentation/devicetree/bindings/mmc/tmio_mmc.txt | 24 ++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
> index a1650ed..1464c16 100644
> --- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
> +++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
> @@ -25,8 +25,32 @@ Required properties:
>  		"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
>  		"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
>  
> +- clocks: Most controllers only have 1 clock source per channel. However, on
> +	  some variations of this controller, the internal card detection

This should be explicit as to which compatible strings have 2 clocks and 
which have 1 clock.

> +	  logic that exists in this controller is sectioned off to be run by a
> +	  separate second clock source to allow the main core clock to be turned
> +	  off to save power.
> +	  If 2 clocks are specified by the hardware, you must name them as
> +	  "core" and "cd".
> +	  If the controller only has 1 clock, naming is not required.
> +
>  Optional properties:
>  - toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
>  - pinctrl-names: should be "default", "state_uhs"
>  - pinctrl-0: should contain default/high speed pin ctrl
>  - pinctrl-1: should contain uhs mode pin ctrl
> +
> +Example showing 2 clocks:
> +	sdhi0: sd@e804e000 {

mmc@...

> +		compatible = "renesas,sdhi-r7s72100";
> +		reg = <0xe804e000 0x100>;
> +		interrupts = <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH
> +			      GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH
> +			      GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
> +
> +		clocks = <&mstp12_clks R7S72100_CLK_SDHI00>,
> +			 <&mstp12_clks R7S72100_CLK_SDHI01>;
> +		clock-names = "core", "cd";
> +		cap-sd-highspeed;
> +		cap-sdio-irq;
> +	};
> -- 
> 2.10.1
> 
> 

^ permalink raw reply

* Re: [PATCH 10/18] ARM64: dts: meson-gxbb: Reorder nodes
From: Andreas Färber @ 2017-01-23 16:56 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Carlo Caione,
	Kevin Hilman, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
	Catalin Marinas, Will Deacon, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170123165208.4ujwm6rjz2b3dom5@rob-hp-laptop>

Am 23.01.2017 um 17:52 schrieb Rob Herring:
> On Fri, Jan 20, 2017 at 11:21:10PM +0100, Andreas Färber wrote:
>> Sort nodes referenced by label alphabetically.
> 
> Seems to be pointless churn.

We are constantly adding new nodes, so this avoids duplicates, mismerges
and uncertainties of where things should be added. Thought that would be
obvious, as I've been asked to do it elsewhere.

It also helps with diff -u'ing .dts files to see actual differences.

Regards,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply


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