* [PATCH 2/3 v2] dt-bindings: gpio: aspeed: Add SGPIO support
From: Hongwei Zhang @ 2019-07-12 20:14 UTC (permalink / raw)
To: Joel Stanley, Andrew Jeffery, Linus Walleij, devicetree
Cc: Hongwei Zhang, Rob Herring, Mark Rutland, Bartosz Golaszewski,
linux-aspeed, linux-kernel, linux-arm-kernel, linux-gpio
Add bindings to support SGPIO on AST2400 or AST2500.
Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
---
.../devicetree/bindings/gpio/sgpio-aspeed.txt | 43 ++++++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100755 Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
diff --git a/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
new file mode 100755
index 0000000..3ae2b79
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
@@ -0,0 +1,43 @@
+Aspeed SGPIO controller Device Tree Bindings
+-------------------------------------------
+
+Required properties:
+- compatible : Either "aspeed,ast2400-sgpio" or "aspeed,ast2500-sgpio"
+
+- #gpio-cells : Should be two
+ - First cell is the GPIO line number
+ - Second cell is used to specify optional
+ parameters (unused)
+
+- reg : Address and length of the register set for the device
+- gpio-controller : Marks the device node as a GPIO controller.
+- interrupts : Interrupt specifier (see interrupt bindings for
+ details)
+
+- interrupt-controller : Mark the GPIO controller as an interrupt-controller
+
+- nr-gpios : number of GPIO pins to serialise. (should be multiple of 8, up to 80 pins)
+ if not specified, defaults to 80.
+
+- clocks : A phandle to the APB clock for SGPM clock division
+
+- bus-frequency : SGPM CLK frequency, if not specified defaults to 1 MHz
+
+
+The sgpio and interrupt properties are further described in their respective bindings documentation:
+
+- Documentation/devicetree/bindings/sgpio/gpio.txt
+- Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+
+ Example:
+ sgpio@1e780200 {
+ #gpio-cells = <2>;
+ compatible = "aspeed,ast2500-sgpio";
+ gpio-controller;
+ interrupts = <40>;
+ reg = <0x1e780200 0x0100>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+ interrupt-controller;
+ nr-gpios = <80>;
+ bus-frequency = <1000000>;
+ };
--
2.7.4
^ permalink raw reply related
* Re: [PATCH v2 7/7] arm64: dts: allwinner: a64: enable ANX6345 bridge on Teres-I
From: Maxime Ripard @ 2019-07-12 20:15 UTC (permalink / raw)
To: Vasily Khoruzhick
Cc: Andrzej Hajda, Torsten Duwe, Harald Geyer, Chen-Yu Tsai,
Rob Herring, Mark Rutland, Thierry Reding, David Airlie,
Daniel Vetter, Laurent Pinchart, Icenowy Zheng, Sean Paul,
Greg Kroah-Hartman, Thomas Gleixner, dri-devel, devicetree,
arm-linux, linux-kernel
In-Reply-To: <CA+E=qVdFoT137pADfxz3uMwhOqjqrA9+6hBeOfbJxuH-M-3Pjw@mail.gmail.com>
On Wed, Jul 10, 2019 at 03:11:04PM -0700, Vasily Khoruzhick wrote:
> On Wed, Jul 10, 2019 at 4:40 AM Maxime Ripard <maxime.ripard@bootlin.com> wrote:
> > > > > There's another issue: if we introduce edp-connector we'll have to
> > > > > specify power up delays somewhere (in dts? or in platform driver?), so
> > > > > edp-connector doesn't really solve the issue of multiple panels with
> > > > > same motherboard.
> > > >
> > > > And that's what that compatible is about :)
> > >
> > > Sorry, I fail to see how it would be different from using existing
> > > panels infrastructure and different panels compatibles. I think Rob's
> > > idea was to introduce generic edp-connector.
> >
> > Again, there's no such thing as a generic edp-connector. The spec
> > doesn't define anything related to the power sequence for example.
> >
> > > If we can't make it generic then let's use panel infrastructure.
> >
> > Which uses a device specific compatible. Really, I'm not sure what
> > your objection and / or argument is here.
> >
> > In addition, when that was brought up in the discussion, you rejected
> > it because it was inconvenient:
> > https://patchwork.freedesktop.org/patch/283012/?series=56163&rev=1#comment_535206
>
> It is inconvenient, but I don't understand how having board-specific
> connectors fixes it.
How it would not fix it?
You'll have one connector, without the need to describe each and every
panel in the device tree and rely on the EDID instead, and you'll have
the option to power up the regulator you need.
I really don't understand what's the issue here, so let's take a step
back. What are is the issue , what are your requirements, and how
would you like that to be described ?
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* RE: [linux,dev-5.1 v1] dt-bindings: gpio: aspeed: Add SGPIO support
From: Hongwei Zhang @ 2019-07-12 20:18 UTC (permalink / raw)
To: Andrew Jeffery, devicetree@vger.kernel.org, Joel Stanley,
Linus Walleij
Cc: Rob Herring, Mark Rutland, linux-aspeed@lists.ozlabs.org,
linux-kernel@vger.kernel.org
In-Reply-To: <9c998f5f-42ef-43bd-b024-839ee00126de@www.fastmail.com>
Thanks for your review, Andrew,
Just submitted an updated binding document, with new proper subject line:
[PATCH 2/3 v2] dt-bindings: gpio: aspeed: Add SGPIO support
Regards,
--Hongwei
-----Original Message-----
From: Andrew Jeffery <andrew@aj.id.au>
Sent: Tuesday, July 9, 2019 10:16 PM
To: Hongwei Zhang <Hongweiz@ami.com>; devicetree@vger.kernel.org; Joel Stanley <joel@jms.id.au>; Linus Walleij <linus.walleij@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-aspeed@lists.ozlabs.org; linux-kernel@vger.kernel.org
Subject: Re: [linux,dev-5.1 v1] dt-bindings: gpio: aspeed: Add SGPIO support
On Thu, 4 Jul 2019, at 05:31, Hongwei Zhang wrote:
> Add bindings to support SGPIO on AST2400 or AST2500.
>
> Signed-off-by: Hongwei Zhang <hongweiz@ami.com>
> ---
> .../devicetree/bindings/gpio/sgpio-aspeed.txt | 36 ++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
> create mode 100644
> Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
>
> diff --git a/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
> b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
> new file mode 100644
> index 0000000..f5fc6ef
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt
> @@ -0,0 +1,36 @@
> +Aspeed SGPIO controller Device Tree Bindings
> +-------------------------------------------
> +
> +Required properties:
> +- compatible : Either "aspeed,ast2400-sgpio" or "aspeed,ast2500-sgpio"
> +
> +- #gpio-cells : Should be two
> + - First cell is the GPIO line number
> + - Second cell is used to specify optional
> + parameters (unused)
> +
> +- reg : Address and length of the register set for the device
> +- gpio-controller : Marks the device node as a GPIO controller.
> +- interrupts : Interrupt specifier (see interrupt bindings for
> + details)
> +- interrupt-controller : Mark the GPIO controller as an
> interrupt-controller
As this is a serial GPIO controller, a critical piece of configuration information is how many GPIOs we wish to serialise. This is done in multiples of 8, up to 80 pins.
The bindings need to describe the "ngpios" property from the generic GPIO bindings and how this affects the behaviour of the controller.
We also need to add the "bus-frequency" property here to control the rate of SGPMCK.
> +
> +Optional properties:
> +
> +- clocks : A phandle to the clock to use for debounce
> timings
We need this, but not for the reason specified, and it should be a required property. We need PCLK (the APB clock) to derive the SGPIO bus frequency. Despite what the datasheet blurb says, there's no debounce control for the SGPIO master (this is a copy/paste mistake from the description of the parallel GPIO master).
> +
> +The sgpio and interrupt properties are further described in their
> respective
> +bindings documentation:
> +
> +- Documentation/devicetree/bindings/sgpio/gpio.txt
> +-
> +Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> +
> + Example:
> + sgpio@1e780200 {
> + #gpio-cells = <2>;
> + compatible = "aspeed,ast2500-sgpio";
> + gpio-controller;
> + interrupts = <40>;
> + reg = <0x1e780200 0x0100>;
> + interrupt-controller;
> + };
You'll need to fix up the example after making the changes mentioned above.
Andrew
> --
> 2.7.4
>
>
^ permalink raw reply
* [PATCH 1/2] bus: imx-weim: optionally enable burst clock mode
From: Sven Van Asbroeck @ 2019-07-12 20:43 UTC (permalink / raw)
To: Shawn Guo, Rob Herring
Cc: NXP Linux Team, Kees Cook, linux-kernel, linux-arm-kernel,
Mark Rutland, Sascha Hauer, devicetree, Fabio Estevam,
Pengutronix Kernel Team, Arnd Bergmann
To enable burst clock mode, add the fsl,burst-clk-enable
property to the weim bus's devicetree node.
Example:
weim: weim@21b8000 {
compatible = "fsl,imx6q-weim";
reg = <0x021b8000 0x4000>;
clocks = <&clks 196>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x08000000>;
fsl,weim-cs-gpr = <&gpr>;
fsl,burst-clk-enable;
client-device@0,0 {
compatible = "something";
reg = <0 0 0x02000000>;
#address-cells = <1>;
#size-cells = <1>;
bank-width = <2>;
fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000
0x0000c000 0x1404a38e 0x00000000>;
};
};
Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
drivers/bus/imx-weim.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index db74334ca5ef..cb7d5504a22a 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -19,6 +19,8 @@ struct imx_weim_devtype {
unsigned int cs_count;
unsigned int cs_regs_count;
unsigned int cs_stride;
+ unsigned int wcr_offset;
+ unsigned int wcr_bcm;
};
static const struct imx_weim_devtype imx1_weim_devtype = {
@@ -37,6 +39,8 @@ static const struct imx_weim_devtype imx50_weim_devtype = {
.cs_count = 4,
.cs_regs_count = 6,
.cs_stride = 0x18,
+ .wcr_offset = 0x90,
+ .wcr_bcm = BIT(0),
};
static const struct imx_weim_devtype imx51_weim_devtype = {
@@ -192,6 +196,7 @@ static int __init weim_parse_dt(struct platform_device *pdev,
struct device_node *child;
int ret, have_child = 0;
struct cs_timing_state ts = {};
+ u32 reg;
if (devtype == &imx50_weim_devtype) {
ret = imx_weim_gpr_setup(pdev);
@@ -199,6 +204,17 @@ static int __init weim_parse_dt(struct platform_device *pdev,
return ret;
}
+ if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) {
+ if (devtype->wcr_bcm) {
+ reg = readl(base + devtype->wcr_offset);
+ writel(reg | devtype->wcr_bcm,
+ base + devtype->wcr_offset);
+ } else {
+ dev_err(&pdev->dev, "burst clk mode not supported.\n");
+ return -EINVAL;
+ }
+ }
+
for_each_available_child_of_node(pdev->dev.of_node, child) {
ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts);
if (ret)
--
2.17.1
^ permalink raw reply related
* [PATCH 2/2] dt-bindings: bus: imx-weim: document optional burst clock mode
From: Sven Van Asbroeck @ 2019-07-12 20:43 UTC (permalink / raw)
To: Shawn Guo, Rob Herring
Cc: NXP Linux Team, Kees Cook, linux-kernel, linux-arm-kernel,
Mark Rutland, Sascha Hauer, devicetree, Fabio Estevam,
Pengutronix Kernel Team, Arnd Bergmann
In-Reply-To: <20190712204316.16783-1-TheSven73@gmail.com>
An optional devicetree property was added to the imx-weim driver,
which if present instructs it to operate in burst clock mode.
Update the dt-bindings to reflect this.
Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
Documentation/devicetree/bindings/bus/imx-weim.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt
index dda7d6d66479..1b1d1c5c21ea 100644
--- a/Documentation/devicetree/bindings/bus/imx-weim.txt
+++ b/Documentation/devicetree/bindings/bus/imx-weim.txt
@@ -44,6 +44,10 @@ Optional properties:
what bootloader sets up in IOMUXC_GPR1[11:0] will be
used.
+ - fsl,burst-clk-enable For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
+ devices, the presence of this property indicates that
+ the weim bus should operate in Burst Clock Mode.
+
Timing property for child nodes. It is mandatory, not optional.
- fsl,weim-cs-timing: The timing array, contains timing values for the
--
2.17.1
^ permalink raw reply related
* Re: [PATCH 1/3] dt-bindings: dma: Add YAML schemas for the generic DMA bindings
From: Peter Ujfalusi @ 2019-07-12 21:27 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Jon Hunter
Cc: Mark Rutland, devicetree, Chen-Yu Tsai, Vinod Koul,
open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM, Frank Rowand,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <CAL_JsqLh8QEwa-3v9-Vs=e55k3GyyvwsNVxmdBMWMD_VxqKMyA@mail.gmail.com>
On 11.7.2019 20.33, Rob Herring wrote:
> On Thu, Jul 11, 2019 at 3:34 AM Maxime Ripard <maxime.ripard@bootlin.com> wrote:
>>
>> The DMA controllers and consumers have a bunch of generic properties that
>> are needed in a device tree. Add a YAML schemas for those.
>>
>> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
>> ---
>> .../devicetree/bindings/dma/dma-consumer.yaml | 60 +++++++++
>
> This already exists in the dt-schema/schemas/dma/dma.yaml though not
> the descriptions because that needs relicensing.
>
> Looks like we need NVidia's (Jon H) and TI's (Peter U) permission.
If I'm not mistaken the new license is GPL-2.0, if so I don't see any
issue, but I'll ask our legal to be sure.
And one comment for the change.
>
>> .../bindings/dma/dma-controller.yaml | 79 ++++++++++++
>> Documentation/devicetree/bindings/dma/dma.txt | 114 +-----------------
>> 3 files changed, 140 insertions(+), 113 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/dma/dma-consumer.yaml
>> create mode 100644 Documentation/devicetree/bindings/dma/dma-controller.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/dma/dma-consumer.yaml b/Documentation/devicetree/bindings/dma/dma-consumer.yaml
>> new file mode 100644
>> index 000000000000..2f6315863ad1
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/dma/dma-consumer.yaml
>> @@ -0,0 +1,60 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/dma/dma-consumer.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: DMA Consumer Generic Binding
>> +
>> +maintainers:
>> + - Vinod Koul <vkoul@kernel.org>
>> +
>> +select: true
>> +
>> +properties:
>> + dmas:
>> + description:
>> + List of one or more DMA specifiers, each consisting of
>> + - A phandle pointing to DMA controller node
>> + - A number of integer cells, as determined by the
>> + \#dma-cells property in the node referenced by phandle
>> + containing DMA controller specific information. This
>> + typically contains a DMA request line number or a
>> + channel number, but can contain any data that is
>> + required for configuring a channel.
>> +
>> + dma-names:
>> + description:
>> + Contains one identifier string for each DMA specifier in the
>> + dmas property. The specific strings that can be used are defined
>> + in the binding of the DMA client device. Multiple DMA
>> + specifiers can be used to represent alternatives and in this
>> + case the dma-names for those DMA specifiers must be identical
>> + (see examples).
>> +
>> +dependencies:
>> + dma-names: [ dmas ]
>> +
>> +examples:
>> + - |
>> + /* A device with one DMA read channel, one DMA write channel */
>> + i2c1: i2c@1 {
>> + /* ... */
>> + dmas = <&dma 2>, /* read channel */
>> + <&dma 3>; /* write channel */
>> + dma-names = "rx", "tx";
>> + /* ... */
>> + };
>> +
>> + - |
>> + /* A single read-write channel with three alternative DMA controllers */
>> + dmas = <&dma1 5>, <&dma2 7>, <&dma3 2>;
>> + dma-names = "rx-tx", "rx-tx", "rx-tx";
>> +
>> + - |
>> + /* A device with three channels, one of which has two alternatives */
>> + dmas = <&dma1 2>, /* read channel */
>> + <&dma1 3>, /* write channel */
>> + <&dma2 0>, /* error read */
>> + <&dma3 0>; /* alternative error read */
>> + dma-names = "rx", "tx", "error", "error";
>> diff --git a/Documentation/devicetree/bindings/dma/dma-controller.yaml b/Documentation/devicetree/bindings/dma/dma-controller.yaml
>> new file mode 100644
>> index 000000000000..17c650131b78
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/dma/dma-controller.yaml
>> @@ -0,0 +1,79 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/dma/dma-controller.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: DMA Controller Generic Binding
>> +
>> +maintainers:
>> + - Vinod Koul <vkoul@kernel.org>
>> +
>> +description:
>> + Generic binding to provide a way for a driver using DMA Engine to
>> + retrieve the DMA request or channel information that goes from a
>> + hardware device to a DMA controller.
>> +
>> +properties:
>> + $nodename:
>> + pattern: "^dma-controller(@.*)?$"
>> +
>> + "#dma-cells":
>> + # minimum: 1
>> + description:
>> + Used to provide DMA controller specific information.
>> +
>> + dma-channel-masks:
>> + $ref: /schemas/types.yaml#definitions/uint32
>> + description:
>> + Bitmask of available DMA channels in ascending order that are
>> + not reserved by firmware and are available to the
>> + kernel. i.e. first channel corresponds to LSB.
>> +
>> + dma-channels:
>> + $ref: /schemas/types.yaml#definitions/uint32
>> + description:
>> + Number of DMA channels supported by the controller.
>> +
>> + dma-masters:
>> + $ref: /schemas/types.yaml#definitions/phandle-array
>> + description:
>> + DMA routers are transparent IP blocks used to route DMA request
>> + lines from devices to the DMA controller. Some SoCs (like TI
>> + DRA7x) have more peripherals integrated with DMA requests than
>> + what the DMA controller can handle directly.
>> +
>> + In such a case, dma-masters is an array of phandle to the DMA
>> + controllers the router can direct the signal to.
It is no longer clear what is needed and what is optional anymore and if
I would need to look up how a DMA router node should look like I will be
in trouble, iow if I need to figure out how to describe an SoC with DMA
controller and DMA event router.
>> +
>> + dma-requests:
>> + $ref: /schemas/types.yaml#definitions/uint32
>> + description:
>> + Number of DMA request signals supported by the controller.
>> +
>> +examples:
>> + - |
>> + dma: dma@48000000 {
>
> dma-controller@...
>
> This is a case where I'd like some check that the schema is actually
> applied to the schema's example.
>
>> + compatible = "ti,omap-sdma";
>> + reg = <0x48000000 0x1000>;
>> + interrupts = <0 12 0x4
>> + 0 13 0x4
>> + 0 14 0x4
>> + 0 15 0x4>;
>> + #dma-cells = <1>;
>> + dma-channels = <32>;
>> + dma-requests = <127>;
>> + dma-channel-mask = <0xfffe>;
>> + };
>> +
>> + - |
>> + sdma_xbar: dma-router@4a002b78 {
>> + compatible = "ti,dra7-dma-crossbar";
>> + reg = <0x4a002b78 0xfc>;
>> + #dma-cells = <1>;
>> + dma-requests = <205>;
>> + ti,dma-safe-map = <0>;
>> + dma-masters = <&sdma>;
>> + };
>> +
>> +...
>> diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt
>> index eeb4e4d1771e..90a67a016a48 100644
>> --- a/Documentation/devicetree/bindings/dma/dma.txt
>> +++ b/Documentation/devicetree/bindings/dma/dma.txt
>> @@ -1,113 +1 @@
>> -* Generic DMA Controller and DMA request bindings
>> -
>> -Generic binding to provide a way for a driver using DMA Engine to retrieve the
>> -DMA request or channel information that goes from a hardware device to a DMA
>> -controller.
>> -
>> -
>> -* DMA controller
>> -
>> -Required property:
>> -- #dma-cells: Must be at least 1. Used to provide DMA controller
>> - specific information. See DMA client binding below for
>> - more details.
>> -
>> -Optional properties:
>> -- dma-channels: Number of DMA channels supported by the controller.
>> -- dma-requests: Number of DMA request signals supported by the
>> - controller.
>> -- dma-channel-mask: Bitmask of available DMA channels in ascending order
>> - that are not reserved by firmware and are available to
>> - the kernel. i.e. first channel corresponds to LSB.
>> -
>> -Example:
>> -
>> - dma: dma@48000000 {
>> - compatible = "ti,omap-sdma";
>> - reg = <0x48000000 0x1000>;
>> - interrupts = <0 12 0x4
>> - 0 13 0x4
>> - 0 14 0x4
>> - 0 15 0x4>;
>> - #dma-cells = <1>;
>> - dma-channels = <32>;
>> - dma-requests = <127>;
>> - dma-channel-mask = <0xfffe>
>> - };
>> -
>> -* DMA router
>> -
>> -DMA routers are transparent IP blocks used to route DMA request lines from
>> -devices to the DMA controller. Some SoCs (like TI DRA7x) have more peripherals
>> -integrated with DMA requests than what the DMA controller can handle directly.
>> -
>> -Required property:
>> -- dma-masters: phandle of the DMA controller or list of phandles for
>> - the DMA controllers the router can direct the signal to.
>> -- #dma-cells: Must be at least 1. Used to provide DMA router specific
>> - information. See DMA client binding below for more
>> - details.
>> -
>> -Optional properties:
>> -- dma-requests: Number of incoming request lines the router can handle.
>> -- In the node pointed by the dma-masters:
>> - - dma-requests: The router driver might need to look for this in order
>> - to configure the routing.
>> -
>> -Example:
>> - sdma_xbar: dma-router@4a002b78 {
>> - compatible = "ti,dra7-dma-crossbar";
>> - reg = <0x4a002b78 0xfc>;
>> - #dma-cells = <1>;
>> - dma-requests = <205>;
>> - ti,dma-safe-map = <0>;
>> - dma-masters = <&sdma>;
>> - };
>> -
>> -* DMA client
>> -
>> -Client drivers should specify the DMA property using a phandle to the controller
>> -followed by DMA controller specific data.
>> -
>> -Required property:
>> -- dmas: List of one or more DMA specifiers, each consisting of
>> - - A phandle pointing to DMA controller node
>> - - A number of integer cells, as determined by the
>> - #dma-cells property in the node referenced by phandle
>> - containing DMA controller specific information. This
>> - typically contains a DMA request line number or a
>> - channel number, but can contain any data that is
>> - required for configuring a channel.
>> -- dma-names: Contains one identifier string for each DMA specifier in
>> - the dmas property. The specific strings that can be used
>> - are defined in the binding of the DMA client device.
>> - Multiple DMA specifiers can be used to represent
>> - alternatives and in this case the dma-names for those
>> - DMA specifiers must be identical (see examples).
>> -
>> -Examples:
>> -
>> -1. A device with one DMA read channel, one DMA write channel:
>> -
>> - i2c1: i2c@1 {
>> - ...
>> - dmas = <&dma 2 /* read channel */
>> - &dma 3>; /* write channel */
>> - dma-names = "rx", "tx";
>> - ...
>> - };
>> -
>> -2. A single read-write channel with three alternative DMA controllers:
>> -
>> - dmas = <&dma1 5
>> - &dma2 7
>> - &dma3 2>;
>> - dma-names = "rx-tx", "rx-tx", "rx-tx";
>> -
>> -3. A device with three channels, one of which has two alternatives:
>> -
>> - dmas = <&dma1 2 /* read channel */
>> - &dma1 3 /* write channel */
>> - &dma2 0 /* error read */
>> - &dma3 0>; /* alternative error read */
>> - dma-names = "rx", "tx", "error", "error";
>> +This file has been moved to dma-controller.yaml.
>> --
>> 2.21.0
>>
--
Peter
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* Re: [PATCH 1/3] dt-bindings: dma: Add YAML schemas for the generic DMA bindings
From: Rob Herring @ 2019-07-12 21:49 UTC (permalink / raw)
To: Peter Ujfalusi
Cc: Mark Rutland, devicetree, Maxime Ripard, Chen-Yu Tsai, Jon Hunter,
Vinod Koul, open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM,
Frank Rowand,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <28a776e2-52fa-60e9-a7d9-8caeec78f1d1@ti.com>
On Fri, Jul 12, 2019 at 3:24 PM Peter Ujfalusi <peter.ujfalusi@ti.com> wrote:
>
>
>
> On 11.7.2019 20.33, Rob Herring wrote:
> > On Thu, Jul 11, 2019 at 3:34 AM Maxime Ripard <maxime.ripard@bootlin.com> wrote:
> >>
> >> The DMA controllers and consumers have a bunch of generic properties that
> >> are needed in a device tree. Add a YAML schemas for those.
> >>
> >> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> >> ---
> >> .../devicetree/bindings/dma/dma-consumer.yaml | 60 +++++++++
> >
> > This already exists in the dt-schema/schemas/dma/dma.yaml though not
> > the descriptions because that needs relicensing.
> >
> > Looks like we need NVidia's (Jon H) and TI's (Peter U) permission.
>
> If I'm not mistaken the new license is GPL-2.0, if so I don't see any
> issue, but I'll ask our legal to be sure.
To move it to the schema repository we need it to be (GPL-2.0 OR
BSD-2-Clause). I'd prefer to have the core bindings in the dt-schema
repo rather than add to the kernel.
Rob
^ permalink raw reply
* [PATCH v5 00/11] Solve postboot supplier cleanup and optimize probe ordering
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
Add device-links to track functional dependencies between devices
after they are created (but before they are probed) by looking at
their common DT bindings like clocks, interconnects, etc.
Having functional dependencies automatically added before the devices
are probed, provides the following benefits:
- Optimizes device probe order and avoids the useless work of
attempting probes of devices that will not probe successfully
(because their suppliers aren't present or haven't probed yet).
For example, in a commonly available mobile SoC, registering just
one consumer device's driver at an initcall level earlier than the
supplier device's driver causes 11 failed probe attempts before the
consumer device probes successfully. This was with a kernel with all
the drivers statically compiled in. This problem gets a lot worse if
all the drivers are loaded as modules without direct symbol
dependencies.
- Supplier devices like clock providers, interconnect providers, etc
need to keep the resources they provide active and at a particular
state(s) during boot up even if their current set of consumers don't
request the resource to be active. This is because the rest of the
consumers might not have probed yet and turning off the resource
before all the consumers have probed could lead to a hang or
undesired user experience.
Some frameworks (Eg: regulator) handle this today by turning off
"unused" resources at late_initcall_sync and hoping all the devices
have probed by then. This is not a valid assumption for systems with
loadable modules. Other frameworks (Eg: clock) just don't handle
this due to the lack of a clear signal for when they can turn off
resources. This leads to downstream hacks to handle cases like this
that can easily be solved in the upstream kernel.
By linking devices before they are probed, we give suppliers a clear
count of the number of dependent consumers. Once all of the
consumers are active, the suppliers can turn off the unused
resources without making assumptions about the number of consumers.
By default we just add device-links to track "driver presence" (probe
succeeded) of the supplier device. If any other functionality provided
by device-links are needed, it is left to the consumer/supplier
devices to change the link when they probe.
v1 -> v2:
- Drop patch to speed up of_find_device_by_node()
- Drop depends-on property and use existing bindings
v2 -> v3:
- Refactor the code to have driver core initiate the linking of devs
- Have driver core link consumers to supplier before it's probed
- Add support for drivers to edit the device links before probing
v3 -> v4
- Tested edit_links() on system with cyclic dependency. Works.
- Added some checks to make sure device link isn't attempted from
parent device node to child device node.
- Added way to pause/resume sync_state callbacks across
of_platform_populate().
- Recursively parse DT node to create device links from parent to
suppliers of parent and all child nodes.
v4 -> v5
- Fixed copy-pasta bugs with linked list handling
- Walk up the phandle reference till I find an actual device (needed
for regulators to work)
- Added support for linking devices from regulator DT bindings
- Tested the whole series again to make sure cyclic dependencies are
broken with edit_links() and regulator links are created properly.
I could probably squash some of the patches, but leaving them like this
because I think it's easier to understand this way.
I've also not updated this patch series to handle the new patch [1] from
Rafael. Will do that once this patch series is close to being Acked.
[1] - https://lore.kernel.org/lkml/3121545.4lOhFoIcdQ@kreacher/
-Saravana
Saravana Kannan (11):
driver core: Add support for linking devices during device addition
of/platform: Add functional dependency link from DT bindings
driver core: Add sync_state driver/bus callback
driver core: Add edit_links() callback for drivers
driver core: Add APIs to pause/resume sync state callbacks
of/platform: Pause/resume sync state in of_platform_populate()
of/platform: Sanity check DT bindings before creating device links
of/platform: Make sure supplier DT node is device when creating device
links
of/platform: Create device links for all child-supplier depencencies
of/platform: Add functional dependency link from DT regulator bindings
of/platform: Don't create device links default busses
.../admin-guide/kernel-parameters.txt | 5 +
drivers/base/core.c | 168 ++++++++++++++++++
drivers/base/dd.c | 29 +++
drivers/of/platform.c | 132 ++++++++++++++
include/linux/device.h | 47 +++++
5 files changed, 381 insertions(+)
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply
* [PATCH v5 01/11] driver core: Add support for linking devices during device addition
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
When devices are added, the bus might want to create device links to track
functional dependencies between supplier and consumer devices. This
tracking of supplier-consumer relationship allows optimizing device probe
order and tracking whether all consumers of a supplier are active. The
add_links bus callback is added to support this.
However, when consumer devices are added, they might not have a supplier
device to link to despite needing mandatory resources/functionality from
one or more suppliers. A waiting_for_suppliers list is created to track
such consumers and retry linking them when new devices get added.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/base/core.c | 83 ++++++++++++++++++++++++++++++++++++++++++
include/linux/device.h | 8 ++++
2 files changed, 91 insertions(+)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index fd7511e04e62..0705926d362f 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -44,6 +44,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup);
#endif
/* Device links support. */
+static LIST_HEAD(wait_for_suppliers);
+static DEFINE_MUTEX(wfs_lock);
#ifdef CONFIG_SRCU
static DEFINE_MUTEX(device_links_lock);
@@ -401,6 +403,51 @@ struct device_link *device_link_add(struct device *consumer,
}
EXPORT_SYMBOL_GPL(device_link_add);
+/**
+ * device_link_wait_for_supplier - Mark device as waiting for supplier
+ * @consumer: Consumer device
+ *
+ * Marks the consumer device as waiting for suppliers to become available. The
+ * consumer device will never be probed until it's unmarked as waiting for
+ * suppliers. The caller is responsible for adding the link to the supplier
+ * once the supplier device is present.
+ *
+ * This function is NOT meant to be called from the probe function of the
+ * consumer but rather from code that creates/adds the consumer device.
+ */
+static void device_link_wait_for_supplier(struct device *consumer)
+{
+ mutex_lock(&wfs_lock);
+ list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers);
+ mutex_unlock(&wfs_lock);
+}
+
+/**
+ * device_link_check_waiting_consumers - Try to unmark waiting consumers
+ *
+ * Loops through all consumers waiting on suppliers and tries to add all their
+ * supplier links. If that succeeds, the consumer device is unmarked as waiting
+ * for suppliers. Otherwise, they are left marked as waiting on suppliers,
+ *
+ * The add_links bus callback is expected to return 0 if it has found and added
+ * all the supplier links for the consumer device. It should return an error if
+ * it isn't able to do so.
+ *
+ * The caller of device_link_wait_for_supplier() is expected to call this once
+ * it's aware of potential suppliers becoming available.
+ */
+static void device_link_check_waiting_consumers(void)
+{
+ struct device *dev, *tmp;
+
+ mutex_lock(&wfs_lock);
+ list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
+ links.needs_suppliers)
+ if (!dev->bus->add_links(dev))
+ list_del_init(&dev->links.needs_suppliers);
+ mutex_unlock(&wfs_lock);
+}
+
static void device_link_free(struct device_link *link)
{
while (refcount_dec_not_one(&link->rpm_active))
@@ -535,6 +582,19 @@ int device_links_check_suppliers(struct device *dev)
struct device_link *link;
int ret = 0;
+ /*
+ * If a device is waiting for one or more suppliers (in
+ * wait_for_suppliers list), it is not ready to probe yet. So just
+ * return -EPROBE_DEFER without having to check the links with existing
+ * suppliers.
+ */
+ mutex_lock(&wfs_lock);
+ if (!list_empty(&dev->links.needs_suppliers)) {
+ mutex_unlock(&wfs_lock);
+ return -EPROBE_DEFER;
+ }
+ mutex_unlock(&wfs_lock);
+
device_links_write_lock();
list_for_each_entry(link, &dev->links.suppliers, c_node) {
@@ -812,6 +872,10 @@ static void device_links_purge(struct device *dev)
{
struct device_link *link, *ln;
+ mutex_lock(&wfs_lock);
+ list_del(&dev->links.needs_suppliers);
+ mutex_unlock(&wfs_lock);
+
/*
* Delete all of the remaining links from this device to any other
* devices (either consumers or suppliers).
@@ -1673,6 +1737,7 @@ void device_initialize(struct device *dev)
#endif
INIT_LIST_HEAD(&dev->links.consumers);
INIT_LIST_HEAD(&dev->links.suppliers);
+ INIT_LIST_HEAD(&dev->links.needs_suppliers);
dev->links.status = DL_DEV_NO_DRIVER;
}
EXPORT_SYMBOL_GPL(device_initialize);
@@ -2108,6 +2173,24 @@ int device_add(struct device *dev)
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
+
+ /*
+ * Check if any of the other devices (consumers) have been waiting for
+ * this device (supplier) to be added so that they can create a device
+ * link to it.
+ *
+ * This needs to happen after device_pm_add() because device_link_add()
+ * requires the supplier be registered before it's called.
+ *
+ * But this also needs to happe before bus_probe_device() to make sure
+ * waiting consumers can link to it before the driver is bound to the
+ * device and the driver sync_state callback is called for this device.
+ */
+ device_link_check_waiting_consumers();
+
+ if (dev->bus && dev->bus->add_links && dev->bus->add_links(dev))
+ device_link_wait_for_supplier(dev);
+
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
diff --git a/include/linux/device.h b/include/linux/device.h
index 848fc71c6ba6..7f8ae7e5fc6b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -77,6 +77,11 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
* -EPROBE_DEFER it will queue the device for deferred probing.
* @uevent: Called when a device is added, removed, or a few other things
* that generate uevents to add the environment variables.
+ * @add_links: Called, perhaps multiple times, when a new device is added to
+ * this bus. The function is expected to create all the device
+ * links for the new device and return 0 if it was completed
+ * successfully or return an error if it needs to be reattempted
+ * in the future.
* @probe: Called when a new device or driver add to this bus, and callback
* the specific driver's probe to initial the matched device.
* @remove: Called when a device removed from this bus.
@@ -121,6 +126,7 @@ struct bus_type {
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
+ int (*add_links)(struct device *dev);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
@@ -888,11 +894,13 @@ enum dl_dev_state {
* struct dev_links_info - Device data related to device links.
* @suppliers: List of links to supplier devices.
* @consumers: List of links to consumer devices.
+ * @needs_suppliers: Hook to global list of devices waiting for suppliers.
* @status: Driver status information.
*/
struct dev_links_info {
struct list_head suppliers;
struct list_head consumers;
+ struct list_head needs_suppliers;
enum dl_dev_state status;
};
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 02/11] of/platform: Add functional dependency link from DT bindings
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand, Jonathan Corbet
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team, linux-doc
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
Add device-links after the devices are created (but before they are
probed) by looking at common DT bindings like clocks and
interconnects.
Automatically adding device-links for functional dependencies at the
framework level provides the following benefits:
- Optimizes device probe order and avoids the useless work of
attempting probes of devices that will not probe successfully
(because their suppliers aren't present or haven't probed yet).
For example, in a commonly available mobile SoC, registering just
one consumer device's driver at an initcall level earlier than the
supplier device's driver causes 11 failed probe attempts before the
consumer device probes successfully. This was with a kernel with all
the drivers statically compiled in. This problem gets a lot worse if
all the drivers are loaded as modules without direct symbol
dependencies.
- Supplier devices like clock providers, interconnect providers, etc
need to keep the resources they provide active and at a particular
state(s) during boot up even if their current set of consumers don't
request the resource to be active. This is because the rest of the
consumers might not have probed yet and turning off the resource
before all the consumers have probed could lead to a hang or
undesired user experience.
Some frameworks (Eg: regulator) handle this today by turning off
"unused" resources at late_initcall_sync and hoping all the devices
have probed by then. This is not a valid assumption for systems with
loadable modules. Other frameworks (Eg: clock) just don't handle
this due to the lack of a clear signal for when they can turn off
resources. This leads to downstream hacks to handle cases like this
that can easily be solved in the upstream kernel.
By linking devices before they are probed, we give suppliers a clear
count of the number of dependent consumers. Once all of the
consumers are active, the suppliers can turn off the unused
resources without making assumptions about the number of consumers.
By default we just add device-links to track "driver presence" (probe
succeeded) of the supplier device. If any other functionality provided
by device-links are needed, it is left to the consumer/supplier
devices to change the link when they probe.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
.../admin-guide/kernel-parameters.txt | 5 ++
drivers/of/platform.c | 57 +++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 138f6664b2e2..109b4310844f 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3141,6 +3141,11 @@
This can be set from sysctl after boot.
See Documentation/sysctl/vm.txt for details.
+ of_devlink [KNL] Make device links from common DT bindings. Useful
+ for optimizing probe order and making sure resources
+ aren't turned off before the consumer devices have
+ probed.
+
ohci1394_dma=early [HW] enable debugging via the ohci1394 driver.
See Documentation/debugging-via-ohci1394.txt for more
info.
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 04ad312fd85b..0930f9f89571 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -509,6 +509,62 @@ int of_platform_default_populate(struct device_node *root,
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
+static int of_link_binding(struct device *dev,
+ const char *binding, const char *cell)
+{
+ struct of_phandle_args sup_args;
+ struct platform_device *sup_dev;
+ unsigned int i = 0, links = 0;
+ u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER;
+
+ while (!of_parse_phandle_with_args(dev->of_node, binding, cell, i,
+ &sup_args)) {
+ i++;
+ sup_dev = of_find_device_by_node(sup_args.np);
+ of_node_put(sup_args.np);
+ if (!sup_dev)
+ continue;
+ if (device_link_add(dev, &sup_dev->dev, dl_flags))
+ links++;
+ put_device(&sup_dev->dev);
+ }
+ if (links < i)
+ return -ENODEV;
+ return 0;
+}
+
+static bool of_devlink;
+core_param(of_devlink, of_devlink, bool, 0);
+
+/*
+ * List of bindings and their cell names (use NULL if no cell names) from which
+ * device links need to be created.
+ */
+static const char * const link_bindings[] = {
+ "clocks", "#clock-cells",
+ "interconnects", "#interconnect-cells",
+};
+
+static int of_link_to_suppliers(struct device *dev)
+{
+ unsigned int i = 0;
+ bool done = true;
+
+ if (!of_devlink)
+ return 0;
+ if (unlikely(!dev->of_node))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(link_bindings) / 2; i++)
+ if (of_link_binding(dev, link_bindings[i * 2],
+ link_bindings[i * 2 + 1]))
+ done = false;
+
+ if (!done)
+ return -ENODEV;
+ return 0;
+}
+
#ifndef CONFIG_PPC
static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "qcom,rmtfs-mem" },
@@ -524,6 +580,7 @@ static int __init of_platform_default_populate_init(void)
if (!of_have_populated_dt())
return -ENODEV;
+ platform_bus_type.add_links = of_link_to_suppliers;
/*
* Handle certain compatibles explicitly, since we don't want to create
* platform_devices for every node in /reserved-memory with a
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 03/11] driver core: Add sync_state driver/bus callback
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
This sync_state driver/bus callback is called once all the consumers
of a supplier have probed successfully.
This allows the supplier device's driver/bus to sync the supplier
device's state to the software state with the guarantee that all the
consumers are actively managing the resources provided by the supplier
device.
To maintain backwards compatibility and ease transition from existing
frameworks and resource cleanup schemes, late_initcall_sync is the
earliest when the sync_state callback might be called.
There is no upper bound on the time by which the sync_state callback
has to be called. This is because if a consumer device never probes,
the supplier has to maintain its resources in the state left by the
bootloader. For example, if the bootloader leaves the display
backlight at a fixed voltage and the backlight driver is never probed,
you don't want the backlight to ever be turned off after boot up.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/base/core.c | 39 +++++++++++++++++++++++++++++++++++++++
drivers/of/platform.c | 9 +++++++++
include/linux/device.h | 19 +++++++++++++++++++
3 files changed, 67 insertions(+)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 0705926d362f..8b8b812d26f1 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -46,6 +46,7 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup);
/* Device links support. */
static LIST_HEAD(wait_for_suppliers);
static DEFINE_MUTEX(wfs_lock);
+static bool supplier_sync_state_enabled;
#ifdef CONFIG_SRCU
static DEFINE_MUTEX(device_links_lock);
@@ -614,6 +615,41 @@ int device_links_check_suppliers(struct device *dev)
return ret;
}
+static void __device_links_supplier_sync_state(struct device *dev)
+{
+ struct device_link *link;
+
+ if (dev->state_synced)
+ return;
+
+ list_for_each_entry(link, &dev->links.consumers, s_node) {
+ if (link->flags & DL_FLAG_STATELESS)
+ continue;
+ if (link->status != DL_STATE_ACTIVE)
+ return;
+ }
+
+ if (dev->bus->sync_state)
+ dev->bus->sync_state(dev);
+ else if (dev->driver && dev->driver->sync_state)
+ dev->driver->sync_state(dev);
+
+ dev->state_synced = true;
+}
+
+int device_links_supplier_sync_state(struct device *dev, void *data)
+{
+ device_links_write_lock();
+ __device_links_supplier_sync_state(dev);
+ device_links_write_unlock();
+ return 0;
+}
+
+void device_links_supplier_sync_state_enable(void)
+{
+ supplier_sync_state_enabled = true;
+}
+
/**
* device_links_driver_bound - Update device links after probing its driver.
* @dev: Device to update the links for.
@@ -658,6 +694,9 @@ void device_links_driver_bound(struct device *dev)
WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);
WRITE_ONCE(link->status, DL_STATE_ACTIVE);
+
+ if (supplier_sync_state_enabled)
+ __device_links_supplier_sync_state(link->supplier);
}
dev->links.status = DL_DEV_DRIVER_BOUND;
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 0930f9f89571..4d12d6658999 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -601,6 +601,15 @@ static int __init of_platform_default_populate_init(void)
return 0;
}
arch_initcall_sync(of_platform_default_populate_init);
+
+static int __init of_platform_sync_state_init(void)
+{
+ device_links_supplier_sync_state_enable();
+ bus_for_each_dev(&platform_bus_type, NULL, NULL,
+ device_links_supplier_sync_state);
+ return 0;
+}
+late_initcall_sync(of_platform_sync_state_init);
#endif
int of_platform_device_destroy(struct device *dev, void *data)
diff --git a/include/linux/device.h b/include/linux/device.h
index 7f8ae7e5fc6b..4a0db34ae650 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -84,6 +84,13 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
* in the future.
* @probe: Called when a new device or driver add to this bus, and callback
* the specific driver's probe to initial the matched device.
+ * @sync_state: Called to sync device state to software state after all the
+ * state tracking consumers linked to this device (present at
+ * the time of late_initcall) have successfully bound to a
+ * driver. If the device has no consumers, this function will
+ * be called at late_initcall_sync level. If the device has
+ * consumers that are never bound to a driver, this function
+ * will never get called until they do.
* @remove: Called when a device removed from this bus.
* @shutdown: Called at shut-down time to quiesce the device.
*
@@ -128,6 +135,7 @@ struct bus_type {
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*add_links)(struct device *dev);
int (*probe)(struct device *dev);
+ void (*sync_state)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
@@ -257,6 +265,13 @@ enum probe_type {
* @probe: Called to query the existence of a specific device,
* whether this driver can work with it, and bind the driver
* to a specific device.
+ * @sync_state: Called to sync device state to software state after all the
+ * state tracking consumers linked to this device (present at
+ * the time of late_initcall) have successfully bound to a
+ * driver. If the device has no consumers, this function will
+ * be called at late_initcall_sync level. If the device has
+ * consumers that are never bound to a driver, this function
+ * will never get called until they do.
* @remove: Called when the device is removed from the system to
* unbind a device from this driver.
* @shutdown: Called at shut-down time to quiesce the device.
@@ -294,6 +309,7 @@ struct device_driver {
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
+ void (*sync_state)(struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
@@ -1065,6 +1081,7 @@ struct device {
bool offline_disabled:1;
bool offline:1;
bool of_node_reused:1;
+ bool state_synced:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
@@ -1404,6 +1421,8 @@ struct device_link *device_link_add(struct device *consumer,
struct device *supplier, u32 flags);
void device_link_del(struct device_link *link);
void device_link_remove(void *consumer, struct device *supplier);
+int device_links_supplier_sync_state(struct device *dev, void *data);
+void device_links_supplier_sync_state_enable(void);
#ifndef dev_fmt
#define dev_fmt(fmt) fmt
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 04/11] driver core: Add edit_links() callback for drivers
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
The driver core/bus adding dependencies by default makes sure that
suppliers don't sync the hardware state with software state before all the
consumers have their drivers loaded (if they are modules) and are probed.
However, when the bus incorrectly adds dependencies that it shouldn't have
added, the devices might never probe.
For example, if device-C is a consumer of device-S and they have phandles
to each other in DT, the following could happen:
1. Device-S get added first.
2. The bus add_links() callback will (incorrectly) try to link it as
a consumer of device-C.
3. Since device-C isn't present, device-S will be put in
"waiting-for-supplier" list.
4. Device-C gets added next.
5. All devices in "waiting-for-supplier" list are retried for linking.
6. Device-S gets linked as consumer to Device-C.
7. The bus add_links() callback will (correctly) try to link it as
a consumer of device-S.
8. This isn't allowed because it would create a cyclic device links.
So neither devices will get probed since the supplier is dependent on a
consumer that'll never probe (because it can't get resources from the
supplier).
Without this patch, things stay in this broken state. However, with this
patch, the execution will continue like this:
9. Device-C's driver is loaded.
10. Device-C's driver removes Device-S as a consumer of Device-C.
11. Device-C's driver adds Device-C as a consumer of Device-S.
12. Device-S probes.
13. Device-S sync_state() isn't called because Device-C hasn't probed yet.
14. Device-C probes.
15. Device-S's sync_state() callback is called.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/base/core.c | 24 ++++++++++++++++++++++--
drivers/base/dd.c | 29 +++++++++++++++++++++++++++++
include/linux/device.h | 18 ++++++++++++++++++
3 files changed, 69 insertions(+), 2 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 8b8b812d26f1..dce97b5f3536 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -423,6 +423,19 @@ static void device_link_wait_for_supplier(struct device *consumer)
mutex_unlock(&wfs_lock);
}
+/**
+ * device_link_remove_from_wfs - Unmark device as waiting for supplier
+ * @consumer: Consumer device
+ *
+ * Unmark the consumer device as waiting for suppliers to become available.
+ */
+void device_link_remove_from_wfs(struct device *consumer)
+{
+ mutex_lock(&wfs_lock);
+ list_del_init(&consumer->links.needs_suppliers);
+ mutex_unlock(&wfs_lock);
+}
+
/**
* device_link_check_waiting_consumers - Try to unmark waiting consumers
*
@@ -440,12 +453,19 @@ static void device_link_wait_for_supplier(struct device *consumer)
static void device_link_check_waiting_consumers(void)
{
struct device *dev, *tmp;
+ int ret;
mutex_lock(&wfs_lock);
list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
- links.needs_suppliers)
- if (!dev->bus->add_links(dev))
+ links.needs_suppliers) {
+ ret = 0;
+ if (dev->has_edit_links)
+ ret = driver_edit_links(dev);
+ else if (dev->bus->add_links)
+ ret = dev->bus->add_links(dev);
+ if (!ret)
list_del_init(&dev->links.needs_suppliers);
+ }
mutex_unlock(&wfs_lock);
}
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 0df9b4461766..842fc7b704f9 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -659,6 +659,12 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
+ if (drv->edit_links) {
+ if (drv->edit_links(dev))
+ dev->has_edit_links = true;
+ else
+ device_link_remove_from_wfs(dev);
+ }
pm_runtime_get_suppliers(dev);
if (dev->parent)
pm_runtime_get_sync(dev->parent);
@@ -747,6 +753,29 @@ struct device_attach_data {
bool have_async;
};
+static int __driver_edit_links(struct device_driver *drv, void *data)
+{
+ struct device *dev = data;
+
+ if (!drv->edit_links)
+ return 0;
+
+ if (driver_match_device(drv, dev) <= 0)
+ return 0;
+
+ return drv->edit_links(dev);
+}
+
+int driver_edit_links(struct device *dev)
+{
+ int ret;
+
+ device_lock(dev);
+ ret = bus_for_each_drv(dev->bus, NULL, dev, __driver_edit_links);
+ device_unlock(dev);
+ return ret;
+}
+
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
struct device_attach_data *data = _data;
diff --git a/include/linux/device.h b/include/linux/device.h
index 4a0db34ae650..d3c9e70052d8 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -262,6 +262,20 @@ enum probe_type {
* @probe_type: Type of the probe (synchronous or asynchronous) to use.
* @of_match_table: The open firmware table.
* @acpi_match_table: The ACPI match table.
+ * @edit_links: Called to allow a matched driver to edit the device links the
+ * bus might have added incorrectly. This will be useful to handle
+ * cases where the bus incorrectly adds functional dependencies
+ * that aren't true or tries to create cyclic dependencies. But
+ * doesn't correctly handle functional dependencies that are
+ * missed by the bus as the supplier's sync_state might get to
+ * execute before the driver for a missing consumer is loaded and
+ * gets to edit the device links for the consumer.
+ *
+ * This function might be called multiple times after a new device
+ * is added. The function is expected to create all the device
+ * links for the new device and return 0 if it was completed
+ * successfully or return an error if it needs to be reattempted
+ * in the future.
* @probe: Called to query the existence of a specific device,
* whether this driver can work with it, and bind the driver
* to a specific device.
@@ -308,6 +322,7 @@ struct device_driver {
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
+ int (*edit_links)(struct device *dev);
int (*probe) (struct device *dev);
void (*sync_state)(struct device *dev);
int (*remove) (struct device *dev);
@@ -1082,6 +1097,7 @@ struct device {
bool offline:1;
bool of_node_reused:1;
bool state_synced:1;
+ bool has_edit_links:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
@@ -1331,6 +1347,7 @@ extern int __must_check device_attach(struct device *dev);
extern int __must_check driver_attach(struct device_driver *drv);
extern void device_initial_probe(struct device *dev);
extern int __must_check device_reprobe(struct device *dev);
+extern int driver_edit_links(struct device *dev);
extern bool device_is_bound(struct device *dev);
@@ -1423,6 +1440,7 @@ void device_link_del(struct device_link *link);
void device_link_remove(void *consumer, struct device *supplier);
int device_links_supplier_sync_state(struct device *dev, void *data);
void device_links_supplier_sync_state_enable(void);
+void device_link_remove_from_wfs(struct device *consumer);
#ifndef dev_fmt
#define dev_fmt(fmt) fmt
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 05/11] driver core: Add APIs to pause/resume sync state callbacks
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
When multiple devices are added after kernel init, some suppliers could be
added before their consumer devices get added. In these instances, the
supplier devices could get their sync_state callback called right after
they probe because the consumers haven't had a chance to create device
links to the suppliers.
This change adds APIs to pause/resume sync state callbacks so that when
multiple devices are added, their sync_state callback evaluation can be
postponed to happen after all of them are added.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/base/core.c | 40 +++++++++++++++++++++++++++++++++-------
drivers/of/platform.c | 5 ++---
include/linux/device.h | 6 ++++--
3 files changed, 39 insertions(+), 12 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index dce97b5f3536..b03e679faea4 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -46,7 +46,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup);
/* Device links support. */
static LIST_HEAD(wait_for_suppliers);
static DEFINE_MUTEX(wfs_lock);
-static bool supplier_sync_state_enabled;
+static LIST_HEAD(deferred_sync);
+static unsigned int supplier_sync_state_disabled;
#ifdef CONFIG_SRCU
static DEFINE_MUTEX(device_links_lock);
@@ -657,17 +658,38 @@ static void __device_links_supplier_sync_state(struct device *dev)
dev->state_synced = true;
}
-int device_links_supplier_sync_state(struct device *dev, void *data)
+void device_links_supplier_sync_state_pause(void)
{
device_links_write_lock();
- __device_links_supplier_sync_state(dev);
+ supplier_sync_state_disabled++;
device_links_write_unlock();
- return 0;
}
-void device_links_supplier_sync_state_enable(void)
+void device_links_supplier_sync_state_resume(void)
{
- supplier_sync_state_enabled = true;
+ struct device *dev, *tmp;
+
+ device_links_write_lock();
+ if (!supplier_sync_state_disabled) {
+ WARN(true, "Unmatched sync_state pause/resume!");
+ goto out;
+ }
+ supplier_sync_state_disabled--;
+ if (supplier_sync_state_disabled)
+ goto out;
+
+ list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) {
+ __device_links_supplier_sync_state(dev);
+ list_del_init(&dev->links.defer_sync);
+ }
+out:
+ device_links_write_unlock();
+}
+
+static void __device_links_supplier_defer_sync(struct device *sup)
+{
+ if (list_empty(&sup->links.defer_sync))
+ list_add_tail(&sup->links.defer_sync, &deferred_sync);
}
/**
@@ -715,7 +737,9 @@ void device_links_driver_bound(struct device *dev)
WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);
WRITE_ONCE(link->status, DL_STATE_ACTIVE);
- if (supplier_sync_state_enabled)
+ if (supplier_sync_state_disabled)
+ __device_links_supplier_defer_sync(link->supplier);
+ else
__device_links_supplier_sync_state(link->supplier);
}
@@ -826,6 +850,7 @@ void device_links_driver_cleanup(struct device *dev)
WRITE_ONCE(link->status, DL_STATE_DORMANT);
}
+ list_del_init(&dev->links.defer_sync);
__device_links_no_driver(dev);
device_links_write_unlock();
@@ -1797,6 +1822,7 @@ void device_initialize(struct device *dev)
INIT_LIST_HEAD(&dev->links.consumers);
INIT_LIST_HEAD(&dev->links.suppliers);
INIT_LIST_HEAD(&dev->links.needs_suppliers);
+ INIT_LIST_HEAD(&dev->links.defer_sync);
dev->links.status = DL_DEV_NO_DRIVER;
}
EXPORT_SYMBOL_GPL(device_initialize);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 4d12d6658999..56b718f09929 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -581,6 +581,7 @@ static int __init of_platform_default_populate_init(void)
return -ENODEV;
platform_bus_type.add_links = of_link_to_suppliers;
+ device_links_supplier_sync_state_pause();
/*
* Handle certain compatibles explicitly, since we don't want to create
* platform_devices for every node in /reserved-memory with a
@@ -604,9 +605,7 @@ arch_initcall_sync(of_platform_default_populate_init);
static int __init of_platform_sync_state_init(void)
{
- device_links_supplier_sync_state_enable();
- bus_for_each_dev(&platform_bus_type, NULL, NULL,
- device_links_supplier_sync_state);
+ device_links_supplier_sync_state_resume();
return 0;
}
late_initcall_sync(of_platform_sync_state_init);
diff --git a/include/linux/device.h b/include/linux/device.h
index d3c9e70052d8..0ea28cb8c77e 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -926,12 +926,14 @@ enum dl_dev_state {
* @suppliers: List of links to supplier devices.
* @consumers: List of links to consumer devices.
* @needs_suppliers: Hook to global list of devices waiting for suppliers.
+ * @defer_sync: Hook to global list of devices that have deferred sync_state.
* @status: Driver status information.
*/
struct dev_links_info {
struct list_head suppliers;
struct list_head consumers;
struct list_head needs_suppliers;
+ struct list_head defer_sync;
enum dl_dev_state status;
};
@@ -1438,8 +1440,8 @@ struct device_link *device_link_add(struct device *consumer,
struct device *supplier, u32 flags);
void device_link_del(struct device_link *link);
void device_link_remove(void *consumer, struct device *supplier);
-int device_links_supplier_sync_state(struct device *dev, void *data);
-void device_links_supplier_sync_state_enable(void);
+void device_links_supplier_sync_state_pause(void);
+void device_links_supplier_sync_state_resume(void);
void device_link_remove_from_wfs(struct device *consumer);
#ifndef dev_fmt
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 06/11] of/platform: Pause/resume sync state in of_platform_populate()
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
When multiple child devices are populated using of_platform_populate()
after kernel init, there could be supplier-consumer dependencies between
the child devices.
Wait for all the devices to be added and linked before calling sync_state()
on all the suppliers.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/of/platform.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 56b718f09929..dba962a0ee50 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -486,6 +486,7 @@ int of_platform_populate(struct device_node *root,
pr_debug("%s()\n", __func__);
pr_debug(" starting at: %pOF\n", root);
+ device_links_supplier_sync_state_pause();
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
@@ -493,6 +494,8 @@ int of_platform_populate(struct device_node *root,
break;
}
}
+ device_links_supplier_sync_state_resume();
+
of_node_set_flag(root, OF_POPULATED_BUS);
of_node_put(root);
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 07/11] of/platform: Sanity check DT bindings before creating device links
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
If a common DT binding is pointing to a child DT node of a particular
parent DT node, don't add device links for such DT references. This is
because, by definition, a child node can't be a functional dependency for
the parent node.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/of/platform.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index dba962a0ee50..98414ba53b1f 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -512,6 +512,19 @@ int of_platform_default_populate(struct device_node *root,
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
+bool of_link_is_valid(struct device_node *con, struct device_node *sup)
+{
+ of_node_get(sup);
+ while (sup) {
+ if (sup == con) {
+ of_node_put(sup);
+ return false;
+ }
+ sup = of_get_next_parent(sup);
+ }
+ return true;
+}
+
static int of_link_binding(struct device *dev,
const char *binding, const char *cell)
{
@@ -522,6 +535,10 @@ static int of_link_binding(struct device *dev,
while (!of_parse_phandle_with_args(dev->of_node, binding, cell, i,
&sup_args)) {
+ if (!of_link_is_valid(dev->of_node, sup_args.np)) {
+ of_node_put(sup_args.np);
+ continue;
+ }
i++;
sup_dev = of_find_device_by_node(sup_args.np);
of_node_put(sup_args.np);
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 08/11] of/platform: Make sure supplier DT node is device when creating device links
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
While most phandle references in common bindings point to the supplier
device node, there are also common bindings where the phandle can
pointing to a child node of the supplier device node.
Therefore, when trying to find the supplier device that corresponds to a
supplier phandle, we need to make sure we are using the supplier's
device node. Otherwise, we'll never find the supplier device.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/of/platform.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 98414ba53b1f..cf8625abe30c 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -529,19 +529,33 @@ static int of_link_binding(struct device *dev,
const char *binding, const char *cell)
{
struct of_phandle_args sup_args;
+ struct device_node *sup_np;
struct platform_device *sup_dev;
unsigned int i = 0, links = 0;
u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER;
while (!of_parse_phandle_with_args(dev->of_node, binding, cell, i,
&sup_args)) {
- if (!of_link_is_valid(dev->of_node, sup_args.np)) {
- of_node_put(sup_args.np);
+ sup_np = sup_args.np;
+ /*
+ * Since we are trying to create device links, we need to find
+ * the actual device node that owns this supplier phandle.
+ * Often times it's the same node, but sometimes it can be one
+ * of the parents. So walk up the parent till you find a
+ * device.
+ */
+ while (sup_np && !of_find_property(sup_np, "compatible", NULL))
+ sup_np = of_get_next_parent(sup_np);
+ if (!sup_np)
+ continue;
+
+ if (!of_link_is_valid(dev->of_node, sup_np)) {
+ of_node_put(sup_np);
continue;
}
i++;
- sup_dev = of_find_device_by_node(sup_args.np);
- of_node_put(sup_args.np);
+ sup_dev = of_find_device_by_node(sup_np);
+ of_node_put(sup_np);
if (!sup_dev)
continue;
if (device_link_add(dev, &sup_dev->dev, dl_flags))
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 09/11] of/platform: Create device links for all child-supplier depencencies
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
A parent device can have child devices that it adds when it probes. But
this probing of the parent device can happen way after kernel init is done
-- for example, when the parent device's driver is loaded as a module.
In such cases, if the child devices depend on a supplier in the system, we
need to make sure the supplier gets the sync_state() callback only after
these child devices are added and probed.
To achieve this, when creating device links for a device by looking at its
DT node, don't just look at DT references at the top node level. Look at DT
references in all the descendant nodes too and create device links from the
ancestor device to all these supplier devices.
This way, when the parent device probes and adds child devices, the child
devices can then create their own device links to the suppliers and further
delay the supplier's sync_state() callback to after the child devices are
probed.
Example:
In this illustration, -> denotes DT references and indentation
represents child status.
Device node A
Device node B -> D
Device node C -> B, D
Device node D
Assume all these devices have their drivers loaded as modules.
Without this patch, this is the sequence of events:
1. D is added.
2. A is added.
3. Device D probes.
4. Device D gets its sync_state() callback.
5. Device B and C might malfunction because their resources got
altered/turned off before they can make active requests for them.
With this patch, this is the sequence of events:
1. D is added.
2. A is added and creates device links to D.
3. Device link from A to B is not added because A is a parent of B.
4. Device D probes.
5. Device D does not get it's sync_state() callback because consumer A
hasn't probed yet.
5. Device A probes.
5. a. Devices B and C are added.
5. b. Device links from B and C to D are added.
5. c. Device A's probe completes.
6. Device D does not get it's sync_state() callback because consumer A
has probed but consumers B and C haven't probed yet.
7. Device B and C probe.
8. Device D gets it's sync_state() callback because all its consumers
have probed.
9. None of the devices malfunction.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/of/platform.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index cf8625abe30c..6523d07ef2d7 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -525,7 +525,7 @@ bool of_link_is_valid(struct device_node *con, struct device_node *sup)
return true;
}
-static int of_link_binding(struct device *dev,
+static int of_link_binding(struct device *dev, struct device_node *con_np,
const char *binding, const char *cell)
{
struct of_phandle_args sup_args;
@@ -534,7 +534,7 @@ static int of_link_binding(struct device *dev,
unsigned int i = 0, links = 0;
u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER;
- while (!of_parse_phandle_with_args(dev->of_node, binding, cell, i,
+ while (!of_parse_phandle_with_args(con_np, binding, cell, i,
&sup_args)) {
sup_np = sup_args.np;
/*
@@ -579,26 +579,37 @@ static const char * const link_bindings[] = {
"interconnects", "#interconnect-cells",
};
-static int of_link_to_suppliers(struct device *dev)
+static int __of_link_to_suppliers(struct device *dev,
+ struct device_node *con_np)
{
unsigned int i = 0;
bool done = true;
-
- if (!of_devlink)
- return 0;
- if (unlikely(!dev->of_node))
- return 0;
+ struct device_node *child;
for (i = 0; i < ARRAY_SIZE(link_bindings) / 2; i++)
- if (of_link_binding(dev, link_bindings[i * 2],
+ if (of_link_binding(dev, con_np, link_bindings[i * 2],
link_bindings[i * 2 + 1]))
done = false;
+ for_each_child_of_node(con_np, child)
+ if (__of_link_to_suppliers(dev, child))
+ done = false;
+
if (!done)
return -ENODEV;
return 0;
}
+static int of_link_to_suppliers(struct device *dev)
+{
+ if (!of_devlink)
+ return 0;
+ if (unlikely(!dev->of_node))
+ return 0;
+
+ return __of_link_to_suppliers(dev, dev->of_node);
+}
+
#ifndef CONFIG_PPC
static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "qcom,rmtfs-mem" },
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 10/11] of/platform: Add functional dependency link from DT regulator bindings
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
Similar to creating functional dependency links from clock and
interconnect DT bindings, also create functional dependency links from
regulator DT bindings.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/of/platform.c | 83 ++++++++++++++++++++++++++-----------------
1 file changed, 51 insertions(+), 32 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 6523d07ef2d7..9f3b7e1801bc 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -525,46 +525,50 @@ bool of_link_is_valid(struct device_node *con, struct device_node *sup)
return true;
}
-static int of_link_binding(struct device *dev, struct device_node *con_np,
- const char *binding, const char *cell)
+static int of_link_to_phandle(struct device *dev, struct device_node *sup_np)
{
- struct of_phandle_args sup_args;
- struct device_node *sup_np;
struct platform_device *sup_dev;
- unsigned int i = 0, links = 0;
u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER;
+ int ret = 0;
- while (!of_parse_phandle_with_args(con_np, binding, cell, i,
- &sup_args)) {
- sup_np = sup_args.np;
- /*
- * Since we are trying to create device links, we need to find
- * the actual device node that owns this supplier phandle.
- * Often times it's the same node, but sometimes it can be one
- * of the parents. So walk up the parent till you find a
- * device.
- */
- while (sup_np && !of_find_property(sup_np, "compatible", NULL))
- sup_np = of_get_next_parent(sup_np);
- if (!sup_np)
- continue;
+ /*
+ * Since we are trying to create device links, we need to find
+ * the actual device node that owns this supplier phandle.
+ * Often times it's the same node, but sometimes it can be one
+ * of the parents. So walk up the parent till you find a
+ * device.
+ */
+ while (sup_np && !of_find_property(sup_np, "compatible", NULL))
+ sup_np = of_get_next_parent(sup_np);
+ if (!sup_np)
+ return 0;
- if (!of_link_is_valid(dev->of_node, sup_np)) {
- of_node_put(sup_np);
- continue;
- }
- i++;
- sup_dev = of_find_device_by_node(sup_np);
+ if (!of_link_is_valid(dev->of_node, sup_np)) {
of_node_put(sup_np);
- if (!sup_dev)
- continue;
- if (device_link_add(dev, &sup_dev->dev, dl_flags))
- links++;
- put_device(&sup_dev->dev);
+ return 0;
}
- if (links < i)
+ sup_dev = of_find_device_by_node(sup_np);
+ of_node_put(sup_np);
+ if (!sup_dev)
return -ENODEV;
- return 0;
+ if (!device_link_add(dev, &sup_dev->dev, dl_flags))
+ ret = -ENODEV;
+ put_device(&sup_dev->dev);
+ return ret;
+}
+
+static int of_link_binding(struct device *dev, struct device_node *con_np,
+ const char *binding, const char *cell)
+{
+ struct of_phandle_args sup_args;
+ unsigned int i = 0;
+ bool done = true;
+
+ while (!of_parse_phandle_with_args(con_np, binding, cell, i++,
+ &sup_args))
+ if (of_link_to_phandle(dev, sup_args.np))
+ done = false;
+ return done ? 0 : -ENODEV;
}
static bool of_devlink;
@@ -579,18 +583,33 @@ static const char * const link_bindings[] = {
"interconnects", "#interconnect-cells",
};
+#define REG_SUFFIX "-supply"
static int __of_link_to_suppliers(struct device *dev,
struct device_node *con_np)
{
unsigned int i = 0;
bool done = true;
struct device_node *child;
+ struct property *p;
+ unsigned int len, reg_len;
for (i = 0; i < ARRAY_SIZE(link_bindings) / 2; i++)
if (of_link_binding(dev, con_np, link_bindings[i * 2],
link_bindings[i * 2 + 1]))
done = false;
+ reg_len = strlen(REG_SUFFIX);
+ for_each_property_of_node(con_np, p) {
+ len = strlen(p->name);
+ if (len <= reg_len)
+ continue;
+ if (strcmp(p->name + len - reg_len, REG_SUFFIX))
+ continue;
+ if (of_link_to_phandle(dev,
+ of_parse_phandle(con_np, p->name, 0)))
+ done = false;
+ }
+
for_each_child_of_node(con_np, child)
if (__of_link_to_suppliers(dev, child))
done = false;
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v5 11/11] of/platform: Don't create device links default busses
From: Saravana Kannan @ 2019-07-12 23:52 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Greg Kroah-Hartman, Rafael J. Wysocki,
Frank Rowand
Cc: Saravana Kannan, devicetree, linux-kernel, David Collins,
kernel-team
In-Reply-To: <20190712235245.202558-1-saravanak@google.com>
Default busses also have devices created for them. But there's no point
in creating device links for them. It's especially wasteful as it'll
cause the traversal of the entire device tree and also spend a lot of
time checking and figuring out that creating those links isn't allowed.
So check for default busses and skip trying to create device links for
them.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/of/platform.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 9f3b7e1801bc..b02dbaa01bfe 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -593,6 +593,9 @@ static int __of_link_to_suppliers(struct device *dev,
struct property *p;
unsigned int len, reg_len;
+ if (of_match_node(of_default_bus_match_table, con_np))
+ return 0;
+
for (i = 0; i < ARRAY_SIZE(link_bindings) / 2; i++)
if (of_link_binding(dev, con_np, link_bindings[i * 2],
link_bindings[i * 2 + 1]))
--
2.22.0.510.g264f2c817a-goog
^ permalink raw reply related
* [PATCH v4 0/8] Support for Allwinner V3/S3L and Sochip S3
From: Icenowy Zheng @ 2019-07-13 3:46 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Linus Walleij
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
This patchset tries to add support for Allwinner V3/S3L and Sochip S3.
Allwinner V3/V3s/S3L and Sochip S3 share the same die, but with
different package. V3 is BGA w/o co-packaged DDR, V3s is QFP w/ DDR2,
S3L is BGA w/ DDR2 and S3 is BGA w/ DDR3. (S3 and S3L is compatible
for pinout, but because of different DDR, DDR voltage is different
between the two variants). Because of the pin count of V3s is
restricted due to the package, some pins are not bound on V3s, but
they're bound on V3/S3/S3L.
Currently the kernel is only prepared for the features available on V3s.
This patchset adds the features missing on V3s for using them on
V3/S3/S3L, and add bindings for V3/S3/S3L. It also adds a S3 SoM by
Sipeed, called Lichee Zero Plus.
Icenowy Zheng (8):
pinctrl: sunxi: v3s: introduce support for V3
clk: sunxi-ng: v3s: add the missing PLL_DDR1
dt-bindings: clk: sunxi-ccu: add compatible string for V3 CCU
clk: sunxi-ng: v3s: add missing clock slices for MMC2 module clocks
clk: sunxi-ng: v3s: add Allwinner V3 support
ARM: sunxi: dts: s3/s3l/v3: add DTSI files for S3/S3L/V3 SoCs
dt-bindings: arm: sunxi: add binding for Lichee Zero Plus core board
ARM: dts: sun8i: s3: add devicetree for Lichee zero plus w/ S3
.../devicetree/bindings/arm/sunxi.yaml | 5 +
.../clock/allwinner,sun4i-a10-ccu.yaml | 1 +
arch/arm/boot/dts/Makefile | 1 +
.../boot/dts/sun8i-s3-lichee-zero-plus.dts | 8 +
.../dts/sun8i-s3-s3l-lichee-zero-plus.dtsi | 46 +++
arch/arm/boot/dts/sun8i-s3.dtsi | 6 +
arch/arm/boot/dts/sun8i-s3l.dtsi | 6 +
arch/arm/boot/dts/sun8i-v3.dtsi | 14 +
drivers/clk/sunxi-ng/ccu-sun8i-v3s.c | 250 ++++++++++++++++-
drivers/clk/sunxi-ng/ccu-sun8i-v3s.h | 6 +-
drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c | 265 +++++++++++++++++-
drivers/pinctrl/sunxi/pinctrl-sunxi.h | 2 +
include/dt-bindings/clock/sun8i-v3s-ccu.h | 4 +
include/dt-bindings/reset/sun8i-v3s-ccu.h | 3 +
14 files changed, 604 insertions(+), 13 deletions(-)
create mode 100644 arch/arm/boot/dts/sun8i-s3-lichee-zero-plus.dts
create mode 100644 arch/arm/boot/dts/sun8i-s3-s3l-lichee-zero-plus.dtsi
create mode 100644 arch/arm/boot/dts/sun8i-s3.dtsi
create mode 100644 arch/arm/boot/dts/sun8i-s3l.dtsi
create mode 100644 arch/arm/boot/dts/sun8i-v3.dtsi
--
2.21.0
^ permalink raw reply
* [PATCH v4 1/8] pinctrl: sunxi: v3s: introduce support for V3
From: Icenowy Zheng @ 2019-07-13 3:46 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Linus Walleij
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20190713034634.44585-1-icenowy-h8G6r0blFSE@public.gmane.org>
Introduce the GPIO pins that is only available on V3 (not on V3s) to the
V3s pinctrl driver.
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
Changes in v4:
- Removed bogus alignment change.
Changes in v3:
- Fixed code alignment.
- Fixed LVDS function number.
Changes in v2:
- Dropped the driver rename patch and apply the changes directly on V3s
driver.
drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c | 265 +++++++++++++++++++++-
drivers/pinctrl/sunxi/pinctrl-sunxi.h | 2 +
2 files changed, 262 insertions(+), 5 deletions(-)
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c
index 6704ce8e5e3d..ca85438e379a 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c
@@ -1,5 +1,5 @@
/*
- * Allwinner V3s SoCs pinctrl driver.
+ * Allwinner V3/V3s SoCs pinctrl driver.
*
* Copyright (C) 2016 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
*
@@ -77,6 +77,30 @@ static const struct sunxi_desc_pin sun8i_v3s_pins[] = {
SUNXI_FUNCTION(0x2, "i2c1"), /* SCK */
SUNXI_FUNCTION(0x3, "uart0"), /* RX */
SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)), /* PB_EINT9 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(B, 10),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "jtag"), /* MS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 10)), /* PB_EINT10 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(B, 11),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "jtag"), /* CK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 11)), /* PB_EINT11 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(B, 12),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "jtag"), /* DO */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 12)), /* PB_EINT12 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(B, 13),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "jtag"), /* DI */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 13)), /* PB_EINT13 */
/* Hole */
SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
SUNXI_FUNCTION(0x0, "gpio_in"),
@@ -98,6 +122,180 @@ static const struct sunxi_desc_pin sun8i_v3s_pins[] = {
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "mmc2"), /* D0 */
SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(C, 4),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc2")), /* D1 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(C, 5),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc2")), /* D2 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(C, 6),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc2")), /* D3 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(C, 7),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc2")), /* D4 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(C, 8),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc2")), /* D5 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(C, 9),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc2")), /* D6 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(C, 10),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc2")), /* D7 */
+ /* Hole */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 0),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D2 */
+ SUNXI_FUNCTION(0x4, "emac")), /* RXD3 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 1),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D3 */
+ SUNXI_FUNCTION(0x4, "emac")), /* RXD2 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 2),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D4 */
+ SUNXI_FUNCTION(0x4, "emac")), /* RXD1 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 3),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D5 */
+ SUNXI_FUNCTION(0x4, "emac")), /* RXD0 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 4),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D6 */
+ SUNXI_FUNCTION(0x4, "emac")), /* RXCK */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 5),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D7 */
+ SUNXI_FUNCTION(0x4, "emac")), /* RXCTL/RXDV */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 6),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D10 */
+ SUNXI_FUNCTION(0x4, "emac")), /* RXERR */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 7),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D11 */
+ SUNXI_FUNCTION(0x4, "emac")), /* TXD3 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 8),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D12 */
+ SUNXI_FUNCTION(0x4, "emac")), /* TXD2 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 9),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D13 */
+ SUNXI_FUNCTION(0x4, "emac")), /* TXD1 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 10),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D14 */
+ SUNXI_FUNCTION(0x4, "emac")), /* TXD0 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 11),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D15 */
+ SUNXI_FUNCTION(0x4, "emac")), /* CRS */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 12),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D18 */
+ SUNXI_FUNCTION(0x3, "lvds"), /* VP0 */
+ SUNXI_FUNCTION(0x4, "emac")), /* TXCK */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 13),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D19 */
+ SUNXI_FUNCTION(0x3, "lvds"), /* VN0 */
+ SUNXI_FUNCTION(0x4, "emac")), /* TXCTL/TXEN */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 14),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D20 */
+ SUNXI_FUNCTION(0x3, "lvds"), /* VP1 */
+ SUNXI_FUNCTION(0x4, "emac")), /* TXERR */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 15),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D21 */
+ SUNXI_FUNCTION(0x3, "lvds"), /* VN1 */
+ SUNXI_FUNCTION(0x4, "emac")), /* CLKIN/COL */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 16),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D22 */
+ SUNXI_FUNCTION(0x3, "lvds"), /* VP2 */
+ SUNXI_FUNCTION(0x4, "emac")), /* MDC */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 17),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* D23 */
+ SUNXI_FUNCTION(0x3, "lvds"), /* VN2 */
+ SUNXI_FUNCTION(0x4, "emac")), /* MDIO */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 18),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* CLK */
+ SUNXI_FUNCTION(0x3, "lvds")), /* VPC */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 19),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* DE */
+ SUNXI_FUNCTION(0x3, "lvds")), /* VNC */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 20),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* HSYNC */
+ SUNXI_FUNCTION(0x3, "lvds")), /* VP3 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(D, 21),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd"), /* VSYNC */
+ SUNXI_FUNCTION(0x3, "lvds")), /* VN3 */
/* Hole */
SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
SUNXI_FUNCTION(0x0, "gpio_in"),
@@ -291,6 +489,54 @@ static const struct sunxi_desc_pin sun8i_v3s_pins[] = {
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */
SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)), /* PG_EINT5 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 6),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)), /* PG_EINT6 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 7),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* RX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)), /* PG_EINT7 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 8),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)), /* PG_EINT8 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 9),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)), /* PG_EINT9 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 10),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s"), /* SYNC */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)), /* PG_EINT10 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 11),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s"), /* BCLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)), /* PG_EINT11 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 12),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s"), /* DOUT */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)), /* PG_EINT12 */
+ SUNXI_PIN_VARIANT(SUNXI_PINCTRL_PIN(G, 13),
+ PINCTRL_SUN8I_V3,
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s"), /* DIN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)), /* PG_EINT13 */
};
static const unsigned int sun8i_v3s_pinctrl_irq_bank_map[] = { 1, 2 };
@@ -305,13 +551,22 @@ static const struct sunxi_pinctrl_desc sun8i_v3s_pinctrl_data = {
static int sun8i_v3s_pinctrl_probe(struct platform_device *pdev)
{
- return sunxi_pinctrl_init(pdev,
- &sun8i_v3s_pinctrl_data);
+ unsigned long variant = (unsigned long)of_device_get_match_data(&pdev->dev);
+
+ return sunxi_pinctrl_init_with_variant(pdev, &sun8i_v3s_pinctrl_data,
+ variant);
}
static const struct of_device_id sun8i_v3s_pinctrl_match[] = {
- { .compatible = "allwinner,sun8i-v3s-pinctrl", },
- {}
+ {
+ .compatible = "allwinner,sun8i-v3-pinctrl",
+ .data = (void *)PINCTRL_SUN8I_V3
+ },
+ {
+ .compatible = "allwinner,sun8i-v3s-pinctrl",
+ .data = (void *)PINCTRL_SUN8I_V3S
+ },
+ { },
};
static struct platform_driver sun8i_v3s_pinctrl_driver = {
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
index 44e30deeee38..a32bb5bcb754 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -94,6 +94,8 @@
#define PINCTRL_SUN4I_A10 BIT(6)
#define PINCTRL_SUN7I_A20 BIT(7)
#define PINCTRL_SUN8I_R40 BIT(8)
+#define PINCTRL_SUN8I_V3 BIT(9)
+#define PINCTRL_SUN8I_V3S BIT(10)
#define PIO_POW_MOD_SEL_REG 0x340
--
2.21.0
^ permalink raw reply related
* [PATCH v4 2/8] clk: sunxi-ng: v3s: add the missing PLL_DDR1
From: Icenowy Zheng @ 2019-07-13 3:46 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Linus Walleij
Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, linux-gpio,
linux-sunxi, Icenowy Zheng
In-Reply-To: <20190713034634.44585-1-icenowy@aosc.io>
The user manual of V3/V3s/S3 declares a PLL_DDR1, however it's forgot
when developing the V3s CCU driver.
Add back the missing PLL_DDR1.
Fixes: d0f11d14b0bc ("clk: sunxi-ng: add support for V3s CCU")
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
---
No changes since v1.
drivers/clk/sunxi-ng/ccu-sun8i-v3s.c | 19 +++++++++++++++----
drivers/clk/sunxi-ng/ccu-sun8i-v3s.h | 6 ++++--
2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
index 9b3939fc7faa..4eb68243e310 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
@@ -77,7 +77,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
BIT(28), /* lock */
0);
-static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr",
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0",
"osc24M", 0x020,
8, 5, /* N */
4, 2, /* K */
@@ -116,6 +116,14 @@ static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph1_clk, "pll-periph1",
2, /* post-div */
0);
+static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1",
+ "osc24M", 0x04c,
+ 8, 7, /* N */
+ 0, 2, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
static const char * const cpu_parents[] = { "osc32k", "osc24M",
"pll-cpu", "pll-cpu" };
static SUNXI_CCU_MUX(cpu_clk, "cpu", cpu_parents,
@@ -303,7 +311,8 @@ static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M",
0x0cc, BIT(16), 0);
-static const char * const dram_parents[] = { "pll-ddr", "pll-periph0-2x" };
+static const char * const dram_parents[] = { "pll-ddr0", "pll-ddr1",
+ "pll-periph0-2x" };
static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents,
0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL);
@@ -363,10 +372,11 @@ static struct ccu_common *sun8i_v3s_ccu_clks[] = {
&pll_audio_base_clk.common,
&pll_video_clk.common,
&pll_ve_clk.common,
- &pll_ddr_clk.common,
+ &pll_ddr0_clk.common,
&pll_periph0_clk.common,
&pll_isp_clk.common,
&pll_periph1_clk.common,
+ &pll_ddr1_clk.common,
&cpu_clk.common,
&axi_clk.common,
&ahb1_clk.common,
@@ -460,11 +470,12 @@ static struct clk_hw_onecell_data sun8i_v3s_hw_clks = {
[CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
[CLK_PLL_VIDEO] = &pll_video_clk.common.hw,
[CLK_PLL_VE] = &pll_ve_clk.common.hw,
- [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw,
[CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw,
[CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw,
[CLK_PLL_ISP] = &pll_isp_clk.common.hw,
[CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw,
+ [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw,
[CLK_CPU] = &cpu_clk.common.hw,
[CLK_AXI] = &axi_clk.common.hw,
[CLK_AHB1] = &ahb1_clk.common.hw,
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
index fbc1da8b4520..10af324bd6b1 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
@@ -20,7 +20,7 @@
#define CLK_PLL_AUDIO_8X 5
#define CLK_PLL_VIDEO 6
#define CLK_PLL_VE 7
-#define CLK_PLL_DDR 8
+#define CLK_PLL_DDR0 8
#define CLK_PLL_PERIPH0 9
#define CLK_PLL_PERIPH0_2X 10
#define CLK_PLL_ISP 11
@@ -49,6 +49,8 @@
/* And the GPU module clock is exported */
-#define CLK_NUMBER (CLK_MIPI_CSI + 1)
+#define CLK_PLL_DDR1 74
+
+#define CLK_NUMBER (CLK_PLL_DDR1 + 1)
#endif /* _CCU_SUN8I_H3_H_ */
--
2.21.0
^ permalink raw reply related
* [PATCH v4 3/8] dt-bindings: clk: sunxi-ccu: add compatible string for V3 CCU
From: Icenowy Zheng @ 2019-07-13 3:46 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Linus Walleij
Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, linux-gpio,
linux-sunxi, Icenowy Zheng, Rob Herring
In-Reply-To: <20190713034634.44585-1-icenowy@aosc.io>
Despite Allwinner V3 and V3s shares the same die, one peripheral (I2S)
is only available on V3, and thus the clocks is not declared for V3s
CCU.
Add a V3 CCU compatible string to the binding to prepare for a CCU
driver that provide I2S clock on V3, but not on V3s.
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
Reviewed-by: Rob Herring <robh@kernel.org>
---
No changes since v3.
Changes in v2:
- Adapt to YAML binding document in linux-next.
.../devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml b/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml
index c935405458fe..1bde87fc94c5 100644
--- a/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml
+++ b/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml
@@ -31,6 +31,7 @@ properties:
- allwinner,sun8i-h3-ccu
- allwinner,sun8i-h3-r-ccu
- allwinner,sun8i-r40-ccu
+ - allwinner,sun8i-v3-ccu
- allwinner,sun8i-v3s-ccu
- allwinner,sun9i-a80-ccu
- allwinner,sun50i-a64-ccu
--
2.21.0
^ permalink raw reply related
* [PATCH v4 4/8] clk: sunxi-ng: v3s: add missing clock slices for MMC2 module clocks
From: Icenowy Zheng @ 2019-07-13 3:46 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Linus Walleij
Cc: devicetree, linux-arm-kernel, linux-kernel, linux-clk, linux-gpio,
linux-sunxi, Icenowy Zheng
In-Reply-To: <20190713034634.44585-1-icenowy@aosc.io>
The MMC2 clock slices are currently not defined in V3s CCU driver, which
makes MMC2 not working.
Fix this issue.
Fixes: d0f11d14b0bc ("clk: sunxi-ng: add support for V3s CCU")
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
---
New patch in v4.
drivers/clk/sunxi-ng/ccu-sun8i-v3s.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
index 4eb68243e310..9c88015d4419 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
@@ -513,6 +513,9 @@ static struct clk_hw_onecell_data sun8i_v3s_hw_clks = {
[CLK_MMC1] = &mmc1_clk.common.hw,
[CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw,
[CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_MMC2] = &mmc1_clk.common.hw,
+ [CLK_MMC2_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC2_OUTPUT] = &mmc1_output_clk.common.hw,
[CLK_CE] = &ce_clk.common.hw,
[CLK_SPI0] = &spi0_clk.common.hw,
[CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
--
2.21.0
^ permalink raw reply related
* [PATCH v4 5/8] clk: sunxi-ng: v3s: add Allwinner V3 support
From: Icenowy Zheng @ 2019-07-13 3:46 UTC (permalink / raw)
To: Rob Herring, Maxime Ripard, Chen-Yu Tsai, Linus Walleij
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Icenowy Zheng
In-Reply-To: <20190713034634.44585-1-icenowy-h8G6r0blFSE@public.gmane.org>
Allwinner V3 has the same main die with V3s, but with more pins wired.
There's a I2S bus on V3 that is not available on V3s.
Add the V3-only peripheral's clocks and reset to the V3s CCU driver,
bound to a new V3 compatible string. The driver name is not changed
because it's part of the device tree binding (the header file name).
Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
---
Changes in v4:
- Add the missing MMC2 clock slices.
No changes in v3/v2.
drivers/clk/sunxi-ng/ccu-sun8i-v3s.c | 228 +++++++++++++++++++++-
drivers/clk/sunxi-ng/ccu-sun8i-v3s.h | 2 +-
include/dt-bindings/clock/sun8i-v3s-ccu.h | 4 +
include/dt-bindings/reset/sun8i-v3s-ccu.h | 3 +
4 files changed, 234 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
index 9c88015d4419..32854dec3753 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
@@ -235,6 +235,8 @@ static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1",
0x068, BIT(0), 0);
static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1",
0x068, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1",
+ 0x068, BIT(12), 0);
static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2",
0x06c, BIT(0), 0);
@@ -306,6 +308,11 @@ static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
BIT(31), /* gate */
0);
+static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x",
+ "pll-audio-2x", "pll-audio" };
+static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents,
+ 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
+
static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
0x0cc, BIT(8), 0);
static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M",
@@ -443,6 +450,80 @@ static const struct clk_hw *clk_parent_pll_audio[] = {
&pll_audio_base_clk.common.hw
};
+static struct ccu_common *sun8i_v3_ccu_clks[] = {
+ &pll_cpu_clk.common,
+ &pll_audio_base_clk.common,
+ &pll_video_clk.common,
+ &pll_ve_clk.common,
+ &pll_ddr0_clk.common,
+ &pll_periph0_clk.common,
+ &pll_isp_clk.common,
+ &pll_periph1_clk.common,
+ &pll_ddr1_clk.common,
+ &cpu_clk.common,
+ &axi_clk.common,
+ &ahb1_clk.common,
+ &apb1_clk.common,
+ &apb2_clk.common,
+ &ahb2_clk.common,
+ &bus_ce_clk.common,
+ &bus_dma_clk.common,
+ &bus_mmc0_clk.common,
+ &bus_mmc1_clk.common,
+ &bus_mmc2_clk.common,
+ &bus_dram_clk.common,
+ &bus_emac_clk.common,
+ &bus_hstimer_clk.common,
+ &bus_spi0_clk.common,
+ &bus_otg_clk.common,
+ &bus_ehci0_clk.common,
+ &bus_ohci0_clk.common,
+ &bus_ve_clk.common,
+ &bus_tcon0_clk.common,
+ &bus_csi_clk.common,
+ &bus_de_clk.common,
+ &bus_codec_clk.common,
+ &bus_pio_clk.common,
+ &bus_i2s0_clk.common,
+ &bus_i2c0_clk.common,
+ &bus_i2c1_clk.common,
+ &bus_uart0_clk.common,
+ &bus_uart1_clk.common,
+ &bus_uart2_clk.common,
+ &bus_ephy_clk.common,
+ &bus_dbg_clk.common,
+ &mmc0_clk.common,
+ &mmc0_sample_clk.common,
+ &mmc0_output_clk.common,
+ &mmc1_clk.common,
+ &mmc1_sample_clk.common,
+ &mmc1_output_clk.common,
+ &mmc2_clk.common,
+ &mmc2_sample_clk.common,
+ &mmc2_output_clk.common,
+ &ce_clk.common,
+ &spi0_clk.common,
+ &i2s0_clk.common,
+ &usb_phy0_clk.common,
+ &usb_ohci0_clk.common,
+ &dram_clk.common,
+ &dram_ve_clk.common,
+ &dram_csi_clk.common,
+ &dram_ohci_clk.common,
+ &dram_ehci_clk.common,
+ &de_clk.common,
+ &tcon_clk.common,
+ &csi_misc_clk.common,
+ &csi0_mclk_clk.common,
+ &csi1_sclk_clk.common,
+ &csi1_mclk_clk.common,
+ &ve_clk.common,
+ &ac_dig_clk.common,
+ &avs_clk.common,
+ &mbus_clk.common,
+ &mipi_csi_clk.common,
+};
+
/* We hardcode the divider to 4 for now */
static CLK_FIXED_FACTOR_HWS(pll_audio_clk, "pll-audio",
clk_parent_pll_audio,
@@ -540,6 +621,88 @@ static struct clk_hw_onecell_data sun8i_v3s_hw_clks = {
.num = CLK_NUMBER,
};
+static struct clk_hw_onecell_data sun8i_v3_hw_clks = {
+ .hws = {
+ [CLK_PLL_CPU] = &pll_cpu_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO] = &pll_video_clk.common.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw,
+ [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw,
+ [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw,
+ [CLK_PLL_ISP] = &pll_isp_clk.common.hw,
+ [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw,
+ [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw,
+ [CLK_CPU] = &cpu_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB1] = &ahb1_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_APB2] = &apb2_clk.common.hw,
+ [CLK_AHB2] = &ahb2_clk.common.hw,
+ [CLK_BUS_CE] = &bus_ce_clk.common.hw,
+ [CLK_BUS_DMA] = &bus_dma_clk.common.hw,
+ [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw,
+ [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw,
+ [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw,
+ [CLK_BUS_DRAM] = &bus_dram_clk.common.hw,
+ [CLK_BUS_EMAC] = &bus_emac_clk.common.hw,
+ [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw,
+ [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw,
+ [CLK_BUS_OTG] = &bus_otg_clk.common.hw,
+ [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw,
+ [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw,
+ [CLK_BUS_VE] = &bus_ve_clk.common.hw,
+ [CLK_BUS_TCON0] = &bus_tcon0_clk.common.hw,
+ [CLK_BUS_CSI] = &bus_csi_clk.common.hw,
+ [CLK_BUS_DE] = &bus_de_clk.common.hw,
+ [CLK_BUS_CODEC] = &bus_codec_clk.common.hw,
+ [CLK_BUS_PIO] = &bus_pio_clk.common.hw,
+ [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw,
+ [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw,
+ [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw,
+ [CLK_BUS_UART0] = &bus_uart0_clk.common.hw,
+ [CLK_BUS_UART1] = &bus_uart1_clk.common.hw,
+ [CLK_BUS_UART2] = &bus_uart2_clk.common.hw,
+ [CLK_BUS_EPHY] = &bus_ephy_clk.common.hw,
+ [CLK_BUS_DBG] = &bus_dbg_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw,
+ [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_MMC2] = &mmc1_clk.common.hw,
+ [CLK_MMC2_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC2_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_CE] = &ce_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_I2S0] = &i2s0_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw,
+ [CLK_DRAM] = &dram_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI] = &dram_csi_clk.common.hw,
+ [CLK_DRAM_EHCI] = &dram_ehci_clk.common.hw,
+ [CLK_DRAM_OHCI] = &dram_ohci_clk.common.hw,
+ [CLK_DE] = &de_clk.common.hw,
+ [CLK_TCON0] = &tcon_clk.common.hw,
+ [CLK_CSI_MISC] = &csi_misc_clk.common.hw,
+ [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw,
+ [CLK_CSI1_SCLK] = &csi1_sclk_clk.common.hw,
+ [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_AC_DIG] = &ac_dig_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_MIPI_CSI] = &mipi_csi_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
static struct ccu_reset_map sun8i_v3s_ccu_resets[] = {
[RST_USB_PHY0] = { 0x0cc, BIT(0) },
@@ -575,6 +738,42 @@ static struct ccu_reset_map sun8i_v3s_ccu_resets[] = {
[RST_BUS_UART2] = { 0x2d8, BIT(18) },
};
+static struct ccu_reset_map sun8i_v3_ccu_resets[] = {
+ [RST_USB_PHY0] = { 0x0cc, BIT(0) },
+
+ [RST_MBUS] = { 0x0fc, BIT(31) },
+
+ [RST_BUS_CE] = { 0x2c0, BIT(5) },
+ [RST_BUS_DMA] = { 0x2c0, BIT(6) },
+ [RST_BUS_MMC0] = { 0x2c0, BIT(8) },
+ [RST_BUS_MMC1] = { 0x2c0, BIT(9) },
+ [RST_BUS_MMC2] = { 0x2c0, BIT(10) },
+ [RST_BUS_DRAM] = { 0x2c0, BIT(14) },
+ [RST_BUS_EMAC] = { 0x2c0, BIT(17) },
+ [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) },
+ [RST_BUS_SPI0] = { 0x2c0, BIT(20) },
+ [RST_BUS_OTG] = { 0x2c0, BIT(24) },
+ [RST_BUS_EHCI0] = { 0x2c0, BIT(26) },
+ [RST_BUS_OHCI0] = { 0x2c0, BIT(29) },
+
+ [RST_BUS_VE] = { 0x2c4, BIT(0) },
+ [RST_BUS_TCON0] = { 0x2c4, BIT(4) },
+ [RST_BUS_CSI] = { 0x2c4, BIT(8) },
+ [RST_BUS_DE] = { 0x2c4, BIT(12) },
+ [RST_BUS_DBG] = { 0x2c4, BIT(31) },
+
+ [RST_BUS_EPHY] = { 0x2c8, BIT(2) },
+
+ [RST_BUS_CODEC] = { 0x2d0, BIT(0) },
+ [RST_BUS_I2S0] = { 0x2d0, BIT(12) },
+
+ [RST_BUS_I2C0] = { 0x2d8, BIT(0) },
+ [RST_BUS_I2C1] = { 0x2d8, BIT(1) },
+ [RST_BUS_UART0] = { 0x2d8, BIT(16) },
+ [RST_BUS_UART1] = { 0x2d8, BIT(17) },
+ [RST_BUS_UART2] = { 0x2d8, BIT(18) },
+};
+
static const struct sunxi_ccu_desc sun8i_v3s_ccu_desc = {
.ccu_clks = sun8i_v3s_ccu_clks,
.num_ccu_clks = ARRAY_SIZE(sun8i_v3s_ccu_clks),
@@ -585,7 +784,18 @@ static const struct sunxi_ccu_desc sun8i_v3s_ccu_desc = {
.num_resets = ARRAY_SIZE(sun8i_v3s_ccu_resets),
};
-static void __init sun8i_v3s_ccu_setup(struct device_node *node)
+static const struct sunxi_ccu_desc sun8i_v3_ccu_desc = {
+ .ccu_clks = sun8i_v3_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun8i_v3_ccu_clks),
+
+ .hw_clks = &sun8i_v3_hw_clks,
+
+ .resets = sun8i_v3_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun8i_v3_ccu_resets),
+};
+
+static void __init sun8i_v3_v3s_ccu_init(struct device_node *node,
+ const struct sunxi_ccu_desc *ccu_desc)
{
void __iomem *reg;
u32 val;
@@ -601,7 +811,21 @@ static void __init sun8i_v3s_ccu_setup(struct device_node *node)
val &= ~GENMASK(19, 16);
writel(val | (3 << 16), reg + SUN8I_V3S_PLL_AUDIO_REG);
- sunxi_ccu_probe(node, reg, &sun8i_v3s_ccu_desc);
+ sunxi_ccu_probe(node, reg, ccu_desc);
+}
+
+static void __init sun8i_v3s_ccu_setup(struct device_node *node)
+{
+ sun8i_v3_v3s_ccu_init(node, &sun8i_v3s_ccu_desc);
+}
+
+static void __init sun8i_v3_ccu_setup(struct device_node *node)
+{
+ sun8i_v3_v3s_ccu_init(node, &sun8i_v3_ccu_desc);
}
+
CLK_OF_DECLARE(sun8i_v3s_ccu, "allwinner,sun8i-v3s-ccu",
sun8i_v3s_ccu_setup);
+
+CLK_OF_DECLARE(sun8i_v3_ccu, "allwinner,sun8i-v3-ccu",
+ sun8i_v3_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
index 10af324bd6b1..b0160d305a67 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h
@@ -51,6 +51,6 @@
#define CLK_PLL_DDR1 74
-#define CLK_NUMBER (CLK_PLL_DDR1 + 1)
+#define CLK_NUMBER (CLK_I2S0 + 1)
#endif /* _CCU_SUN8I_H3_H_ */
diff --git a/include/dt-bindings/clock/sun8i-v3s-ccu.h b/include/dt-bindings/clock/sun8i-v3s-ccu.h
index c0d5d5599c87..014ac6123d17 100644
--- a/include/dt-bindings/clock/sun8i-v3s-ccu.h
+++ b/include/dt-bindings/clock/sun8i-v3s-ccu.h
@@ -104,4 +104,8 @@
#define CLK_MIPI_CSI 73
+/* Clocks not available on V3s */
+#define CLK_BUS_I2S0 75
+#define CLK_I2S0 76
+
#endif /* _DT_BINDINGS_CLK_SUN8I_V3S_H_ */
diff --git a/include/dt-bindings/reset/sun8i-v3s-ccu.h b/include/dt-bindings/reset/sun8i-v3s-ccu.h
index b58ef21a2e18..b6790173afd6 100644
--- a/include/dt-bindings/reset/sun8i-v3s-ccu.h
+++ b/include/dt-bindings/reset/sun8i-v3s-ccu.h
@@ -75,4 +75,7 @@
#define RST_BUS_UART1 50
#define RST_BUS_UART2 51
+/* Reset lines not available on V3s */
+#define RST_BUS_I2S0 52
+
#endif /* _DT_BINDINGS_RST_SUN8I_H3_H_ */
--
2.21.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox