Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] arm64: mmu: set the contiguous for kernel mappings when appropriate
From: Mark Rutland @ 2016-10-11 15:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAKv+Gu_y126PuYhmTq81Psjs6Ss_g2zTYm9asqPDBpc5Uqvh0Q@mail.gmail.com>

On Tue, Oct 11, 2016 at 09:21:14AM +0100, Ard Biesheuvel wrote:
> On 11 October 2016 at 08:44, Mark Rutland <mark.rutland@arm.com> wrote:
> > On Mon, Oct 10, 2016 at 07:12:44PM +0100, Ard Biesheuvel wrote:
> > Not a big deal, but the 'block' part here and elsewhere is now arguably
> > misleading (given 'block' is an architectural term).
> >
> > I haven't come up with a better term, so again, not a big deal. ;)
> 
> Indeed. I could simply call it 'allow_cont_mappings' in the context of
> this function, and keep the call below as is.

I'd prefer that the naming is consistent across functions, even if it's left
as-is (and arguably cont makes it sounds like it doesn't cover block mappings).
So unless we find a suitably-equipped thesaurus, as-is is probably fine.

Thanks,
Mark.

^ permalink raw reply

* [PATCH v3] mmc: sunxi: Handle the 'New Timing' mode
From: Maxime Ripard @ 2016-10-11 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160830193202.4baaefd508a5d20d48b2cbed@free.fr>

On Tue, Aug 30, 2016 at 07:32:02PM +0200, Jean-Francois Moine wrote:
> On Tue, 30 Aug 2016 18:26:13 +0200
> Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> 
> > > There are 2 flags saying that the new timing is used:
> > > - the bit 'mode select' in the clock register, and
> > > - the bit 'new timing' in the MMC register.
> > > Both bits must be set/reset at the same time, otherwise the device
> > > does not work (tested with wifi and eMMC in H3 and A83T boards).
> > > So, some synchronization must exist.
> > > 
> > > The previous versions was using a DT property for the MMC and a flag
> > > in the clock driver. This did work with a correct configuration
> > > on both sides, but experiment showed that it was easy to do an error.
> > 
> > I still believe that we will need a property, at least to identify on
> > which we can try the new mode, and on which clocks it's irrelevant (at
> > least for the A33 and A83T).
> 
> As told above, setting the new mode on side (clock or MMC) and not on
> the other one prevents the devices to work. Then, it is safer to have
> the new mode capability flag only once for both sides.
> 
> Now, as the clocks are defined by memory tables and not by the DT, it
> seems natural to have the flag on the clock side.

You can look at it from two sides.

Either you try to switch to the new mode all the time, or you try to
do it only for MMC controllers (and their associated clocks) that
support it.

In the former case, you'll need to ignore a failure in the switch
(mostly if the clock doesn't support it) only for the MMC controllers
that do not have that mode. For the controllers that support it, you
cannot ignore that error anymore. So you have to have some way to
identify in which case you are.

And you need to have that in the former case too, so there's really no
way you can go without such a flag.

> > However, I also believe we should make that mode switching explicit
> > through a function call, instead of relying on some side effect (of
> > some non-upstream code).
> 
> Do you mean a direct call from the MMC driver to the clock driver?

Yes.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161011/69218957/attachment.sig>

^ permalink raw reply

* latest version of bluetooth for n950?
From: Sebastian Reichel @ 2016-10-11 14:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161011074704.GA21003@amd>

Hi,

On Tue, Oct 11, 2016 at 09:47:04AM +0200, Pavel Machek wrote:
> Hi, Sebastian!
> 
> I got some free cycles to play with n900 and bluetooth. There's still
> some unrelated config option that breaks even the old vesion of
> patches, but I'm ready for more debugging now.
> 
> Could I have the latest version of the (clean) bluetooth patch? I have
> feeling it might work with the right config option, and would like to
> try.
> 
> For the record, here's working .config and the tricky tricky oneliner
> that took me week to figure out.

https://git.kernel.org/cgit/linux/kernel/git/sre/linux-n900.git/log/?h=n950-bluetooth

My local branch is based on 4.8 and fixes a few of Marcel's
comments. I'm currently at ELCE, but I will push my local
stuff later in the hotel after verifying, that it works as
expected (luckily I brought N950 with me :)).

The work I push will not yet be using Rob's (Herring) work
based on serio (I'm working on that). I think his patches
should go in first, so that we avoid introducing userspace
API, that we do not really want (hciattach).

-- Sebastian
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161011/704b5d0b/attachment.sig>

^ permalink raw reply

* [PATCH v4 9/9] arm64: dts: add Pine64 support
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

From: Andre Przywara <andre.przywara@arm.com>

The Pine64 is a cost-efficient development board based on the
Allwinner A64 SoC.
There are three models: the basic version with Fast Ethernet and
512 MB of DRAM (Pine64) and two Pine64+ versions, which both
feature Gigabit Ethernet and additional connectors for touchscreens
and a camera. Or as my son put it: "Those are smaller and these are
missing." ;-)
The two Pine64+ models just differ in the amount of DRAM
(1GB vs. 2GB). Since U-Boot will figure out the right size for us and
patches the DT accordingly we just need to provide one DT for the
Pine64+.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
[Maxime: Removed the common DTSI and include directly the pine64 DTS]
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm64/boot/dts/Makefile                             |  1 +-
 arch/arm64/boot/dts/allwinner/Makefile                   |  5 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts | 50 ++++++-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts      | 74 +++++++++-
 4 files changed, 130 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm64/boot/dts/allwinner/Makefile
 create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
 create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts

diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile
index 6e199c903676..ddcbf5a2c17e 100644
--- a/arch/arm64/boot/dts/Makefile
+++ b/arch/arm64/boot/dts/Makefile
@@ -1,4 +1,5 @@
 dts-dirs += al
+dts-dirs += allwinner
 dts-dirs += altera
 dts-dirs += amd
 dts-dirs += amlogic
diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile
new file mode 100644
index 000000000000..1e29a5ae8282
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/Makefile
@@ -0,0 +1,5 @@
+dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pine64-plus.dtb sun50i-a64-pine64.dtb
+
+always		:= $(dtb-y)
+subdir-y	:= $(dts-dirs)
+clean-files	:= *.dtb
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
new file mode 100644
index 000000000000..790d14daaa6a
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 ARM Ltd.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This library is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "sun50i-a64-pine64.dts"
+
+/ {
+	model = "Pine64+";
+	compatible = "pine64,pine64-plus", "allwinner,sun50i-a64";
+
+	/* TODO: Camera, Ethernet PHY, touchscreen, etc. */
+};
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
new file mode 100644
index 000000000000..9f127b3d0e33
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 ARM Ltd.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This library is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "sun50i-a64.dtsi"
+
+/ {
+	model = "Pine64";
+	compatible = "pine64,pine64", "allwinner,sun50i-a64";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_pins_a>;
+	status = "okay";
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	status = "okay";
+};
+
+&i2c1_pins {
+	allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
+};
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 8/9] Documentation: devicetree: add vendor prefix for Pine64
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

From: Andre Przywara <andre.przywara@arm.com>

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Chen-Yu Tsai <wens@csie.org>
[Maxime: Change title prefix to match the usual style]
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 7b6bda3b4189..3dd1d77c1c30 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -200,6 +200,7 @@ parade	Parade Technologies Inc.
 pericom	Pericom Technology Inc.
 phytec	PHYTEC Messtechnik GmbH
 picochip	Picochip Ltd
+pine64	Pine64
 plathome	Plat'Home Co., Ltd.
 plda	PLDA
 pixcir  PIXCIR MICROELECTRONICS Co., Ltd
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 7/9] arm64: dts: add Allwinner A64 SoC .dtsi
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

From: Andre Przywara <andre.przywara@arm.com>

The Allwinner A64 SoC is a low-cost chip with 4 ARM Cortex-A53 cores
and the typical tablet / TV box peripherals.
The SoC is based on the (32-bit) Allwinner H3 chip, sharing most of
the peripherals and the memory map.
Although the cores are proper 64-bit ones, the whole SoC is actually
limited to 4GB (including all the supported DRAM), so we use 32-bit
address and size cells. This has the nice feature of us being able to
reuse the DT for 32-bit kernels as well.
This .dtsi lists the hardware that we support so far.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Chen-Yu Tsai <wens@csie.org>
[Maxime: Convert to CCU binding, drop the MMC support for now]
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 Documentation/devicetree/bindings/arm/sunxi.txt |   1 +-
 MAINTAINERS                                     |   1 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi   | 263 +++++++++++++++++-
 3 files changed, 265 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi

diff --git a/Documentation/devicetree/bindings/arm/sunxi.txt b/Documentation/devicetree/bindings/arm/sunxi.txt
index 3975d0a0e4c2..4d6467cc2aa2 100644
--- a/Documentation/devicetree/bindings/arm/sunxi.txt
+++ b/Documentation/devicetree/bindings/arm/sunxi.txt
@@ -14,4 +14,5 @@ using one of the following compatible strings:
   allwinner,sun8i-a83t
   allwinner,sun8i-h3
   allwinner,sun9i-a80
+  allwinner,sun50i-a64
   nextthing,gr8
diff --git a/MAINTAINERS b/MAINTAINERS
index 7be47efb2159..926879c05dc6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -983,6 +983,7 @@ L:	linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 N:	sun[x456789]i
 F:	arch/arm/boot/dts/ntc-gr8*
+F:	arch/arm64/boot/dts/allwinner/
 
 ARM/Allwinner SoC Clock Support
 M:	Emilio L?pez <emilio@elopez.com.ar>
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
new file mode 100644
index 000000000000..0f75fec23dc9
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2016 ARM Ltd.
+ * based on the Allwinner H3 dtsi:
+ *    Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/pinctrl/sun4i-a10.h>
+#include <dt-bindings/reset/sun50i-a64-ccu.h>
+
+/ {
+	interrupt-parent = <&gic>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu at 0 {
+			compatible = "arm,cortex-a53", "arm,armv8";
+			device_type = "cpu";
+			reg = <0>;
+			enable-method = "psci";
+		};
+
+		cpu1: cpu at 1 {
+			compatible = "arm,cortex-a53", "arm,armv8";
+			device_type = "cpu";
+			reg = <1>;
+			enable-method = "psci";
+		};
+
+		cpu2: cpu at 2 {
+			compatible = "arm,cortex-a53", "arm,armv8";
+			device_type = "cpu";
+			reg = <2>;
+			enable-method = "psci";
+		};
+
+		cpu3: cpu at 3 {
+			compatible = "arm,cortex-a53", "arm,armv8";
+			device_type = "cpu";
+			reg = <3>;
+			enable-method = "psci";
+		};
+	};
+
+	osc24M: osc24M_clk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <24000000>;
+		clock-output-names = "osc24M";
+	};
+
+	osc32k: osc32k_clk {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <32768>;
+		clock-output-names = "osc32k";
+	};
+
+	psci {
+		compatible = "arm,psci-0.2";
+		method = "smc";
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupts = <GIC_PPI 13
+			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+			     <GIC_PPI 14
+			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+			     <GIC_PPI 11
+			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+			     <GIC_PPI 10
+			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
+	};
+
+	soc {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		ccu: clock at 01c20000 {
+			compatible = "allwinner,sun50i-a64-ccu";
+			reg = <0x01c20000 0x400>;
+			clocks = <&osc24M>, <&osc32k>;
+			clock-names = "hosc", "losc";
+			#clock-cells = <1>;
+			#reset-cells = <1>;
+		};
+
+		pio: pinctrl at 1c20800 {
+			compatible = "allwinner,sun50i-a64-pinctrl";
+			reg = <0x01c20800 0x400>;
+			interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_PIO>;
+			gpio-controller;
+			#gpio-cells = <3>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			i2c1_pins: i2c1_pins {
+				allwinner,pins = "PH2", "PH3";
+				allwinner,function = "i2c1";
+			};
+
+			uart0_pins_a: uart0 at 0 {
+				allwinner,pins = "PB8", "PB9";
+				allwinner,function = "uart0";
+			};
+		};
+
+		uart0: serial at 1c28000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0x01c28000 0x400>;
+			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&ccu CLK_BUS_UART0>;
+			resets = <&ccu RST_BUS_UART0>;
+			status = "disabled";
+		};
+
+		uart1: serial at 1c28400 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0x01c28400 0x400>;
+			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&ccu CLK_BUS_UART1>;
+			resets = <&ccu RST_BUS_UART1>;
+			status = "disabled";
+		};
+
+		uart2: serial at 1c28800 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0x01c28800 0x400>;
+			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&ccu CLK_BUS_UART2>;
+			resets = <&ccu RST_BUS_UART2>;
+			status = "disabled";
+		};
+
+		uart3: serial at 1c28c00 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0x01c28c00 0x400>;
+			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&ccu CLK_BUS_UART3>;
+			resets = <&ccu RST_BUS_UART3>;
+			status = "disabled";
+		};
+
+		uart4: serial at 1c29000 {
+			compatible = "snps,dw-apb-uart";
+			reg = <0x01c29000 0x400>;
+			interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+			reg-shift = <2>;
+			reg-io-width = <4>;
+			clocks = <&ccu CLK_BUS_UART4>;
+			resets = <&ccu RST_BUS_UART4>;
+			status = "disabled";
+		};
+
+		rtc: rtc at 1f00000 {
+			compatible = "allwinner,sun6i-a31-rtc";
+			reg = <0x01f00000 0x54>;
+			interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		i2c0: i2c at 1c2ac00 {
+			compatible = "allwinner,sun6i-a31-i2c";
+			reg = <0x01c2ac00 0x400>;
+			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_I2C0>;
+			resets = <&ccu RST_BUS_I2C0>;
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c1: i2c at 1c2b000 {
+			compatible = "allwinner,sun6i-a31-i2c";
+			reg = <0x01c2b000 0x400>;
+			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_I2C1>;
+			resets = <&ccu RST_BUS_I2C1>;
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		i2c2: i2c at 1c2b400 {
+			compatible = "allwinner,sun6i-a31-i2c";
+			reg = <0x01c2b400 0x400>;
+			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_BUS_I2C2>;
+			resets = <&ccu RST_BUS_I2C2>;
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		gic: interrupt-controller at 1c81000 {
+			compatible = "arm,gic-400";
+			reg = <0x01c81000 0x1000>,
+			      <0x01c82000 0x2000>,
+			      <0x01c84000 0x2000>,
+			      <0x01c86000 0x2000>;
+			interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
+			interrupt-controller;
+			#interrupt-cells = <3>;
+		};
+	};
+};
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 6/9] clk: sunxi-ng: Add A64 clocks
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

Add the A64 CCU clocks set.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 Documentation/devicetree/bindings/clock/sunxi-ccu.txt |   1 +-
 drivers/clk/sunxi-ng/Kconfig                          |  11 +-
 drivers/clk/sunxi-ng/Makefile                         |   1 +-
 drivers/clk/sunxi-ng/ccu-sun50i-a64.c                 | 918 +++++++++++-
 drivers/clk/sunxi-ng/ccu-sun50i-a64.h                 |  72 +-
 include/dt-bindings/clock/sun50i-a64-ccu.h            | 134 ++-
 include/dt-bindings/reset/sun50i-a64-ccu.h            |  98 +-
 7 files changed, 1235 insertions(+), 0 deletions(-)
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-a64.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-a64.h
 create mode 100644 include/dt-bindings/clock/sun50i-a64-ccu.h
 create mode 100644 include/dt-bindings/reset/sun50i-a64-ccu.h

diff --git a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
index 3868458a5feb..74d44a4273f2 100644
--- a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
@@ -7,6 +7,7 @@ Required properties :
 		- "allwinner,sun8i-a23-ccu"
 		- "allwinner,sun8i-a33-ccu"
 		- "allwinner,sun8i-h3-ccu"
+		- "allwinner,sun50i-a64-ccu"
 
 - reg: Must contain the registers base address and length
 - clocks: phandle to the oscillators feeding the CCU. Two are needed:
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 1b4c55a53d7a..8454c6e3dd65 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -53,6 +53,17 @@ config SUNXI_CCU_MP
 
 # SoC Drivers
 
+config SUN50I_A64_CCU
+	bool "Support for the Allwinner A64 CCU"
+	select SUNXI_CCU_DIV
+	select SUNXI_CCU_NK
+	select SUNXI_CCU_NKM
+	select SUNXI_CCU_NKMP
+	select SUNXI_CCU_NM
+	select SUNXI_CCU_MP
+	select SUNXI_CCU_PHASE
+	default ARM64 && ARCH_SUNXI
+
 config SUN6I_A31_CCU
 	bool "Support for the Allwinner A31/A31s CCU"
 	select SUNXI_CCU_DIV
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 106cba27c331..24fbc6e5deb8 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SUNXI_CCU_NM)	+= ccu_nm.o
 obj-$(CONFIG_SUNXI_CCU_MP)	+= ccu_mp.o
 
 # SoC support
+obj-$(CONFIG_SUN50I_A64_CCU)	+= ccu-sun50i-a64.o
 obj-$(CONFIG_SUN6I_A31_CCU)	+= ccu-sun6i-a31.o
 obj-$(CONFIG_SUN8I_A23_CCU)	+= ccu-sun8i-a23.o
 obj-$(CONFIG_SUN8I_A33_CCU)	+= ccu-sun8i-a33.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
new file mode 100644
index 000000000000..c0e96bf6d104
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
@@ -0,0 +1,918 @@
+/*
+ * Copyright (c) 2016 Maxime Ripard. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun50i-a64.h"
+
+static struct ccu_nkmp pll_cpux_clk = {
+	.enable		= BIT(31),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT(8, 5),
+	.k		= _SUNXI_CCU_MULT(4, 2),
+	.m		= _SUNXI_CCU_DIV(0, 2),
+	.p		= _SUNXI_CCU_DIV_MAX(16, 2, 4),
+	.common		= {
+		.reg		= 0x000,
+		.hw.init	= CLK_HW_INIT("pll-cpux",
+					      "osc24M",
+					      &ccu_nkmp_ops,
+					      0),
+	},
+};
+
+/*
+ * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
+ * the base (2x, 4x and 8x), and one variable divider (the one true
+ * pll audio).
+ *
+ * We don't have any need for the variable divider for now, so we just
+ * hardcode it to match with the clock names
+ */
+#define SUN50I_A64_PLL_AUDIO_REG	0x008
+
+static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
+				   "osc24M", 0x008,
+				   8, 7,	/* N */
+				   0, 5,	/* M */
+				   BIT(31),	/* gate */
+				   BIT(28),	/* lock */
+				   0);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0",
+					"osc24M", 0x010,
+					8, 7,		/* N */
+					0, 4,		/* M */
+					BIT(24),	/* frac enable */
+					BIT(25),	/* frac select */
+					270000000,	/* frac rate 0 */
+					297000000,	/* frac rate 1 */
+					BIT(31),	/* gate */
+					BIT(28),	/* lock */
+					0);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
+					"osc24M", 0x018,
+					8, 7,		/* N */
+					0, 4,		/* M */
+					BIT(24),	/* frac enable */
+					BIT(25),	/* frac select */
+					270000000,	/* frac rate 0 */
+					297000000,	/* frac rate 1 */
+					BIT(31),	/* gate */
+					BIT(28),	/* lock */
+					0);
+
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0",
+				    "osc24M", 0x020,
+				    8, 5,	/* N */
+				    4, 2,	/* K */
+				    0, 2,	/* M */
+				    BIT(31),	/* gate */
+				    BIT(28),	/* lock */
+				    0);
+
+static struct ccu_nk pll_periph0_clk = {
+	.enable		= BIT(31),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT(8, 5),
+	.k		= _SUNXI_CCU_MULT_MIN(4, 2, 2),
+	.fixed_post_div	= 2,
+	.common		= {
+		.reg		= 0x028,
+		.features	= CCU_FEATURE_FIXED_POSTDIV,
+		.hw.init	= CLK_HW_INIT("pll-periph0", "osc24M",
+					      &ccu_nk_ops, 0),
+	},
+};
+
+static struct ccu_nk pll_periph1_clk = {
+	.enable		= BIT(31),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT(8, 5),
+	.k		= _SUNXI_CCU_MULT_MIN(4, 2, 2),
+	.fixed_post_div	= 2,
+	.common		= {
+		.reg		= 0x02c,
+		.features	= CCU_FEATURE_FIXED_POSTDIV,
+		.hw.init	= CLK_HW_INIT("pll-periph1", "osc24M",
+					      &ccu_nk_ops, 0),
+	},
+};
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1",
+					"osc24M", 0x030,
+					8, 7,		/* N */
+					0, 4,		/* M */
+					BIT(24),	/* frac enable */
+					BIT(25),	/* frac select */
+					270000000,	/* frac rate 0 */
+					297000000,	/* frac rate 1 */
+					BIT(31),	/* gate */
+					BIT(28),	/* lock */
+					0);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu",
+					"osc24M", 0x038,
+					8, 7,		/* N */
+					0, 4,		/* M */
+					BIT(24),	/* frac enable */
+					BIT(25),	/* frac select */
+					270000000,	/* frac rate 0 */
+					297000000,	/* frac rate 1 */
+					BIT(31),	/* gate */
+					BIT(28),	/* lock */
+					0);
+
+/*
+ * The output function can be changed to something more complex that
+ * we do not handle yet.
+ *
+ * Hardcode the mode so that we don't fall in that case.
+ */
+#define SUN50I_A64_PLL_MIPI_REG		0x040
+
+struct ccu_nkm pll_mipi_clk = {
+	.enable		= BIT(31),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT(8, 4),
+	.k		= _SUNXI_CCU_MULT_MIN(4, 2, 2),
+	.m		= _SUNXI_CCU_DIV(0, 4),
+	.common		= {
+		.reg		= 0x040,
+		.hw.init	= CLK_HW_INIT("pll-mipi", "pll-video0",
+					      &ccu_nkm_ops, 0),
+	},
+};
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_hsic_clk, "pll-hsic",
+					"osc24M", 0x044,
+					8, 7,		/* N */
+					0, 4,		/* M */
+					BIT(24),	/* frac enable */
+					BIT(25),	/* frac select */
+					270000000,	/* frac rate 0 */
+					297000000,	/* frac rate 1 */
+					BIT(31),	/* gate */
+					BIT(28),	/* lock */
+					0);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de",
+					"osc24M", 0x048,
+					8, 7,		/* N */
+					0, 4,		/* M */
+					BIT(24),	/* frac enable */
+					BIT(25),	/* frac select */
+					270000000,	/* frac rate 0 */
+					297000000,	/* frac rate 1 */
+					BIT(31),	/* gate */
+					BIT(28),	/* lock */
+					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 cpux_parents[] = { "osc32k", "osc24M",
+					     "pll-cpux" , "pll-cpux" };
+static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
+		     0x050, 16, 2, CLK_IS_CRITICAL);
+
+static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0);
+
+static const char * const ahb1_parents[] = { "osc32k", "osc24M",
+					     "axi" , "pll-periph0" };
+static struct ccu_div ahb1_clk = {
+	.div		= _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+	.mux		= {
+		.shift	= 12,
+		.width	= 2,
+
+		.variable_prediv	= {
+			.index	= 3,
+			.shift	= 6,
+			.width	= 2,
+		},
+	},
+
+	.common		= {
+		.reg		= 0x054,
+		.features	= CCU_FEATURE_VARIABLE_PREDIV,
+		.hw.init	= CLK_HW_INIT_PARENTS("ahb1",
+						      ahb1_parents,
+						      &ccu_div_ops,
+						      0),
+	},
+};
+
+static struct clk_div_table apb1_div_table[] = {
+	{ .val = 0, .div = 2 },
+	{ .val = 1, .div = 2 },
+	{ .val = 2, .div = 4 },
+	{ .val = 3, .div = 8 },
+	{ /* Sentinel */ },
+};
+static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1",
+			   0x054, 8, 2, apb1_div_table, 0);
+
+static const char * const apb2_parents[] = { "osc32k", "osc24M",
+					     "pll-periph0-2x" ,
+					     "pll-periph0-2x" };
+static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
+			     0, 5,	/* M */
+			     16, 2,	/* P */
+			     24, 2,	/* mux */
+			     0);
+
+static const char * const ahb2_parents[] = { "ahb1" , "pll-periph0" };
+static const struct ccu_mux_fixed_prediv ahb2_fixed_predivs[] = {
+	{ .index = 1, .div = 2 },
+};
+static struct ccu_mux ahb2_clk = {
+	.mux		= {
+		.shift	= 0,
+		.width	= 1,
+		.fixed_predivs	= ahb2_fixed_predivs,
+		.n_predivs	= ARRAY_SIZE(ahb2_fixed_predivs),
+	},
+
+	.common		= {
+		.reg		= 0x05c,
+		.features	= CCU_FEATURE_FIXED_PREDIV,
+		.hw.init	= CLK_HW_INIT_PARENTS("ahb2",
+						      ahb2_parents,
+						      &ccu_mux_ops,
+						      0),
+	},
+};
+
+static SUNXI_CCU_GATE(bus_mipi_dsi_clk,	"bus-mipi-dsi",	"ahb1",
+		      0x060, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_ce_clk,	"bus-ce",	"ahb1",
+		      0x060, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_dma_clk,	"bus-dma",	"ahb1",
+		      0x060, BIT(6), 0);
+static SUNXI_CCU_GATE(bus_mmc0_clk,	"bus-mmc0",	"ahb1",
+		      0x060, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_mmc1_clk,	"bus-mmc1",	"ahb1",
+		      0x060, BIT(9), 0);
+static SUNXI_CCU_GATE(bus_mmc2_clk,	"bus-mmc2",	"ahb1",
+		      0x060, BIT(10), 0);
+static SUNXI_CCU_GATE(bus_nand_clk,	"bus-nand",	"ahb1",
+		      0x060, BIT(13), 0);
+static SUNXI_CCU_GATE(bus_dram_clk,	"bus-dram",	"ahb1",
+		      0x060, BIT(14), 0);
+static SUNXI_CCU_GATE(bus_emac_clk,	"bus-emac",	"ahb2",
+		      0x060, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_ts_clk,	"bus-ts",	"ahb1",
+		      0x060, BIT(18), 0);
+static SUNXI_CCU_GATE(bus_hstimer_clk,	"bus-hstimer",	"ahb1",
+		      0x060, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_spi0_clk,	"bus-spi0",	"ahb1",
+		      0x060, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_spi1_clk,	"bus-spi1",	"ahb1",
+		      0x060, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_otg_clk,	"bus-otg",	"ahb1",
+		      0x060, BIT(23), 0);
+static SUNXI_CCU_GATE(bus_ehci0_clk,	"bus-ehci0",	"ahb1",
+		      0x060, BIT(24), 0);
+static SUNXI_CCU_GATE(bus_ehci1_clk,	"bus-ehci1",	"ahb2",
+		      0x060, BIT(25), 0);
+static SUNXI_CCU_GATE(bus_ohci0_clk,	"bus-ohci0",	"ahb1",
+		      0x060, BIT(28), 0);
+static SUNXI_CCU_GATE(bus_ohci1_clk,	"bus-ohci1",	"ahb2",
+		      0x060, BIT(29), 0);
+
+static SUNXI_CCU_GATE(bus_ve_clk,	"bus-ve",	"ahb1",
+		      0x064, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_tcon0_clk,	"bus-tcon0",	"ahb1",
+		      0x064, BIT(3), 0);
+static SUNXI_CCU_GATE(bus_tcon1_clk,	"bus-tcon1",	"ahb1",
+		      0x064, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_deinterlace_clk,	"bus-deinterlace",	"ahb1",
+		      0x064, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_csi_clk,	"bus-csi",	"ahb1",
+		      0x064, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_hdmi_clk,	"bus-hdmi",	"ahb1",
+		      0x064, BIT(11), 0);
+static SUNXI_CCU_GATE(bus_de_clk,	"bus-de",	"ahb1",
+		      0x064, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_gpu_clk,	"bus-gpu",	"ahb1",
+		      0x064, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_msgbox_clk,	"bus-msgbox",	"ahb1",
+		      0x064, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_spinlock_clk,	"bus-spinlock",	"ahb1",
+		      0x064, BIT(22), 0);
+
+static SUNXI_CCU_GATE(bus_codec_clk,	"bus-codec",	"apb1",
+		      0x068, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_spdif_clk,	"bus-spdif",	"apb1",
+		      0x068, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_pio_clk,	"bus-pio",	"apb1",
+		      0x068, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_ths_clk,	"bus-ths",	"apb1",
+		      0x068, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_i2s0_clk,	"bus-i2s0",	"apb1",
+		      0x068, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_i2s1_clk,	"bus-i2s1",	"apb1",
+		      0x068, BIT(13), 0);
+static SUNXI_CCU_GATE(bus_i2s2_clk,	"bus-i2s2",	"apb1",
+		      0x068, BIT(14), 0);
+
+static SUNXI_CCU_GATE(bus_i2c0_clk,	"bus-i2c0",	"apb2",
+		      0x06c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_i2c1_clk,	"bus-i2c1",	"apb2",
+		      0x06c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_i2c2_clk,	"bus-i2c2",	"apb2",
+		      0x06c, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_scr_clk,	"bus-scr",	"apb2",
+		      0x06c, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_uart0_clk,	"bus-uart0",	"apb2",
+		      0x06c, BIT(16), 0);
+static SUNXI_CCU_GATE(bus_uart1_clk,	"bus-uart1",	"apb2",
+		      0x06c, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_uart2_clk,	"bus-uart2",	"apb2",
+		      0x06c, BIT(18), 0);
+static SUNXI_CCU_GATE(bus_uart3_clk,	"bus-uart3",	"apb2",
+		      0x06c, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_uart4_clk,	"bus-uart4",	"apb2",
+		      0x06c, BIT(20), 0);
+
+static SUNXI_CCU_GATE(bus_dbg_clk,	"bus-dbg",	"ahb1",
+		      0x070, BIT(7), 0);
+
+static struct clk_div_table ths_div_table[] = {
+	{ .val = 0, .div = 1 },
+	{ .val = 1, .div = 2 },
+	{ .val = 2, .div = 4 },
+	{ .val = 3, .div = 6 },
+};
+static const char * const ths_parents[] = { "osc24M" };
+static struct ccu_div ths_clk = {
+	.enable	= BIT(31),
+	.div	= _SUNXI_CCU_DIV_TABLE(0, 2, ths_div_table),
+	.mux	= _SUNXI_CCU_MUX(24, 2),
+	.common	= {
+		.reg		= 0x074,
+		.hw.init	= CLK_HW_INIT_PARENTS("ths",
+						      ths_parents,
+						      &ccu_div_ops,
+						      0),
+	},
+};
+
+static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0",
+						     "pll-periph1" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const char * const mmc_default_parents[] = { "osc24M", "pll-periph0-2x",
+						    "pll-periph1-2x" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc_default_parents, 0x088,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc_default_parents, 0x08c,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc_default_parents, 0x090,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const char * const ts_parents[] = { "osc24M", "pll-periph0", };
+static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 4,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", mmc_default_parents, 0x09c,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4,
+				  0, 4,		/* M */
+				  16, 2,	/* P */
+				  24, 2,	/* mux */
+				  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_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents,
+			       0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents,
+			       0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio",
+			     0x0c0, 0, 4, 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_phy1_clk,	"usb-phy1",	"osc24M",
+		      0x0cc, BIT(9), 0);
+static SUNXI_CCU_GATE(usb_hsic_clk,	"usb-hsic",	"pll-hsic",
+		      0x0cc, BIT(10), 0);
+static SUNXI_CCU_GATE(usb_hsic_12m_clk,	"usb-hsic-12M",	"osc12M",
+		      0x0cc, BIT(11), 0);
+static SUNXI_CCU_GATE(usb_ohci0_clk,	"usb-ohci0",	"osc12M",
+		      0x0cc, BIT(16), 0);
+static SUNXI_CCU_GATE(usb_ohci1_clk,	"usb-ohci1",	"usb-ohci0",
+		      0x0cc, BIT(17), 0);
+
+static const char * const dram_parents[] = { "pll-ddr0", "pll-ddr1" };
+static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents,
+			    0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL);
+
+static SUNXI_CCU_GATE(dram_ve_clk,	"dram-ve",	"dram",
+		      0x100, BIT(0), 0);
+static SUNXI_CCU_GATE(dram_csi_clk,	"dram-csi",	"dram",
+		      0x100, BIT(1), 0);
+static SUNXI_CCU_GATE(dram_deinterlace_clk,	"dram-deinterlace",	"dram",
+		      0x100, BIT(2), 0);
+static SUNXI_CCU_GATE(dram_ts_clk,	"dram-ts",	"dram",
+		      0x100, BIT(3), 0);
+
+static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" };
+static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents,
+				 0x104, 0, 4, 24, 3, BIT(31), 0);
+
+static const char * const tcon0_parents[] = { "pll-mipi", "pll-video0-2x" };
+static const u8 tcon0_table[] = { 0, 2, };
+static SUNXI_CCU_MUX_TABLE_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents,
+				     tcon0_table, 0x118, 24, 3, BIT(31),
+				     CLK_SET_RATE_PARENT);
+
+static const char * const tcon1_parents[] = { "pll-video0", "pll-video1" };
+static const u8 tcon1_table[] = { 0, 2, };
+struct ccu_div tcon1_clk = {
+	.enable		= BIT(31),
+	.div		= _SUNXI_CCU_DIV(0, 4),
+	.mux		= _SUNXI_CCU_MUX_TABLE(24, 3, tcon1_table),
+	.common		= {
+		.reg		= 0x11c,
+		.hw.init	= CLK_HW_INIT_PARENTS("tcon1",
+						      tcon1_parents,
+						      &ccu_div_ops,
+						      CLK_SET_RATE_PARENT),
+	},
+};
+
+static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents,
+				 0x124, 0, 4, 24, 3, BIT(31), 0);
+
+static SUNXI_CCU_GATE(csi_misc_clk,	"csi-misc",	"osc24M",
+		      0x130, BIT(31), 0);
+
+static const char * const csi_sclk_parents[] = { "pll-periph0", "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents,
+				 0x134, 16, 4, 24, 3, BIT(31), 0);
+
+static const char * const csi_mclk_parents[] = { "osc24M", "pll-video1", "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents,
+				 0x134, 0, 5, 8, 3, BIT(15), 0);
+
+static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve",
+			     0x13c, 16, 3, BIT(31), 0);
+
+static SUNXI_CCU_GATE(ac_dig_clk,	"ac-dig",	"pll-audio",
+		      0x140, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(ac_dig_4x_clk,	"ac-dig-4x",	"pll-audio-4x",
+		      0x140, BIT(30), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(avs_clk,		"avs",		"osc24M",
+		      0x144, BIT(31), 0);
+
+static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents,
+				 0x150, 0, 4, 24, 2, BIT(31), 0);
+
+static SUNXI_CCU_GATE(hdmi_ddc_clk,	"hdmi-ddc",	"osc24M",
+		      0x154, BIT(31), 0);
+
+static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x",
+						 "pll-ddr0", "pll-ddr1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents,
+				 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL);
+
+static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-periph0" };
+static const u8 dsi_dphy_table[] = { 0, 2, };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy",
+				       dsi_dphy_parents, dsi_dphy_table,
+				       0x168, 0, 3, 24, 2, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu",
+			     0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT);
+
+/* Fixed Factor clocks */
+static CLK_FIXED_FACTOR(osc12M_clk, "osc12M", "osc24M", 1, 2, 0);
+
+/* We hardcode the divider to 4 for now */
+static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
+			"pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
+			"pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
+			"pll-audio-base", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x",
+			"pll-audio-base", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x",
+			"pll-periph0", 1, 2, 0);
+static CLK_FIXED_FACTOR(pll_periph1_2x_clk, "pll-periph1-2x",
+			"pll-periph1", 1, 2, 0);
+static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x",
+			"pll-video0", 1, 2, CLK_SET_RATE_PARENT);
+
+static struct ccu_common *sun50i_a64_ccu_clks[] = {
+	&pll_cpux_clk.common,
+	&pll_audio_base_clk.common,
+	&pll_video0_clk.common,
+	&pll_ve_clk.common,
+	&pll_ddr0_clk.common,
+	&pll_periph0_clk.common,
+	&pll_periph1_clk.common,
+	&pll_video1_clk.common,
+	&pll_gpu_clk.common,
+	&pll_mipi_clk.common,
+	&pll_hsic_clk.common,
+	&pll_de_clk.common,
+	&pll_ddr1_clk.common,
+	&cpux_clk.common,
+	&axi_clk.common,
+	&ahb1_clk.common,
+	&apb1_clk.common,
+	&apb2_clk.common,
+	&ahb2_clk.common,
+	&bus_mipi_dsi_clk.common,
+	&bus_ce_clk.common,
+	&bus_dma_clk.common,
+	&bus_mmc0_clk.common,
+	&bus_mmc1_clk.common,
+	&bus_mmc2_clk.common,
+	&bus_nand_clk.common,
+	&bus_dram_clk.common,
+	&bus_emac_clk.common,
+	&bus_ts_clk.common,
+	&bus_hstimer_clk.common,
+	&bus_spi0_clk.common,
+	&bus_spi1_clk.common,
+	&bus_otg_clk.common,
+	&bus_ehci0_clk.common,
+	&bus_ehci1_clk.common,
+	&bus_ohci0_clk.common,
+	&bus_ohci1_clk.common,
+	&bus_ve_clk.common,
+	&bus_tcon0_clk.common,
+	&bus_tcon1_clk.common,
+	&bus_deinterlace_clk.common,
+	&bus_csi_clk.common,
+	&bus_hdmi_clk.common,
+	&bus_de_clk.common,
+	&bus_gpu_clk.common,
+	&bus_msgbox_clk.common,
+	&bus_spinlock_clk.common,
+	&bus_codec_clk.common,
+	&bus_spdif_clk.common,
+	&bus_pio_clk.common,
+	&bus_ths_clk.common,
+	&bus_i2s0_clk.common,
+	&bus_i2s1_clk.common,
+	&bus_i2s2_clk.common,
+	&bus_i2c0_clk.common,
+	&bus_i2c1_clk.common,
+	&bus_i2c2_clk.common,
+	&bus_scr_clk.common,
+	&bus_uart0_clk.common,
+	&bus_uart1_clk.common,
+	&bus_uart2_clk.common,
+	&bus_uart3_clk.common,
+	&bus_uart4_clk.common,
+	&bus_dbg_clk.common,
+	&ths_clk.common,
+	&nand_clk.common,
+	&mmc0_clk.common,
+	&mmc1_clk.common,
+	&mmc2_clk.common,
+	&ts_clk.common,
+	&ce_clk.common,
+	&spi0_clk.common,
+	&spi1_clk.common,
+	&i2s0_clk.common,
+	&i2s1_clk.common,
+	&i2s2_clk.common,
+	&spdif_clk.common,
+	&usb_phy0_clk.common,
+	&usb_phy1_clk.common,
+	&usb_hsic_clk.common,
+	&usb_hsic_12m_clk.common,
+	&usb_ohci0_clk.common,
+	&usb_ohci1_clk.common,
+	&dram_clk.common,
+	&dram_ve_clk.common,
+	&dram_csi_clk.common,
+	&dram_deinterlace_clk.common,
+	&dram_ts_clk.common,
+	&de_clk.common,
+	&tcon0_clk.common,
+	&tcon1_clk.common,
+	&deinterlace_clk.common,
+	&csi_misc_clk.common,
+	&csi_sclk_clk.common,
+	&csi_mclk_clk.common,
+	&ve_clk.common,
+	&ac_dig_clk.common,
+	&ac_dig_4x_clk.common,
+	&avs_clk.common,
+	&hdmi_clk.common,
+	&hdmi_ddc_clk.common,
+	&mbus_clk.common,
+	&dsi_dphy_clk.common,
+	&gpu_clk.common,
+};
+
+static struct clk_hw_onecell_data sun50i_a64_hw_clks = {
+	.hws	= {
+		[CLK_OSC_12M]		= &osc12M_clk.hw,
+		[CLK_PLL_CPUX]		= &pll_cpux_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_VIDEO0]	= &pll_video0_clk.common.hw,
+		[CLK_PLL_VIDEO0_2X]	= &pll_video0_2x_clk.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_PERIPH1]	= &pll_periph1_clk.common.hw,
+		[CLK_PLL_PERIPH1_2X]	= &pll_periph1_2x_clk.hw,
+		[CLK_PLL_VIDEO1]	= &pll_video1_clk.common.hw,
+		[CLK_PLL_GPU]		= &pll_gpu_clk.common.hw,
+		[CLK_PLL_MIPI]  	= &pll_mipi_clk.common.hw,
+		[CLK_PLL_HSIC]		= &pll_hsic_clk.common.hw,
+		[CLK_PLL_DE]		= &pll_de_clk.common.hw,
+		[CLK_PLL_DDR1]		= &pll_ddr1_clk.common.hw,
+		[CLK_CPUX]		= &cpux_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_MIPI_DSI]	= &bus_mipi_dsi_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_NAND]		= &bus_nand_clk.common.hw,
+		[CLK_BUS_DRAM]		= &bus_dram_clk.common.hw,
+		[CLK_BUS_EMAC]		= &bus_emac_clk.common.hw,
+		[CLK_BUS_TS]		= &bus_ts_clk.common.hw,
+		[CLK_BUS_HSTIMER]	= &bus_hstimer_clk.common.hw,
+		[CLK_BUS_SPI0]		= &bus_spi0_clk.common.hw,
+		[CLK_BUS_SPI1]		= &bus_spi1_clk.common.hw,
+		[CLK_BUS_OTG]		= &bus_otg_clk.common.hw,
+		[CLK_BUS_EHCI0]		= &bus_ehci0_clk.common.hw,
+		[CLK_BUS_EHCI1]		= &bus_ehci1_clk.common.hw,
+		[CLK_BUS_OHCI0]		= &bus_ohci0_clk.common.hw,
+		[CLK_BUS_OHCI1]		= &bus_ohci1_clk.common.hw,
+		[CLK_BUS_VE]		= &bus_ve_clk.common.hw,
+		[CLK_BUS_TCON0]		= &bus_tcon0_clk.common.hw,
+		[CLK_BUS_TCON1]		= &bus_tcon1_clk.common.hw,
+		[CLK_BUS_DEINTERLACE]	= &bus_deinterlace_clk.common.hw,
+		[CLK_BUS_CSI]		= &bus_csi_clk.common.hw,
+		[CLK_BUS_HDMI]		= &bus_hdmi_clk.common.hw,
+		[CLK_BUS_DE]		= &bus_de_clk.common.hw,
+		[CLK_BUS_GPU]		= &bus_gpu_clk.common.hw,
+		[CLK_BUS_MSGBOX]	= &bus_msgbox_clk.common.hw,
+		[CLK_BUS_SPINLOCK]	= &bus_spinlock_clk.common.hw,
+		[CLK_BUS_CODEC]		= &bus_codec_clk.common.hw,
+		[CLK_BUS_SPDIF]		= &bus_spdif_clk.common.hw,
+		[CLK_BUS_PIO]		= &bus_pio_clk.common.hw,
+		[CLK_BUS_THS]		= &bus_ths_clk.common.hw,
+		[CLK_BUS_I2S0]		= &bus_i2s0_clk.common.hw,
+		[CLK_BUS_I2S1]		= &bus_i2s1_clk.common.hw,
+		[CLK_BUS_I2S2]		= &bus_i2s2_clk.common.hw,
+		[CLK_BUS_I2C0]		= &bus_i2c0_clk.common.hw,
+		[CLK_BUS_I2C1]		= &bus_i2c1_clk.common.hw,
+		[CLK_BUS_I2C2]		= &bus_i2c2_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_UART3]		= &bus_uart3_clk.common.hw,
+		[CLK_BUS_UART4]		= &bus_uart4_clk.common.hw,
+		[CLK_BUS_SCR]		= &bus_scr_clk.common.hw,
+		[CLK_BUS_DBG]		= &bus_dbg_clk.common.hw,
+		[CLK_THS]		= &ths_clk.common.hw,
+		[CLK_NAND]		= &nand_clk.common.hw,
+		[CLK_MMC0]		= &mmc0_clk.common.hw,
+		[CLK_MMC1]		= &mmc1_clk.common.hw,
+		[CLK_MMC2]		= &mmc2_clk.common.hw,
+		[CLK_TS]		= &ts_clk.common.hw,
+		[CLK_CE]		= &ce_clk.common.hw,
+		[CLK_SPI0]		= &spi0_clk.common.hw,
+		[CLK_SPI1]		= &spi1_clk.common.hw,
+		[CLK_I2S0]		= &i2s0_clk.common.hw,
+		[CLK_I2S1]		= &i2s1_clk.common.hw,
+		[CLK_I2S2]		= &i2s2_clk.common.hw,
+		[CLK_SPDIF]		= &spdif_clk.common.hw,
+		[CLK_USB_PHY0]		= &usb_phy0_clk.common.hw,
+		[CLK_USB_PHY1]		= &usb_phy1_clk.common.hw,
+		[CLK_USB_HSIC]		= &usb_hsic_clk.common.hw,
+		[CLK_USB_HSIC_12M]	= &usb_hsic_12m_clk.common.hw,
+		[CLK_USB_OHCI0]		= &usb_ohci0_clk.common.hw,
+		[CLK_USB_OHCI1]		= &usb_ohci1_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_DEINTERLACE]	= &dram_deinterlace_clk.common.hw,
+		[CLK_DRAM_TS]		= &dram_ts_clk.common.hw,
+		[CLK_DE]		= &de_clk.common.hw,
+		[CLK_TCON0]		= &tcon0_clk.common.hw,
+		[CLK_TCON1]		= &tcon1_clk.common.hw,
+		[CLK_DEINTERLACE]	= &deinterlace_clk.common.hw,
+		[CLK_CSI_MISC]		= &csi_misc_clk.common.hw,
+		[CLK_CSI_SCLK]		= &csi_sclk_clk.common.hw,
+		[CLK_CSI_MCLK]		= &csi_mclk_clk.common.hw,
+		[CLK_VE]		= &ve_clk.common.hw,
+		[CLK_AC_DIG]		= &ac_dig_clk.common.hw,
+		[CLK_AC_DIG_4X]		= &ac_dig_4x_clk.common.hw,
+		[CLK_AVS]		= &avs_clk.common.hw,
+		[CLK_HDMI]		= &hdmi_clk.common.hw,
+		[CLK_HDMI_DDC]		= &hdmi_ddc_clk.common.hw,
+		[CLK_MBUS]		= &mbus_clk.common.hw,
+		[CLK_DSI_DPHY]		= &dsi_dphy_clk.common.hw,
+		[CLK_GPU]		= &gpu_clk.common.hw,
+	},
+	.num	= CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun50i_a64_ccu_resets[] = {
+	[RST_USB_PHY0]		=  { 0x0cc, BIT(0) },
+	[RST_USB_PHY1]		=  { 0x0cc, BIT(1) },
+	[RST_USB_HSIC]		=  { 0x0cc, BIT(2) },
+
+	[RST_DRAM]		=  { 0x0f4, BIT(31) },
+	[RST_MBUS]		=  { 0x0fc, BIT(31) },
+
+	[RST_BUS_MIPI_DSI]	=  { 0x2c0, BIT(1) },
+	[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_NAND]		=  { 0x2c0, BIT(13) },
+	[RST_BUS_DRAM]		=  { 0x2c0, BIT(14) },
+	[RST_BUS_EMAC]		=  { 0x2c0, BIT(17) },
+	[RST_BUS_TS]		=  { 0x2c0, BIT(18) },
+	[RST_BUS_HSTIMER]	=  { 0x2c0, BIT(19) },
+	[RST_BUS_SPI0]		=  { 0x2c0, BIT(20) },
+	[RST_BUS_SPI1]		=  { 0x2c0, BIT(21) },
+	[RST_BUS_OTG]		=  { 0x2c0, BIT(23) },
+	[RST_BUS_EHCI0]		=  { 0x2c0, BIT(24) },
+	[RST_BUS_EHCI1]		=  { 0x2c0, BIT(25) },
+	[RST_BUS_OHCI0]		=  { 0x2c0, BIT(28) },
+	[RST_BUS_OHCI1]		=  { 0x2c0, BIT(29) },
+
+	[RST_BUS_VE]		=  { 0x2c4, BIT(0) },
+	[RST_BUS_TCON0]		=  { 0x2c4, BIT(3) },
+	[RST_BUS_TCON1]		=  { 0x2c4, BIT(4) },
+	[RST_BUS_DEINTERLACE]	=  { 0x2c4, BIT(5) },
+	[RST_BUS_CSI]		=  { 0x2c4, BIT(8) },
+	[RST_BUS_HDMI0]		=  { 0x2c4, BIT(10) },
+	[RST_BUS_HDMI1]		=  { 0x2c4, BIT(11) },
+	[RST_BUS_DE]		=  { 0x2c4, BIT(12) },
+	[RST_BUS_GPU]		=  { 0x2c4, BIT(20) },
+	[RST_BUS_MSGBOX]	=  { 0x2c4, BIT(21) },
+	[RST_BUS_SPINLOCK]	=  { 0x2c4, BIT(22) },
+	[RST_BUS_DBG]		=  { 0x2c4, BIT(31) },
+
+	[RST_BUS_LVDS]		=  { 0x2c8, BIT(0) },
+
+	[RST_BUS_CODEC]		=  { 0x2d0, BIT(0) },
+	[RST_BUS_SPDIF]		=  { 0x2d0, BIT(1) },
+	[RST_BUS_THS]		=  { 0x2d0, BIT(8) },
+	[RST_BUS_I2S0]		=  { 0x2d0, BIT(12) },
+	[RST_BUS_I2S1]		=  { 0x2d0, BIT(13) },
+	[RST_BUS_I2S2]		=  { 0x2d0, BIT(14) },
+
+	[RST_BUS_I2C0]		=  { 0x2d8, BIT(0) },
+	[RST_BUS_I2C1]		=  { 0x2d8, BIT(1) },
+	[RST_BUS_I2C2]		=  { 0x2d8, BIT(2) },
+	[RST_BUS_SCR]		=  { 0x2d8, BIT(5) },
+	[RST_BUS_UART0]		=  { 0x2d8, BIT(16) },
+	[RST_BUS_UART1]		=  { 0x2d8, BIT(17) },
+	[RST_BUS_UART2]		=  { 0x2d8, BIT(18) },
+	[RST_BUS_UART3]		=  { 0x2d8, BIT(19) },
+	[RST_BUS_UART4]		=  { 0x2d8, BIT(20) },
+};
+
+static const struct sunxi_ccu_desc sun50i_a64_ccu_desc = {
+	.ccu_clks	= sun50i_a64_ccu_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sun50i_a64_ccu_clks),
+
+	.hw_clks	= &sun50i_a64_hw_clks,
+
+	.resets		= sun50i_a64_ccu_resets,
+	.num_resets	= ARRAY_SIZE(sun50i_a64_ccu_resets),
+};
+
+static int sun50i_a64_ccu_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	void __iomem *reg;
+	u32 val;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(reg)) {
+		dev_err(&pdev->dev, "Could not map the clock registers\n");
+		return PTR_ERR(reg);
+	}
+
+	/* Force the PLL-Audio-1x divider to 4 */
+	val = readl(reg + SUN50I_A64_PLL_AUDIO_REG);
+	val &= ~GENMASK(19, 16);
+	writel(val | (3 << 16), reg + SUN50I_A64_PLL_AUDIO_REG);
+
+	writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
+
+	return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc);
+}
+
+static const struct of_device_id sun50i_a64_ccu_ids[] = {
+	{ .compatible = "allwinner,sun50i-a64-ccu" },
+	{ },
+};
+
+static struct platform_driver sun50i_a64_ccu_driver = {
+	.probe	= sun50i_a64_ccu_probe,
+	.driver	= {
+		.name	= "sun50i-a64-ccu",
+		.of_match_table	= sun50i_a64_ccu_ids,
+	},
+};
+builtin_platform_driver(sun50i_a64_ccu_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.h b/drivers/clk/sunxi-ng/ccu-sun50i-a64.h
new file mode 100644
index 000000000000..9b3cd24b78d2
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN50I_A64_H_
+#define _CCU_SUN50I_A64_H_
+
+#include <dt-bindings/clock/sun50i-a64-ccu.h>
+#include <dt-bindings/reset/sun50i-a64-ccu.h>
+
+#define CLK_OSC_12M			0
+#define CLK_PLL_CPUX			1
+#define CLK_PLL_AUDIO_BASE		2
+#define CLK_PLL_AUDIO			3
+#define CLK_PLL_AUDIO_2X		4
+#define CLK_PLL_AUDIO_4X		5
+#define CLK_PLL_AUDIO_8X		6
+#define CLK_PLL_VIDEO0			7
+#define CLK_PLL_VIDEO0_2X		8
+#define CLK_PLL_VE			9
+#define CLK_PLL_DDR0			10
+#define CLK_PLL_PERIPH0			11
+#define CLK_PLL_PERIPH0_2X		12
+#define CLK_PLL_PERIPH1			13
+#define CLK_PLL_PERIPH1_2X		14
+#define CLK_PLL_VIDEO1			15
+#define CLK_PLL_GPU			16
+#define CLK_PLL_MIPI			17
+#define CLK_PLL_HSIC			18
+#define CLK_PLL_DE			19
+#define CLK_PLL_DDR1			20
+#define CLK_CPUX			21
+#define CLK_AXI				22
+#define CLK_APB				23
+#define CLK_AHB1			24
+#define CLK_APB1			25
+#define CLK_APB2			26
+#define CLK_AHB2			27
+
+/* All the bus gates are exported */
+
+/* The first bunch of module clocks are exported */
+
+#define CLK_USB_OHCI0_12M		90
+
+#define CLK_USB_OHCI1_12M		92
+
+#define CLK_DRAM			94
+
+/* All the DRAM gates are exported */
+
+/* Some more module clocks are exported */
+
+#define CLK_MBUS			112
+
+/* And the DSI and GPU module clock is exported */
+
+#define CLK_NUMBER			(CLK_GPU + 1)
+
+#endif /* _CCU_SUN50I_A64_H_ */
diff --git a/include/dt-bindings/clock/sun50i-a64-ccu.h b/include/dt-bindings/clock/sun50i-a64-ccu.h
new file mode 100644
index 000000000000..370c0a0473fc
--- /dev/null
+++ b/include/dt-bindings/clock/sun50i-a64-ccu.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_CLK_SUN50I_A64_H_
+#define _DT_BINDINGS_CLK_SUN50I_A64_H_
+
+#define CLK_BUS_MIPI_DSI	28
+#define CLK_BUS_CE		29
+#define CLK_BUS_DMA		30
+#define CLK_BUS_MMC0		31
+#define CLK_BUS_MMC1		32
+#define CLK_BUS_MMC2		33
+#define CLK_BUS_NAND		34
+#define CLK_BUS_DRAM		35
+#define CLK_BUS_EMAC		36
+#define CLK_BUS_TS		37
+#define CLK_BUS_HSTIMER		38
+#define CLK_BUS_SPI0		39
+#define CLK_BUS_SPI1		40
+#define CLK_BUS_OTG		41
+#define CLK_BUS_EHCI0		42
+#define CLK_BUS_EHCI1		43
+#define CLK_BUS_OHCI0		44
+#define CLK_BUS_OHCI1		45
+#define CLK_BUS_VE		46
+#define CLK_BUS_TCON0		47
+#define CLK_BUS_TCON1		48
+#define CLK_BUS_DEINTERLACE	49
+#define CLK_BUS_CSI		50
+#define CLK_BUS_HDMI		51
+#define CLK_BUS_DE		52
+#define CLK_BUS_GPU		53
+#define CLK_BUS_MSGBOX		54
+#define CLK_BUS_SPINLOCK	55
+#define CLK_BUS_CODEC		56
+#define CLK_BUS_SPDIF		57
+#define CLK_BUS_PIO		58
+#define CLK_BUS_THS		59
+#define CLK_BUS_I2S0		60
+#define CLK_BUS_I2S1		61
+#define CLK_BUS_I2S2		62
+#define CLK_BUS_I2C0		63
+#define CLK_BUS_I2C1		64
+#define CLK_BUS_I2C2		65
+#define CLK_BUS_SCR		66
+#define CLK_BUS_UART0		67
+#define CLK_BUS_UART1		68
+#define CLK_BUS_UART2		69
+#define CLK_BUS_UART3		70
+#define CLK_BUS_UART4		71
+#define CLK_BUS_DBG		72
+#define CLK_THS			73
+#define CLK_NAND		74
+#define CLK_MMC0		75
+#define CLK_MMC1		76
+#define CLK_MMC2		77
+#define CLK_TS			78
+#define CLK_CE			79
+#define CLK_SPI0		80
+#define CLK_SPI1		81
+#define CLK_I2S0		82
+#define CLK_I2S1		83
+#define CLK_I2S2		84
+#define CLK_SPDIF		85
+#define CLK_USB_PHY0		86
+#define CLK_USB_PHY1		87
+#define CLK_USB_HSIC		88
+#define CLK_USB_HSIC_12M	89
+
+#define CLK_USB_OHCI0		91
+
+#define CLK_USB_OHCI1		93
+
+#define CLK_DRAM_VE		95
+#define CLK_DRAM_CSI		96
+#define CLK_DRAM_DEINTERLACE	97
+#define CLK_DRAM_TS		98
+#define CLK_DE			99
+#define CLK_TCON0		100
+#define CLK_TCON1		101
+#define CLK_DEINTERLACE		102
+#define CLK_CSI_MISC		103
+#define CLK_CSI_SCLK		104
+#define CLK_CSI_MCLK		105
+#define CLK_VE			106
+#define CLK_AC_DIG		107
+#define CLK_AC_DIG_4X		108
+#define CLK_AVS			109
+#define CLK_HDMI		110
+#define CLK_HDMI_DDC		111
+
+#define CLK_DSI_DPHY		113
+#define CLK_GPU			114
+
+#endif /* _DT_BINDINGS_CLK_SUN50I_H_ */
diff --git a/include/dt-bindings/reset/sun50i-a64-ccu.h b/include/dt-bindings/reset/sun50i-a64-ccu.h
new file mode 100644
index 000000000000..db60b29ddb11
--- /dev/null
+++ b/include/dt-bindings/reset/sun50i-a64-ccu.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_RST_SUN50I_A64_H_
+#define _DT_BINDINGS_RST_SUN50I_A64_H_
+
+#define RST_USB_PHY0		0
+#define RST_USB_PHY1		1
+#define RST_USB_HSIC		2
+#define RST_DRAM		3
+#define RST_MBUS		4
+#define RST_BUS_MIPI_DSI	5
+#define RST_BUS_CE		6
+#define RST_BUS_DMA		7
+#define RST_BUS_MMC0		8
+#define RST_BUS_MMC1		9
+#define RST_BUS_MMC2		10
+#define RST_BUS_NAND		11
+#define RST_BUS_DRAM		12
+#define RST_BUS_EMAC		13
+#define RST_BUS_TS		14
+#define RST_BUS_HSTIMER		15
+#define RST_BUS_SPI0		16
+#define RST_BUS_SPI1		17
+#define RST_BUS_OTG		18
+#define RST_BUS_EHCI0		19
+#define RST_BUS_EHCI1		20
+#define RST_BUS_OHCI0		21
+#define RST_BUS_OHCI1		22
+#define RST_BUS_VE		23
+#define RST_BUS_TCON0		24
+#define RST_BUS_TCON1		25
+#define RST_BUS_DEINTERLACE	26
+#define RST_BUS_CSI		27
+#define RST_BUS_HDMI0		28
+#define RST_BUS_HDMI1		29
+#define RST_BUS_DE		30
+#define RST_BUS_GPU		31
+#define RST_BUS_MSGBOX		32
+#define RST_BUS_SPINLOCK	33
+#define RST_BUS_DBG		34
+#define RST_BUS_LVDS		35
+#define RST_BUS_CODEC		36
+#define RST_BUS_SPDIF		37
+#define RST_BUS_THS		38
+#define RST_BUS_I2S0		39
+#define RST_BUS_I2S1		40
+#define RST_BUS_I2S2		41
+#define RST_BUS_I2C0		42
+#define RST_BUS_I2C1		43
+#define RST_BUS_I2C2		44
+#define RST_BUS_SCR		45
+#define RST_BUS_UART0		46
+#define RST_BUS_UART1		47
+#define RST_BUS_UART2		48
+#define RST_BUS_UART3		49
+#define RST_BUS_UART4		50
+
+#endif /* _DT_BINDINGS_RST_SUN50I_A64_H_ */
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 5/9] clk: sunxi-ng: Implement minimum for multipliers
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

Allow the CCU drivers to specify a multiplier for their clocks.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi-ng/ccu_mult.c |  2 +-
 drivers/clk/sunxi-ng/ccu_mult.h | 13 +++++++++----
 drivers/clk/sunxi-ng/ccu_nk.c   |  8 ++++----
 drivers/clk/sunxi-ng/ccu_nkm.c  |  8 ++++----
 drivers/clk/sunxi-ng/ccu_nkmp.c |  4 ++--
 drivers/clk/sunxi-ng/ccu_nm.c   |  2 +-
 6 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
index 6a02ffee5386..678b6cb49f01 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.c
+++ b/drivers/clk/sunxi-ng/ccu_mult.c
@@ -105,7 +105,7 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
 						&parent_rate);
 
-	_cm.min = 1;
+	_cm.min = cm->mult.min;
 	_cm.max = 1 << cm->mult.width;
 	ccu_mult_find_best(parent_rate, rate, &_cm);
 
diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h
index 113780b7558e..c1a2134bdc71 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.h
+++ b/drivers/clk/sunxi-ng/ccu_mult.h
@@ -7,14 +7,19 @@
 struct ccu_mult_internal {
 	u8	shift;
 	u8	width;
+	u8	min;
 };
 
-#define _SUNXI_CCU_MULT(_shift, _width)		\
-	{					\
-		.shift	= _shift,		\
-		.width	= _width,		\
+#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min)	\
+	{						\
+		.shift	= _shift,			\
+		.width	= _width,			\
+		.min	= _min,				\
 	}
 
+#define _SUNXI_CCU_MULT(_shift, _width)		\
+	_SUNXI_CCU_MULT_MIN(_shift, _width, 1)
+
 struct ccu_mult {
 	u32			enable;
 
diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c
index a42d870ba0ef..eaf0fdf78d2b 100644
--- a/drivers/clk/sunxi-ng/ccu_nk.c
+++ b/drivers/clk/sunxi-ng/ccu_nk.c
@@ -97,9 +97,9 @@ static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate,
 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
 		rate *= nk->fixed_post_div;
 
-	_nk.min_n = 1;
+	_nk.min_n = nk->n.min;
 	_nk.max_n = 1 << nk->n.width;
-	_nk.min_k = 1;
+	_nk.min_k = nk->k.min;
 	_nk.max_k = 1 << nk->k.width;
 
 	ccu_nk_find_best(*parent_rate, rate, &_nk);
@@ -122,9 +122,9 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
 		rate = rate * nk->fixed_post_div;
 
-	_nk.min_n = 1;
+	_nk.min_n = nk->n.min;
 	_nk.max_n = 1 << nk->n.width;
-	_nk.min_k = 1;
+	_nk.min_k = nk->k.min;
 	_nk.max_k = 1 << nk->k.width;
 
 	ccu_nk_find_best(parent_rate, rate, &_nk);
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index b2a5fccf2f8c..715b49211ddb 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -100,9 +100,9 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
 	struct ccu_nkm *nkm = data;
 	struct _ccu_nkm _nkm;
 
-	_nkm.min_n = 1;
+	_nkm.min_n = nkm->n.min;
 	_nkm.max_n = 1 << nkm->n.width;
-	_nkm.min_k = 1;
+	_nkm.min_n = nkm->k.min;
 	_nkm.max_k = 1 << nkm->k.width;
 	_nkm.min_m = 1;
 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
@@ -129,9 +129,9 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
 	unsigned long flags;
 	u32 reg;
 
-	_nkm.min_n = 1;
+	_nkm.min_n = nkm->n.min;
 	_nkm.max_n = 1 << nkm->n.width;
-	_nkm.min_k = 1;
+	_nkm.min_n = nkm->k.min;
 	_nkm.max_k = 1 << nkm->k.width;
 	_nkm.min_m = 1;
 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index 2c1398192e48..7968e0bac5db 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -107,9 +107,9 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
 	struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
 	struct _ccu_nkmp _nkmp;
 
-	_nkmp.min_n = 1;
+	_nkmp.min_n = nkmp->n.min;
 	_nkmp.max_n = 1 << nkmp->n.width;
-	_nkmp.min_k = 1;
+	_nkmp.min_n = nkmp->k.min;
 	_nkmp.max_k = 1 << nkmp->k.width;
 	_nkmp.min_m = 1;
 	_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index 2a190bc032a9..b1f3f0e8899d 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -93,7 +93,7 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
 	struct _ccu_nm _nm;
 
-	_nm.min_n = 1;
+	_nm.min_n = nm->n.min;
 	_nm.max_n = 1 << nm->n.width;
 	_nm.min_m = 1;
 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 4/9] clk: sunxi-ng: Add minimums for all the relevant structures and clocks
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

Modify the current clocks we have to be able to specify the minimum for
each clocks we support, just like we support the max.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi-ng/ccu_mult.c |  7 ++++++-
 drivers/clk/sunxi-ng/ccu_nk.c   | 12 ++++++++----
 drivers/clk/sunxi-ng/ccu_nkm.c  | 18 ++++++++++++------
 drivers/clk/sunxi-ng/ccu_nkmp.c | 16 ++++++++++++----
 drivers/clk/sunxi-ng/ccu_nm.c   | 12 ++++++++----
 5 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
index 32a1964439a2..6a02ffee5386 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.c
+++ b/drivers/clk/sunxi-ng/ccu_mult.c
@@ -14,7 +14,7 @@
 #include "ccu_mult.h"
 
 struct _ccu_mult {
-	unsigned long	mult, max;
+	unsigned long	mult, min, max;
 };
 
 static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
@@ -23,6 +23,9 @@ static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
 	int _mult;
 
 	_mult = rate / parent;
+	if (_mult < mult->min)
+		_mult = mult->min;
+
 	if (_mult > mult->max)
 		_mult = mult->max;
 
@@ -37,6 +40,7 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
 	struct ccu_mult *cm = data;
 	struct _ccu_mult _cm;
 
+	_cm.min = 1;
 	_cm.max = 1 << cm->mult.width;
 	ccu_mult_find_best(parent_rate, rate, &_cm);
 
@@ -101,6 +105,7 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
 						&parent_rate);
 
+	_cm.min = 1;
 	_cm.max = 1 << cm->mult.width;
 	ccu_mult_find_best(parent_rate, rate, &_cm);
 
diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c
index e7e2e75618ef..a42d870ba0ef 100644
--- a/drivers/clk/sunxi-ng/ccu_nk.c
+++ b/drivers/clk/sunxi-ng/ccu_nk.c
@@ -14,8 +14,8 @@
 #include "ccu_nk.h"
 
 struct _ccu_nk {
-	unsigned long	n, max_n;
-	unsigned long	k, max_k;
+	unsigned long	n, min_n, max_n;
+	unsigned long	k, min_k, max_k;
 };
 
 static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
@@ -25,8 +25,8 @@ static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
 	unsigned int best_k = 0, best_n = 0;
 	unsigned int _k, _n;
 
-	for (_k = 1; _k <= nk->max_k; _k++) {
-		for (_n = 1; _n <= nk->max_n; _n++) {
+	for (_k = nk->min_k; _k <= nk->max_k; _k++) {
+		for (_n = nk->min_n; _n <= nk->max_n; _n++) {
 			unsigned long tmp_rate = parent * _n * _k;
 
 			if (tmp_rate > rate)
@@ -97,7 +97,9 @@ static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate,
 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
 		rate *= nk->fixed_post_div;
 
+	_nk.min_n = 1;
 	_nk.max_n = 1 << nk->n.width;
+	_nk.min_k = 1;
 	_nk.max_k = 1 << nk->k.width;
 
 	ccu_nk_find_best(*parent_rate, rate, &_nk);
@@ -120,7 +122,9 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
 		rate = rate * nk->fixed_post_div;
 
+	_nk.min_n = 1;
 	_nk.max_n = 1 << nk->n.width;
+	_nk.min_k = 1;
 	_nk.max_k = 1 << nk->k.width;
 
 	ccu_nk_find_best(parent_rate, rate, &_nk);
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index 0b08d000eb38..b2a5fccf2f8c 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -14,9 +14,9 @@
 #include "ccu_nkm.h"
 
 struct _ccu_nkm {
-	unsigned long	n, max_n;
-	unsigned long	k, max_k;
-	unsigned long	m, max_m;
+	unsigned long	n, min_n, max_n;
+	unsigned long	k, min_k, max_k;
+	unsigned long	m, min_m, max_m;
 };
 
 static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
@@ -26,9 +26,9 @@ static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
 	unsigned long best_n = 0, best_k = 0, best_m = 0;
 	unsigned long _n, _k, _m;
 
-	for (_k = 1; _k <= nkm->max_k; _k++) {
-		for (_n = 1; _n <= nkm->max_n; _n++) {
-			for (_m = 1; _n <= nkm->max_m; _m++) {
+	for (_k = nkm->min_k; _k <= nkm->max_k; _k++) {
+		for (_n = nkm->min_n; _n <= nkm->max_n; _n++) {
+			for (_m = nkm->min_m; _n <= nkm->max_m; _m++) {
 				unsigned long tmp_rate;
 
 				tmp_rate = parent * _n * _k / _m;
@@ -100,8 +100,11 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
 	struct ccu_nkm *nkm = data;
 	struct _ccu_nkm _nkm;
 
+	_nkm.min_n = 1;
 	_nkm.max_n = 1 << nkm->n.width;
+	_nkm.min_k = 1;
 	_nkm.max_k = 1 << nkm->k.width;
+	_nkm.min_m = 1;
 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
 
 	ccu_nkm_find_best(parent_rate, rate, &_nkm);
@@ -126,8 +129,11 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
 	unsigned long flags;
 	u32 reg;
 
+	_nkm.min_n = 1;
 	_nkm.max_n = 1 << nkm->n.width;
+	_nkm.min_k = 1;
 	_nkm.max_k = 1 << nkm->k.width;
+	_nkm.min_m = 1;
 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
 
 	ccu_nkm_find_best(parent_rate, rate, &_nkm);
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index 4b457d8cce11..2c1398192e48 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -27,10 +27,10 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
 	unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0;
 	unsigned long _n, _k, _m, _p;
 
-	for (_k = 1; _k <= nkmp->max_k; _k++) {
-		for (_n = 1; _n <= nkm->max_n; _n++) {
-			for (_m = 1; _n <= nkm->max_m; _m++) {
-				for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
+	for (_k = nkmp->min_k; _k <= nkmp->max_k; _k++) {
+		for (_n = nkmp->min_n; _n <= nkmp->max_n; _n++) {
+			for (_m = nkmp->min_m; _n <= nkmp->max_m; _m++) {
+				for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) {
 					unsigned long tmp_rate;
 
 					tmp_rate = parent * _n * _k / (_m * _p);
@@ -107,9 +107,13 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
 	struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
 	struct _ccu_nkmp _nkmp;
 
+	_nkmp.min_n = 1;
 	_nkmp.max_n = 1 << nkmp->n.width;
+	_nkmp.min_k = 1;
 	_nkmp.max_k = 1 << nkmp->k.width;
+	_nkmp.min_m = 1;
 	_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
+	_nkmp.min_p = 1;
 	_nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
 
 	ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
@@ -125,9 +129,13 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
 	unsigned long flags;
 	u32 reg;
 
+	_nkmp.min_n = 1;
 	_nkmp.max_n = 1 << nkmp->n.width;
+	_nkmp.min_k = 1;
 	_nkmp.max_k = 1 << nkmp->k.width;
+	_nkmp.min_m = 1;
 	_nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
+	_nkmp.min_p = 1;
 	_nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
 
 	ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index c6d652289320..2a190bc032a9 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -15,8 +15,8 @@
 #include "ccu_nm.h"
 
 struct _ccu_nm {
-	unsigned long	n, max_n;
-	unsigned long	m, max_m;
+	unsigned long	n, min_n, max_n;
+	unsigned long	m, min_m, max_m;
 };
 
 static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
@@ -26,8 +26,8 @@ static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
 	unsigned long best_n = 0, best_m = 0;
 	unsigned long _n, _m;
 
-	for (_n = 1; _n <= nm->max_n; _n++) {
-		for (_m = 1; _n <= nm->max_m; _m++) {
+	for (_n = nm->min_n; _n <= nm->max_n; _n++) {
+		for (_m = nm->min_m; _n <= nm->max_m; _m++) {
 			unsigned long tmp_rate = parent * _n  / _m;
 
 			if (tmp_rate > rate)
@@ -93,7 +93,9 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
 	struct _ccu_nm _nm;
 
+	_nm.min_n = 1;
 	_nm.max_n = 1 << nm->n.width;
+	_nm.min_m = 1;
 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
 
 	ccu_nm_find_best(*parent_rate, rate, &_nm);
@@ -114,7 +116,9 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
 	else
 		ccu_frac_helper_disable(&nm->common, &nm->frac);
 
+	_nm.min_n = 1;
 	_nm.max_n = 1 << nm->n.width;
+	_nm.min_m = 1;
 	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
 
 	ccu_nm_find_best(parent_rate, rate, &_nm);
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 3/9] clk: sunxi-ng: Finish to convert to structures for arguments
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

Some clocks still use an explicit list of arguments, which make it a bit
more tedious to add new parameters.

Convert those over to a structure pointer argument to add as many
arguments as possible without having to many noise in our patches, or a
very long list of arguments.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi-ng/ccu_mult.c | 28 +++++++++++++++++-------
 drivers/clk/sunxi-ng/ccu_nk.c   | 39 +++++++++++++++++++---------------
 2 files changed, 42 insertions(+), 25 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
index 010e9424691d..32a1964439a2 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.c
+++ b/drivers/clk/sunxi-ng/ccu_mult.c
@@ -13,10 +13,20 @@
 #include "ccu_gate.h"
 #include "ccu_mult.h"
 
+struct _ccu_mult {
+	unsigned long	mult, max;
+};
+
 static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
-			       unsigned int max_n, unsigned int *n)
+			       struct _ccu_mult *mult)
 {
-	*n = rate / parent;
+	int _mult;
+
+	_mult = rate / parent;
+	if (_mult > mult->max)
+		_mult = mult->max;
+
+	mult->mult = _mult;
 }
 
 static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
@@ -25,11 +35,12 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
 					void *data)
 {
 	struct ccu_mult *cm = data;
-	unsigned int n;
+	struct _ccu_mult _cm;
 
-	ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
+	_cm.max = 1 << cm->mult.width;
+	ccu_mult_find_best(parent_rate, rate, &_cm);
 
-	return parent_rate * n;
+	return parent_rate * _cm.mult;
 }
 
 static void ccu_mult_disable(struct clk_hw *hw)
@@ -83,21 +94,22 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
 			   unsigned long parent_rate)
 {
 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
+	struct _ccu_mult _cm;
 	unsigned long flags;
-	unsigned int n;
 	u32 reg;
 
 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
 						&parent_rate);
 
-	ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
+	_cm.max = 1 << cm->mult.width;
+	ccu_mult_find_best(parent_rate, rate, &_cm);
 
 	spin_lock_irqsave(cm->common.lock, flags);
 
 	reg = readl(cm->common.base + cm->common.reg);
 	reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
 
-	writel(reg | ((n - 1) << cm->mult.shift),
+	writel(reg | ((_cm.mult - 1) << cm->mult.shift),
 	       cm->common.base + cm->common.reg);
 
 	spin_unlock_irqrestore(cm->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c
index d6fafb397489..e7e2e75618ef 100644
--- a/drivers/clk/sunxi-ng/ccu_nk.c
+++ b/drivers/clk/sunxi-ng/ccu_nk.c
@@ -9,21 +9,24 @@
  */
 
 #include <linux/clk-provider.h>
-#include <linux/rational.h>
 
 #include "ccu_gate.h"
 #include "ccu_nk.h"
 
+struct _ccu_nk {
+	unsigned long	n, max_n;
+	unsigned long	k, max_k;
+};
+
 static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
-			     unsigned int max_n, unsigned int max_k,
-			     unsigned int *n, unsigned int *k)
+			     struct _ccu_nk *nk)
 {
 	unsigned long best_rate = 0;
 	unsigned int best_k = 0, best_n = 0;
 	unsigned int _k, _n;
 
-	for (_k = 1; _k <= max_k; _k++) {
-		for (_n = 1; _n <= max_n; _n++) {
+	for (_k = 1; _k <= nk->max_k; _k++) {
+		for (_n = 1; _n <= nk->max_n; _n++) {
 			unsigned long tmp_rate = parent * _n * _k;
 
 			if (tmp_rate > rate)
@@ -37,8 +40,8 @@ static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
 		}
 	}
 
-	*k = best_k;
-	*n = best_n;
+	nk->k = best_k;
+	nk->n = best_n;
 }
 
 static void ccu_nk_disable(struct clk_hw *hw)
@@ -89,16 +92,17 @@ static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate,
 			      unsigned long *parent_rate)
 {
 	struct ccu_nk *nk = hw_to_ccu_nk(hw);
-	unsigned int n, k;
+	struct _ccu_nk _nk;
 
 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
 		rate *= nk->fixed_post_div;
 
-	ccu_nk_find_best(*parent_rate, rate,
-			 1 << nk->n.width, 1 << nk->k.width,
-			 &n, &k);
+	_nk.max_n = 1 << nk->n.width;
+	_nk.max_k = 1 << nk->k.width;
+
+	ccu_nk_find_best(*parent_rate, rate, &_nk);
+	rate = *parent_rate * _nk.n * _nk.k;
 
-	rate = *parent_rate * n * k;
 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
 		rate = rate / nk->fixed_post_div;
 
@@ -110,15 +114,16 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct ccu_nk *nk = hw_to_ccu_nk(hw);
 	unsigned long flags;
-	unsigned int n, k;
+	struct _ccu_nk _nk;
 	u32 reg;
 
 	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
 		rate = rate * nk->fixed_post_div;
 
-	ccu_nk_find_best(parent_rate, rate,
-			 1 << nk->n.width, 1 << nk->k.width,
-			 &n, &k);
+	_nk.max_n = 1 << nk->n.width;
+	_nk.max_k = 1 << nk->k.width;
+
+	ccu_nk_find_best(parent_rate, rate, &_nk);
 
 	spin_lock_irqsave(nk->common.lock, flags);
 
@@ -126,7 +131,7 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
 	reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift);
 	reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift);
 
-	writel(reg | ((k - 1) << nk->k.shift) | ((n - 1) << nk->n.shift),
+	writel(reg | ((_nk.k - 1) << nk->k.shift) | ((_nk.n - 1) << nk->n.shift),
 	       nk->common.base + nk->common.reg);
 
 	spin_unlock_irqrestore(nk->common.lock, flags);
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 2/9] clk: sunxi-ng: Remove the use of rational computations
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

While the rational library works great, it doesn't really allow us to add
more constraints, like the minimum.

Remove that in order to be able to deal with the constraints we'll need.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi-ng/Kconfig    |  3 +--
 drivers/clk/sunxi-ng/ccu_nkm.c  | 31 +++++++++-----------
 drivers/clk/sunxi-ng/ccu_nkmp.c | 45 +++++++++++++---------------
 drivers/clk/sunxi-ng/ccu_nm.c   | 54 +++++++++++++++++++++++++---------
 4 files changed, 78 insertions(+), 55 deletions(-)

diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 254d9526c018..1b4c55a53d7a 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -35,17 +35,14 @@ config SUNXI_CCU_NK
 
 config SUNXI_CCU_NKM
 	bool
-	select RATIONAL
 	select SUNXI_CCU_GATE
 
 config SUNXI_CCU_NKMP
 	bool
-	select RATIONAL
 	select SUNXI_CCU_GATE
 
 config SUNXI_CCU_NM
 	bool
-	select RATIONAL
 	select SUNXI_CCU_FRAC
 	select SUNXI_CCU_GATE
 
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index 059fdc3b4f96..0b08d000eb38 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -9,7 +9,6 @@
  */
 
 #include <linux/clk-provider.h>
-#include <linux/rational.h>
 
 #include "ccu_gate.h"
 #include "ccu_nkm.h"
@@ -28,21 +27,21 @@ static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
 	unsigned long _n, _k, _m;
 
 	for (_k = 1; _k <= nkm->max_k; _k++) {
-		unsigned long tmp_rate;
-
-		rational_best_approximation(rate / _k, parent,
-					    nkm->max_n, nkm->max_m, &_n, &_m);
-
-		tmp_rate = parent * _n * _k / _m;
-
-		if (tmp_rate > rate)
-			continue;
-
-		if ((rate - tmp_rate) < (rate - best_rate)) {
-			best_rate = tmp_rate;
-			best_n = _n;
-			best_k = _k;
-			best_m = _m;
+		for (_n = 1; _n <= nkm->max_n; _n++) {
+			for (_m = 1; _n <= nkm->max_m; _m++) {
+				unsigned long tmp_rate;
+
+				tmp_rate = parent * _n * _k / _m;
+
+				if (tmp_rate > rate)
+					continue;
+				if ((rate - tmp_rate) < (rate - best_rate)) {
+					best_rate = tmp_rate;
+					best_n = _n;
+					best_k = _k;
+					best_m = _m;
+				}
+			}
 		}
 	}
 
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index 9769dee99511..4b457d8cce11 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -9,16 +9,15 @@
  */
 
 #include <linux/clk-provider.h>
-#include <linux/rational.h>
 
 #include "ccu_gate.h"
 #include "ccu_nkmp.h"
 
 struct _ccu_nkmp {
-	unsigned long	n, max_n;
-	unsigned long	k, max_k;
-	unsigned long	m, max_m;
-	unsigned long	p, max_p;
+	unsigned long	n, min_n, max_n;
+	unsigned long	k, min_k, max_k;
+	unsigned long	m, min_m, max_m;
+	unsigned long	p, min_p, max_p;
 };
 
 static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
@@ -29,24 +28,24 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
 	unsigned long _n, _k, _m, _p;
 
 	for (_k = 1; _k <= nkmp->max_k; _k++) {
-		for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
-			unsigned long tmp_rate;
-
-			rational_best_approximation(rate / _k, parent / _p,
-						    nkmp->max_n, nkmp->max_m,
-						    &_n, &_m);
-
-			tmp_rate = parent * _n * _k / (_m * _p);
-
-			if (tmp_rate > rate)
-				continue;
-
-			if ((rate - tmp_rate) < (rate - best_rate)) {
-				best_rate = tmp_rate;
-				best_n = _n;
-				best_k = _k;
-				best_m = _m;
-				best_p = _p;
+		for (_n = 1; _n <= nkm->max_n; _n++) {
+			for (_m = 1; _n <= nkm->max_m; _m++) {
+				for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
+					unsigned long tmp_rate;
+
+					tmp_rate = parent * _n * _k / (_m * _p);
+
+					if (tmp_rate > rate)
+						continue;
+
+					if ((rate - tmp_rate) < (rate - best_rate)) {
+						best_rate = tmp_rate;
+						best_n = _n;
+						best_k = _k;
+						best_m = _m;
+						best_p = _p;
+					}
+				}
 			}
 		}
 	}
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index b61bdd8c7a7f..c6d652289320 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -9,12 +9,42 @@
  */
 
 #include <linux/clk-provider.h>
-#include <linux/rational.h>
 
 #include "ccu_frac.h"
 #include "ccu_gate.h"
 #include "ccu_nm.h"
 
+struct _ccu_nm {
+	unsigned long	n, max_n;
+	unsigned long	m, max_m;
+};
+
+static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
+			     struct _ccu_nm *nm)
+{
+	unsigned long best_rate = 0;
+	unsigned long best_n = 0, best_m = 0;
+	unsigned long _n, _m;
+
+	for (_n = 1; _n <= nm->max_n; _n++) {
+		for (_m = 1; _n <= nm->max_m; _m++) {
+			unsigned long tmp_rate = parent * _n  / _m;
+
+			if (tmp_rate > rate)
+				continue;
+
+			if ((rate - tmp_rate) < (rate - best_rate)) {
+				best_rate = tmp_rate;
+				best_n = _n;
+				best_m = _m;
+			}
+		}
+	}
+
+	nm->n = best_n;
+	nm->m = best_m;
+}
+
 static void ccu_nm_disable(struct clk_hw *hw)
 {
 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
@@ -61,24 +91,22 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
 			      unsigned long *parent_rate)
 {
 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
-	unsigned long max_n, max_m;
-	unsigned long n, m;
+	struct _ccu_nm _nm;
 
-	max_n = 1 << nm->n.width;
-	max_m = nm->m.max ?: 1 << nm->m.width;
+	_nm.max_n = 1 << nm->n.width;
+	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
 
-	rational_best_approximation(rate, *parent_rate, max_n, max_m, &n, &m);
+	ccu_nm_find_best(*parent_rate, rate, &_nm);
 
-	return *parent_rate * n / m;
+	return *parent_rate * _nm.n / _nm.m;
 }
 
 static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
 			   unsigned long parent_rate)
 {
 	struct ccu_nm *nm = hw_to_ccu_nm(hw);
+	struct _ccu_nm _nm;
 	unsigned long flags;
-	unsigned long max_n, max_m;
-	unsigned long n, m;
 	u32 reg;
 
 	if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate))
@@ -86,10 +114,10 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
 	else
 		ccu_frac_helper_disable(&nm->common, &nm->frac);
 
-	max_n = 1 << nm->n.width;
-	max_m = nm->m.max ?: 1 << nm->m.width;
+	_nm.max_n = 1 << nm->n.width;
+	_nm.max_m = nm->m.max ?: 1 << nm->m.width;
 
-	rational_best_approximation(rate, parent_rate, max_n, max_m, &n, &m);
+	ccu_nm_find_best(parent_rate, rate, &_nm);
 
 	spin_lock_irqsave(nm->common.lock, flags);
 
@@ -97,7 +125,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
 	reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift);
 	reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift);
 
-	writel(reg | ((m - 1) << nm->m.shift) | ((n - 1) << nm->n.shift),
+	writel(reg | ((_nm.m - 1) << nm->m.shift) | ((_nm.n - 1) << nm->n.shift),
 	       nm->common.base + nm->common.reg);
 
 	spin_unlock_irqrestore(nm->common.lock, flags);
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 1/9] clk: sunxi-ng: Rename the internal structures
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.024e1b023e41efe548dc6a004f7a946408d5c9a2.1476196031.git-series.maxime.ripard@free-electrons.com>

Rename the structures meant to be embedded in other structures to make it
consistent with the mux structure name

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/sunxi-ng/ccu_div.h  |  6 +++---
 drivers/clk/sunxi-ng/ccu_frac.c | 12 ++++++------
 drivers/clk/sunxi-ng/ccu_frac.h | 14 +++++++-------
 drivers/clk/sunxi-ng/ccu_mp.h   |  4 ++--
 drivers/clk/sunxi-ng/ccu_mult.h |  4 ++--
 drivers/clk/sunxi-ng/ccu_nk.h   |  4 ++--
 drivers/clk/sunxi-ng/ccu_nkm.h  |  6 +++---
 drivers/clk/sunxi-ng/ccu_nkmp.h |  8 ++++----
 drivers/clk/sunxi-ng/ccu_nm.h   |  6 +++---
 9 files changed, 32 insertions(+), 32 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
index 34c338832c0d..06540f7cf41c 100644
--- a/drivers/clk/sunxi-ng/ccu_div.h
+++ b/drivers/clk/sunxi-ng/ccu_div.h
@@ -20,7 +20,7 @@
 #include "ccu_mux.h"
 
 /**
- * struct _ccu_div - Internal divider description
+ * struct ccu_div_internal - Internal divider description
  * @shift: Bit offset of the divider in its register
  * @width: Width of the divider field in its register
  * @max: Maximum value allowed for that divider. This is the
@@ -36,7 +36,7 @@
  * It is basically a wrapper around the clk_divider functions
  * arguments.
  */
-struct _ccu_div {
+struct ccu_div_internal {
 	u8			shift;
 	u8			width;
 
@@ -78,7 +78,7 @@ struct _ccu_div {
 struct ccu_div {
 	u32			enable;
 
-	struct _ccu_div		div;
+	struct ccu_div_internal		div;
 	struct ccu_mux_internal	mux;
 	struct ccu_common	common;
 };
diff --git a/drivers/clk/sunxi-ng/ccu_frac.c b/drivers/clk/sunxi-ng/ccu_frac.c
index 5c4b10cd15b5..8b5eb7756bf7 100644
--- a/drivers/clk/sunxi-ng/ccu_frac.c
+++ b/drivers/clk/sunxi-ng/ccu_frac.c
@@ -14,7 +14,7 @@
 #include "ccu_frac.h"
 
 bool ccu_frac_helper_is_enabled(struct ccu_common *common,
-				struct _ccu_frac *cf)
+				struct ccu_frac_internal *cf)
 {
 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
 		return false;
@@ -23,7 +23,7 @@ bool ccu_frac_helper_is_enabled(struct ccu_common *common,
 }
 
 void ccu_frac_helper_enable(struct ccu_common *common,
-			    struct _ccu_frac *cf)
+			    struct ccu_frac_internal *cf)
 {
 	unsigned long flags;
 	u32 reg;
@@ -38,7 +38,7 @@ void ccu_frac_helper_enable(struct ccu_common *common,
 }
 
 void ccu_frac_helper_disable(struct ccu_common *common,
-			     struct _ccu_frac *cf)
+			     struct ccu_frac_internal *cf)
 {
 	unsigned long flags;
 	u32 reg;
@@ -53,7 +53,7 @@ void ccu_frac_helper_disable(struct ccu_common *common,
 }
 
 bool ccu_frac_helper_has_rate(struct ccu_common *common,
-			      struct _ccu_frac *cf,
+			      struct ccu_frac_internal *cf,
 			      unsigned long rate)
 {
 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
@@ -63,7 +63,7 @@ bool ccu_frac_helper_has_rate(struct ccu_common *common,
 }
 
 unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
-					struct _ccu_frac *cf)
+					struct ccu_frac_internal *cf)
 {
 	u32 reg;
 
@@ -84,7 +84,7 @@ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
 }
 
 int ccu_frac_helper_set_rate(struct ccu_common *common,
-			     struct _ccu_frac *cf,
+			     struct ccu_frac_internal *cf,
 			     unsigned long rate)
 {
 	unsigned long flags;
diff --git a/drivers/clk/sunxi-ng/ccu_frac.h b/drivers/clk/sunxi-ng/ccu_frac.h
index e4c670b1cdfe..7b1ee380156f 100644
--- a/drivers/clk/sunxi-ng/ccu_frac.h
+++ b/drivers/clk/sunxi-ng/ccu_frac.h
@@ -18,7 +18,7 @@
 
 #include "ccu_common.h"
 
-struct _ccu_frac {
+struct ccu_frac_internal {
 	u32		enable;
 	u32		select;
 
@@ -33,21 +33,21 @@ struct _ccu_frac {
 	}
 
 bool ccu_frac_helper_is_enabled(struct ccu_common *common,
-				struct _ccu_frac *cf);
+				struct ccu_frac_internal *cf);
 void ccu_frac_helper_enable(struct ccu_common *common,
-			    struct _ccu_frac *cf);
+			    struct ccu_frac_internal *cf);
 void ccu_frac_helper_disable(struct ccu_common *common,
-			     struct _ccu_frac *cf);
+			     struct ccu_frac_internal *cf);
 
 bool ccu_frac_helper_has_rate(struct ccu_common *common,
-			      struct _ccu_frac *cf,
+			      struct ccu_frac_internal *cf,
 			      unsigned long rate);
 
 unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
-					struct _ccu_frac *cf);
+					struct ccu_frac_internal *cf);
 
 int ccu_frac_helper_set_rate(struct ccu_common *common,
-			     struct _ccu_frac *cf,
+			     struct ccu_frac_internal *cf,
 			     unsigned long rate);
 
 #endif /* _CCU_FRAC_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h
index edf9215ea8cc..915625e97d98 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.h
+++ b/drivers/clk/sunxi-ng/ccu_mp.h
@@ -29,8 +29,8 @@
 struct ccu_mp {
 	u32			enable;
 
-	struct _ccu_div		m;
-	struct _ccu_div		p;
+	struct ccu_div_internal		m;
+	struct ccu_div_internal		p;
 	struct ccu_mux_internal	mux;
 	struct ccu_common	common;
 };
diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h
index 5d2c8dc14073..113780b7558e 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.h
+++ b/drivers/clk/sunxi-ng/ccu_mult.h
@@ -4,7 +4,7 @@
 #include "ccu_common.h"
 #include "ccu_mux.h"
 
-struct _ccu_mult {
+struct ccu_mult_internal {
 	u8	shift;
 	u8	width;
 };
@@ -18,7 +18,7 @@ struct _ccu_mult {
 struct ccu_mult {
 	u32			enable;
 
-	struct _ccu_mult	mult;
+	struct ccu_mult_internal	mult;
 	struct ccu_mux_internal	mux;
 	struct ccu_common	common;
 };
diff --git a/drivers/clk/sunxi-ng/ccu_nk.h b/drivers/clk/sunxi-ng/ccu_nk.h
index 4b52da0c29fe..437836b80696 100644
--- a/drivers/clk/sunxi-ng/ccu_nk.h
+++ b/drivers/clk/sunxi-ng/ccu_nk.h
@@ -30,8 +30,8 @@ struct ccu_nk {
 	u32			enable;
 	u32			lock;
 
-	struct _ccu_mult	n;
-	struct _ccu_mult	k;
+	struct ccu_mult_internal	n;
+	struct ccu_mult_internal	k;
 
 	unsigned int		fixed_post_div;
 
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.h b/drivers/clk/sunxi-ng/ccu_nkm.h
index 35493fddd8ab..34580894f4d1 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.h
+++ b/drivers/clk/sunxi-ng/ccu_nkm.h
@@ -29,9 +29,9 @@ struct ccu_nkm {
 	u32			enable;
 	u32			lock;
 
-	struct _ccu_mult	n;
-	struct _ccu_mult	k;
-	struct _ccu_div		m;
+	struct ccu_mult_internal	n;
+	struct ccu_mult_internal	k;
+	struct ccu_div_internal		m;
 	struct ccu_mux_internal	mux;
 
 	struct ccu_common	common;
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.h b/drivers/clk/sunxi-ng/ccu_nkmp.h
index 5adb0c92a614..a82facbc6144 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.h
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.h
@@ -29,10 +29,10 @@ struct ccu_nkmp {
 	u32			enable;
 	u32			lock;
 
-	struct _ccu_mult	n;
-	struct _ccu_mult	k;
-	struct _ccu_div		m;
-	struct _ccu_div		p;
+	struct ccu_mult_internal	n;
+	struct ccu_mult_internal	k;
+	struct ccu_div_internal		m;
+	struct ccu_div_internal		p;
 
 	struct ccu_common	common;
 };
diff --git a/drivers/clk/sunxi-ng/ccu_nm.h b/drivers/clk/sunxi-ng/ccu_nm.h
index 0b7bcd33a2df..e87fd186da78 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.h
+++ b/drivers/clk/sunxi-ng/ccu_nm.h
@@ -30,9 +30,9 @@ struct ccu_nm {
 	u32			enable;
 	u32			lock;
 
-	struct _ccu_mult	n;
-	struct _ccu_div		m;
-	struct _ccu_frac	frac;
+	struct ccu_mult_internal	n;
+	struct ccu_div_internal		m;
+	struct ccu_frac_internal	frac;
 
 	struct ccu_common	common;
 };
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v4 0/9] arm64: Allwinner A64 support based on sunxi-ng
From: Maxime Ripard @ 2016-10-11 14:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

As it was in the first iteration, this is the A64 support based on the
new sunxi-ng clock framework.

The support for it is quite minimal at the moment, but it should be
fairly easy to add new devices, as most of the design is shared with
older SoCs.

Let me know what you think,
Maxime

Changes from v3:
  - Fix patch split

Changes from v2:
  - Added pull-ups on the Pine64 i2c bus
  - Removed the PMU since it doesn't work
  - Refactored the sunxi-ng framework to deal with the specifities of
    the A64 CCU, especially in terms of minimum factors.
  - Fixed a few things in the CCU driver: added CLK_SET_RATE PARENT
    flags, fixed some mux width, etc.
  - Converted the CCU driver to a platform driver
  - Added the DRAM reset line
  - Added IDs for the USB muxes (even though we're not using them yet)

Changes from v1:
  - Split the A64 CCU support out of the H3 driver
  - Added the PMU support
  - Removed the clocks node
  - Rebased on top of current sunxi/clk-for-4.9 branch

Andre Przywara (3):
  arm64: dts: add Allwinner A64 SoC .dtsi
  Documentation: devicetree: add vendor prefix for Pine64
  arm64: dts: add Pine64 support

Maxime Ripard (6):
  clk: sunxi-ng: Rename the internal structures
  clk: sunxi-ng: Remove the use of rational computations
  clk: sunxi-ng: Finish to convert to structures for arguments
  clk: sunxi-ng: Add minimums for all the relevant structures and clocks
  clk: sunxi-ng: Implement minimum for multipliers
  clk: sunxi-ng: Add A64 clocks

 Documentation/devicetree/bindings/arm/sunxi.txt          |   1 +-
 Documentation/devicetree/bindings/clock/sunxi-ccu.txt    |   1 +-
 Documentation/devicetree/bindings/vendor-prefixes.txt    |   1 +-
 MAINTAINERS                                              |   1 +-
 arch/arm64/boot/dts/Makefile                             |   1 +-
 arch/arm64/boot/dts/allwinner/Makefile                   |   5 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts |  50 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts      |  74 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi            | 263 ++-
 drivers/clk/sunxi-ng/Kconfig                             |  14 +-
 drivers/clk/sunxi-ng/Makefile                            |   1 +-
 drivers/clk/sunxi-ng/ccu-sun50i-a64.c                    | 918 ++++++++-
 drivers/clk/sunxi-ng/ccu-sun50i-a64.h                    |  72 +-
 drivers/clk/sunxi-ng/ccu_div.h                           |   6 +-
 drivers/clk/sunxi-ng/ccu_frac.c                          |  12 +-
 drivers/clk/sunxi-ng/ccu_frac.h                          |  14 +-
 drivers/clk/sunxi-ng/ccu_mp.h                            |   4 +-
 drivers/clk/sunxi-ng/ccu_mult.c                          |  33 +-
 drivers/clk/sunxi-ng/ccu_mult.h                          |  17 +-
 drivers/clk/sunxi-ng/ccu_nk.c                            |  43 +-
 drivers/clk/sunxi-ng/ccu_nk.h                            |   4 +-
 drivers/clk/sunxi-ng/ccu_nkm.c                           |  45 +-
 drivers/clk/sunxi-ng/ccu_nkm.h                           |   6 +-
 drivers/clk/sunxi-ng/ccu_nkmp.c                          |  55 +-
 drivers/clk/sunxi-ng/ccu_nkmp.h                          |   8 +-
 drivers/clk/sunxi-ng/ccu_nm.c                            |  58 +-
 drivers/clk/sunxi-ng/ccu_nm.h                            |   6 +-
 include/dt-bindings/clock/sun50i-a64-ccu.h               | 134 +-
 include/dt-bindings/reset/sun50i-a64-ccu.h               |  98 +-
 29 files changed, 1824 insertions(+), 121 deletions(-)
 create mode 100644 arch/arm64/boot/dts/allwinner/Makefile
 create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts
 create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
 create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-a64.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-a64.h
 create mode 100644 include/dt-bindings/clock/sun50i-a64-ccu.h
 create mode 100644 include/dt-bindings/reset/sun50i-a64-ccu.h

-- 
git-series 0.8.10

^ permalink raw reply

* [PATCH v3 07/11] arm64/tracing: fix compat syscall handling
From: Will Deacon @ 2016-10-11 13:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476182576-15247-8-git-send-email-marcin.nowakowski@imgtec.com>

On Tue, Oct 11, 2016 at 12:42:52PM +0200, Marcin Nowakowski wrote:
> Add arch_syscall_addr for arm64 and define NR_compat_syscalls, as the
> number of compat syscalls for arm64 exceeds the number defined by
> NR_syscalls.
> 
> Signed-off-by: Marcin Nowakowski <marcin.nowakowski@imgtec.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: linux-arm-kernel at lists.infradead.org
> ---
>  arch/arm64/include/asm/ftrace.h | 12 +-----------
>  arch/arm64/include/asm/unistd.h |  1 +
>  arch/arm64/kernel/Makefile      |  1 +
>  arch/arm64/kernel/ftrace.c      | 16 ++++++++++++++++
>  4 files changed, 19 insertions(+), 11 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
> index caa955f..b57ff7c 100644
> --- a/arch/arm64/include/asm/ftrace.h
> +++ b/arch/arm64/include/asm/ftrace.h
> @@ -41,17 +41,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
>  
>  #define ftrace_return_address(n) return_address(n)
>  
> -/*
> - * Because AArch32 mode does not share the same syscall table with AArch64,
> - * tracing compat syscalls may result in reporting bogus syscalls or even
> - * hang-up, so just do not trace them.
> - * See kernel/trace/trace_syscalls.c
> - *
> - * x86 code says:
> - * If the user really wants these, then they should use the
> - * raw syscall tracepoints with filtering.
> - */
> -#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS
> +#define ARCH_COMPAT_SYSCALL_NUMBERS_OVERLAP 1
>  static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
>  {
>  	return is_compat_task();
> diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h
> index e78ac26..276d049 100644
> --- a/arch/arm64/include/asm/unistd.h
> +++ b/arch/arm64/include/asm/unistd.h
> @@ -45,6 +45,7 @@
>  #define __ARM_NR_compat_set_tls		(__ARM_NR_COMPAT_BASE+5)
>  
>  #define __NR_compat_syscalls		394
> +#define NR_compat_syscalls (__NR_compat_syscalls)

We may as well just define NR_compat_syscalls instead of
__NR_compat_syscalls and move the handful of users over.

> diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> index 40ad08a..75d010f 100644
> --- a/arch/arm64/kernel/ftrace.c
> +++ b/arch/arm64/kernel/ftrace.c
> @@ -176,4 +176,20 @@ int ftrace_disable_ftrace_graph_caller(void)
>  	return ftrace_modify_graph_caller(false);
>  }
>  #endif /* CONFIG_DYNAMIC_FTRACE */
> +
>  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
> +
> +#if (defined CONFIG_FTRACE_SYSCALLS) && (defined CONFIG_COMPAT)
> +
> +extern const void *sys_call_table[];
> +extern const void *compat_sys_call_table[];
> +
> +unsigned long __init arch_syscall_addr(int nr, bool compat)
> +{
> +	if (compat)
> +		return (unsigned long)compat_sys_call_table[nr];
> +
> +	return (unsigned long)sys_call_table[nr];
> +}

Do we care about the compat private syscalls (from base 0x0f0000)? We
need to make sure that we exhibit the same behaviour as a native
32-bit ARM machine.

Will

^ permalink raw reply

* [PATCH] arm64: mmu: set the contiguous for kernel mappings when appropriate
From: Ard Biesheuvel @ 2016-10-11 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161011124118.GB9532@arm.com>

On 11 October 2016 at 13:41, Will Deacon <will.deacon@arm.com> wrote:
> On Tue, Oct 11, 2016 at 12:17:54PM +0100, Ard Biesheuvel wrote:
>> On 11 October 2016 at 10:09, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>> > On 11 October 2016 at 09:48, Steve Capper <steve.capper@linaro.org> wrote:
>> >> So in arch/arm64/include/asm/pgtable-hwdef.h, we have:
>> >> CONT_PTE_SHIFT
>> >> CONT_PMD_SHIFT
>> >> CONT_PTES
>> >> CONT_PMDS
>> >> CONT_PTE_SIZE
>> >> CONT_PTE_MASK
>> >> ...
>> >>
>> >> which are used by the contiguous hint HugeTLB code.
>> >> Can those be adopted instead of CONT_MASK and CONT_SIZE?
>> >>
>>
>> Looking at the hugetlb code, it appears to support contiguous PMDs for
>> 4k and 64k pages as well, while the ARM ARM only defines it for 16k
>> pages. I suppose the contiguous bit is simply ignored for level 2
>> entries when using 4k or 64k pages kernels, but I think it would be
>> better for the code to reflect this as well.
>
> Which bit in the ARM ARM says that you can't support contiguous PMDs for 4k
> and 64k pages? I see that the number of contiguous entries changes between
> levels for 16k pages, but that's it.
>

You are right, the ARM ARM does not say that at all. But given Mark's comment:

"""
With 16K pages, we can have contiguous PMD entries. Should we handle those,
too? e.g. have separate {PMD,PTE}_CONT{,_SIZE}?
"""

it seems I am not the only one who is confused about this. In any
case, the fact that the ARM ARM documents levels 2 and 3 explicitly
for 16k pages does very little to clarify at which levels this bit is
defined, and if it is defined at levels < 2, what the granularity is
for 16k pages.

So the v2 I just sent out could be modified to allow contiguous PMDs
(32 MB blocks) on 4 KB kernels, which seems useful. I will take that
into account when I prepare the v3.

-- 
Ard.

^ permalink raw reply

* [PATCH v3 9/9] arm64: dts: add Pine64 support
From: Maxime Ripard @ 2016-10-11 12:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <7483f6ea-3b59-46ca-fb31-f26af6ec8423@arm.com>

On Mon, Oct 03, 2016 at 11:24:24AM +0100, Andre Przywara wrote:
> Hi Maxime,
> 
> thanks for the respin!
> 
> On 03/10/16 09:09, Maxime Ripard wrote:
> > From: Andre Przywara <andre.przywara@arm.com>
> > 
> > The Pine64 is a cost-efficient development board based on the
> > Allwinner A64 SoC.
> > There are three models: the basic version with Fast Ethernet and
> > 512 MB of DRAM (Pine64) and two Pine64+ versions, which both
> > feature Gigabit Ethernet and additional connectors for touchscreens
> > and a camera. Or as my son put it: "Those are smaller and these are
> > missing." ;-)
> > The two Pine64+ models just differ in the amount of DRAM
> > (1GB vs. 2GB). Since U-Boot will figure out the right size for us and
> > patches the DT accordingly we just need to provide one DT for the
> > Pine64+.
> > 
> > Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> > [Maxime: Removed the common DTSI and include directly the pine64 DTS]
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> >  arch/arm64/boot/dts/Makefile                       |  1 +
> >  arch/arm64/boot/dts/allwinner/Makefile             |  5 ++
> >  .../boot/dts/allwinner/sun50i-a64-pine64-plus.dts  | 50 +++++++++++
> >  .../arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 74 +++++++++++++++++
> >  include/dt-bindings/reset/sun50i-a64-ccu.h         | 97 +++++++++++-----------
> 
> Shouldn't the changes in this file be merged into patch 6/9?

Oops, yeah, of course.

> > diff --git a/include/dt-bindings/reset/sun50i-a64-ccu.h b/include/dt-bindings/reset/sun50i-a64-ccu.h
> > index e61fac294d73..db60b29ddb11 100644
> > --- a/include/dt-bindings/reset/sun50i-a64-ccu.h
> > +++ b/include/dt-bindings/reset/sun50i-a64-ccu.h
> > @@ -46,52 +46,53 @@
> >  #define RST_USB_PHY0		0
> >  #define RST_USB_PHY1		1
> >  #define RST_USB_HSIC		2
> > -#define RST_MBUS		3
> ....
> > +#define RST_DRAM		3
> > +#define RST_MBUS		4
> 
> So I take it that this kind of changes will not happen anymore once the
> DT has been merged?
> And this numbering is arbitrary and not connected to some hardware
> bits/register addresses at all?
> And in case we find some missing bits later we will just queue them at
> the end?

Your constant reminding of that on all the patches is getting
old. Yes, this is what we agreed on two releases ago. And since
there's already some very real bugs that can't be fixed because of
that, there's really nothing to be proud of.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161011/c48fcec8/attachment.sig>

^ permalink raw reply

* [PATCH] arm64: mmu: set the contiguous for kernel mappings when appropriate
From: Will Deacon @ 2016-10-11 12:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAKv+Gu9W69rP5o09t+d498nVE666Show1VK-Sm9LmchjeUsD_A@mail.gmail.com>

On Tue, Oct 11, 2016 at 12:17:54PM +0100, Ard Biesheuvel wrote:
> On 11 October 2016 at 10:09, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> > On 11 October 2016 at 09:48, Steve Capper <steve.capper@linaro.org> wrote:
> >> So in arch/arm64/include/asm/pgtable-hwdef.h, we have:
> >> CONT_PTE_SHIFT
> >> CONT_PMD_SHIFT
> >> CONT_PTES
> >> CONT_PMDS
> >> CONT_PTE_SIZE
> >> CONT_PTE_MASK
> >> ...
> >>
> >> which are used by the contiguous hint HugeTLB code.
> >> Can those be adopted instead of CONT_MASK and CONT_SIZE?
> >>
> 
> Looking at the hugetlb code, it appears to support contiguous PMDs for
> 4k and 64k pages as well, while the ARM ARM only defines it for 16k
> pages. I suppose the contiguous bit is simply ignored for level 2
> entries when using 4k or 64k pages kernels, but I think it would be
> better for the code to reflect this as well.

Which bit in the ARM ARM says that you can't support contiguous PMDs for 4k
and 64k pages? I see that the number of contiguous entries changes between
levels for 16k pages, but that's it.

Will

^ permalink raw reply

* [PATCH v2 3/3] arm64: mm: set the contiguous bit for kernel mappings where appropriate
From: Ard Biesheuvel @ 2016-10-11 12:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476189589-1443-1-git-send-email-ard.biesheuvel@linaro.org>

Now that we no longer allow live kernel PMDs to be split, it is safe to
start using the contiguous bit for kernel mappings. So set the contiguous
bit in the kernel page mappings for regions whose size and alignment are
suitable for this. This includes contiguous level 2 mappings for 16k
granule kernels.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/mm/mmu.c | 35 +++++++++++++++++---
 1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index ee3cda6c41a7..c2bf7396888b 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -102,8 +102,10 @@ static const pteval_t modifiable_attr_mask = PTE_PXN | PTE_RDONLY | PTE_WRITE;
 static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
 				  unsigned long end, unsigned long pfn,
 				  pgprot_t prot,
-				  phys_addr_t (*pgtable_alloc)(void))
+				  phys_addr_t (*pgtable_alloc)(void),
+				  bool page_mappings_only)
 {
+	pgprot_t __prot = prot;
 	pte_t *pte;
 
 	BUG_ON(pmd_sect(*pmd));
@@ -121,7 +123,18 @@ static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
 	do {
 		pte_t old_pte = *pte;
 
-		set_pte(pte, pfn_pte(pfn, prot));
+		/*
+		 * Set the contiguous bit for the subsequent group of PTEs if
+		 * its size and alignment are suitable.
+		 */
+		if (((addr | PFN_PHYS(pfn)) & ~CONT_PTE_MASK) == 0) {
+			if (!page_mappings_only && end - addr >= CONT_PTE_SIZE)
+				__prot = __pgprot(pgprot_val(prot) | PTE_CONT);
+			else
+				__prot = prot;
+		}
+
+		set_pte(pte, pfn_pte(pfn, __prot));
 		pfn++;
 
 		/*
@@ -141,6 +154,7 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
 				  phys_addr_t (*pgtable_alloc)(void),
 				  bool page_mappings_only)
 {
+	pgprot_t __prot = prot;
 	pmd_t *pmd;
 	unsigned long next;
 
@@ -164,10 +178,22 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
 
 		next = pmd_addr_end(addr, end);
 
+		/*
+		 * For 16K granule only, attempt to put down a 1 GB block by
+		 * stringing 32 PMD block mappings together.
+		 */
+		if (IS_ENABLED(CONFIG_ARM64_16K_PAGES) &&
+		    ((addr | phys) & ~CONT_PMD_MASK) == 0) {
+			if (!page_mappings_only && end - addr >= CONT_PMD_SIZE)
+				__prot = __pgprot(pgprot_val(prot) | PTE_CONT);
+			else
+				__prot = prot;
+		}
+
 		/* try section mapping first */
 		if (((addr | next | phys) & ~SECTION_MASK) == 0 &&
 		      !page_mappings_only) {
-			pmd_set_huge(pmd, phys, prot);
+			pmd_set_huge(pmd, phys, __prot);
 
 			/*
 			 * After the PMD entry has been populated once, we
@@ -178,7 +204,8 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
 				~modifiable_attr_mask) != 0);
 		} else {
 			alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys),
-				       prot, pgtable_alloc);
+				       prot, pgtable_alloc,
+				       page_mappings_only);
 
 			BUG_ON(!pmd_none(old_pmd) &&
 			       pmd_val(old_pmd) != pmd_val(*pmd));
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 2/3] arm64: mm: replace 'block_mappings_allowed' with 'page_mappings_only'
From: Ard Biesheuvel @ 2016-10-11 12:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476189589-1443-1-git-send-email-ard.biesheuvel@linaro.org>

In preparation of adding support for contiguous PTE and PMD mappings,
let's replace 'block_mappings_allowed' with 'page_mappings_only', which
will be a more accurate description of the nature of the setting once we
add such contiguous mappings into the mix.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/include/asm/mmu.h |  2 +-
 arch/arm64/kernel/efi.c      |  8 ++---
 arch/arm64/mm/mmu.c          | 32 ++++++++++----------
 3 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 8d9fce037b2f..a81454ad5455 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -34,7 +34,7 @@ extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
 extern void init_mem_pgprot(void);
 extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
 			       unsigned long virt, phys_addr_t size,
-			       pgprot_t prot, bool allow_block_mappings);
+			       pgprot_t prot, bool page_mappings_only);
 extern void *fixmap_remap_fdt(phys_addr_t dt_phys);
 
 #endif
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index ba9bee389fd5..5d17f377d905 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -62,8 +62,8 @@ struct screen_info screen_info __section(.data);
 int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
 {
 	pteval_t prot_val = create_mapping_protection(md);
-	bool allow_block_mappings = (md->type != EFI_RUNTIME_SERVICES_CODE &&
-				     md->type != EFI_RUNTIME_SERVICES_DATA);
+	bool page_mappings_only = (md->type == EFI_RUNTIME_SERVICES_CODE ||
+				   md->type == EFI_RUNTIME_SERVICES_DATA);
 
 	if (!PAGE_ALIGNED(md->phys_addr) ||
 	    !PAGE_ALIGNED(md->num_pages << EFI_PAGE_SHIFT)) {
@@ -76,12 +76,12 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
 		 * from the MMU routines. So avoid block mappings altogether in
 		 * that case.
 		 */
-		allow_block_mappings = false;
+		page_mappings_only = true;
 	}
 
 	create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
 			   md->num_pages << EFI_PAGE_SHIFT,
-			   __pgprot(prot_val | PTE_NG), allow_block_mappings);
+			   __pgprot(prot_val | PTE_NG), page_mappings_only);
 	return 0;
 }
 
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 1cbadd20e741..ee3cda6c41a7 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -139,7 +139,7 @@ static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
 static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
 				  phys_addr_t phys, pgprot_t prot,
 				  phys_addr_t (*pgtable_alloc)(void),
-				  bool allow_block_mappings)
+				  bool page_mappings_only)
 {
 	pmd_t *pmd;
 	unsigned long next;
@@ -166,7 +166,7 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
 
 		/* try section mapping first */
 		if (((addr | next | phys) & ~SECTION_MASK) == 0 &&
-		      allow_block_mappings) {
+		      !page_mappings_only) {
 			pmd_set_huge(pmd, phys, prot);
 
 			/*
@@ -204,7 +204,7 @@ static inline bool use_1G_block(unsigned long addr, unsigned long next,
 static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
 				  phys_addr_t phys, pgprot_t prot,
 				  phys_addr_t (*pgtable_alloc)(void),
-				  bool allow_block_mappings)
+				  bool page_mappings_only)
 {
 	pud_t *pud;
 	unsigned long next;
@@ -226,7 +226,7 @@ static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
 		/*
 		 * For 4K granule only, attempt to put down a 1GB block
 		 */
-		if (use_1G_block(addr, next, phys) && allow_block_mappings) {
+		if (use_1G_block(addr, next, phys) && !page_mappings_only) {
 			pud_set_huge(pud, phys, prot);
 
 			/*
@@ -238,7 +238,7 @@ static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
 				~modifiable_attr_mask) != 0);
 		} else {
 			alloc_init_pmd(pud, addr, next, phys, prot,
-				       pgtable_alloc, allow_block_mappings);
+				       pgtable_alloc, page_mappings_only);
 
 			BUG_ON(!pud_none(old_pud) &&
 			       pud_val(old_pud) != pud_val(*pud));
@@ -253,7 +253,7 @@ static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
 				 unsigned long virt, phys_addr_t size,
 				 pgprot_t prot,
 				 phys_addr_t (*pgtable_alloc)(void),
-				 bool allow_block_mappings)
+				 bool page_mappings_only)
 {
 	unsigned long addr, length, end, next;
 	pgd_t *pgd = pgd_offset_raw(pgdir, virt);
@@ -273,7 +273,7 @@ static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
 	do {
 		next = pgd_addr_end(addr, end);
 		alloc_init_pud(pgd, addr, next, phys, prot, pgtable_alloc,
-			       allow_block_mappings);
+			       page_mappings_only);
 		phys += next - addr;
 	} while (pgd++, addr = next, addr != end);
 }
@@ -302,17 +302,17 @@ static void __init create_mapping_noalloc(phys_addr_t phys, unsigned long virt,
 			&phys, virt);
 		return;
 	}
-	__create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL, true);
+	__create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL, false);
 }
 
 void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
 			       unsigned long virt, phys_addr_t size,
-			       pgprot_t prot, bool allow_block_mappings)
+			       pgprot_t prot, bool page_mappings_only)
 {
 	BUG_ON(mm == &init_mm);
 
 	__create_pgd_mapping(mm->pgd, phys, virt, size, prot,
-			     pgd_pgtable_alloc, allow_block_mappings);
+			     pgd_pgtable_alloc, page_mappings_only);
 }
 
 static void create_mapping_late(phys_addr_t phys, unsigned long virt,
@@ -325,7 +325,7 @@ static void create_mapping_late(phys_addr_t phys, unsigned long virt,
 	}
 
 	__create_pgd_mapping(init_mm.pgd, phys, virt, size, prot,
-			     NULL, !debug_pagealloc_enabled());
+			     NULL, debug_pagealloc_enabled());
 }
 
 static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end)
@@ -343,7 +343,7 @@ static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end
 		__create_pgd_mapping(pgd, start, __phys_to_virt(start),
 				     end - start, PAGE_KERNEL,
 				     early_pgtable_alloc,
-				     !debug_pagealloc_enabled());
+				     debug_pagealloc_enabled());
 		return;
 	}
 
@@ -356,13 +356,13 @@ static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end
 				     __phys_to_virt(start),
 				     kernel_start - start, PAGE_KERNEL,
 				     early_pgtable_alloc,
-				     !debug_pagealloc_enabled());
+				     debug_pagealloc_enabled());
 	if (kernel_end < end)
 		__create_pgd_mapping(pgd, kernel_end,
 				     __phys_to_virt(kernel_end),
 				     end - kernel_end, PAGE_KERNEL,
 				     early_pgtable_alloc,
-				     !debug_pagealloc_enabled());
+				     debug_pagealloc_enabled());
 
 	/*
 	 * Map the linear alias of the [_text, __init_begin) interval as
@@ -372,7 +372,7 @@ static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end
 	 */
 	__create_pgd_mapping(pgd, kernel_start, __phys_to_virt(kernel_start),
 			     kernel_end - kernel_start, PAGE_KERNEL_RO,
-			     early_pgtable_alloc, !debug_pagealloc_enabled());
+			     early_pgtable_alloc, debug_pagealloc_enabled());
 }
 
 static void __init map_mem(pgd_t *pgd)
@@ -422,7 +422,7 @@ static void __init map_kernel_segment(pgd_t *pgd, void *va_start, void *va_end,
 	BUG_ON(!PAGE_ALIGNED(size));
 
 	__create_pgd_mapping(pgd, pa_start, (unsigned long)va_start, size, prot,
-			     early_pgtable_alloc, !debug_pagealloc_enabled());
+			     early_pgtable_alloc, debug_pagealloc_enabled());
 
 	vma->addr	= va_start;
 	vma->phys_addr	= pa_start;
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 1/3] arm64: mm: BUG on unsupported manipulations of live kernel mappings
From: Ard Biesheuvel @ 2016-10-11 12:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476189589-1443-1-git-send-email-ard.biesheuvel@linaro.org>

Now that we take care not manipulate the live kernel page tables in a
way that may lead to TLB conflicts, the case where a table mapping is
replaced by a block mapping can no longer occur. So remove the handling
of this at the PUD and PMD levels, and instead, BUG() on any occurrence
of live kernel page table manipulations that modify anything other than
the permission bits.

Since mark_rodata_ro() is the only caller where the kernel mappings that
are being manipulated are actually live, drop the various conditional
flush_tlb_all() invocations, and add a single call to mark_rodata_ro()
instead.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/mm/mmu.c | 68 ++++++++++++--------
 1 file changed, 41 insertions(+), 27 deletions(-)

diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 05615a3fdc6f..1cbadd20e741 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -28,8 +28,6 @@
 #include <linux/memblock.h>
 #include <linux/fs.h>
 #include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/stop_machine.h>
 
 #include <asm/barrier.h>
 #include <asm/cputype.h>
@@ -95,6 +93,12 @@ static phys_addr_t __init early_pgtable_alloc(void)
 	return phys;
 }
 
+/*
+ * The following mapping attributes may be updated in live
+ * kernel mappings without the need for break-before-make.
+ */
+static const pteval_t modifiable_attr_mask = PTE_PXN | PTE_RDONLY | PTE_WRITE;
+
 static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
 				  unsigned long end, unsigned long pfn,
 				  pgprot_t prot,
@@ -115,8 +119,18 @@ static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
 
 	pte = pte_set_fixmap_offset(pmd, addr);
 	do {
+		pte_t old_pte = *pte;
+
 		set_pte(pte, pfn_pte(pfn, prot));
 		pfn++;
+
+		/*
+		 * After the PTE entry has been populated once, we
+		 * only allow updates to the permission attributes.
+		 */
+		BUG_ON(!pte_none(old_pte) &&
+		       ((pte_val(old_pte) ^ pte_val(*pte)) &
+			~modifiable_attr_mask) != 0);
 	} while (pte++, addr += PAGE_SIZE, addr != end);
 
 	pte_clear_fixmap();
@@ -146,27 +160,28 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
 
 	pmd = pmd_set_fixmap_offset(pud, addr);
 	do {
+		pmd_t old_pmd = *pmd;
+
 		next = pmd_addr_end(addr, end);
+
 		/* try section mapping first */
 		if (((addr | next | phys) & ~SECTION_MASK) == 0 &&
 		      allow_block_mappings) {
-			pmd_t old_pmd =*pmd;
 			pmd_set_huge(pmd, phys, prot);
+
 			/*
-			 * Check for previous table entries created during
-			 * boot (__create_page_tables) and flush them.
+			 * After the PMD entry has been populated once, we
+			 * only allow updates to the permission attributes.
 			 */
-			if (!pmd_none(old_pmd)) {
-				flush_tlb_all();
-				if (pmd_table(old_pmd)) {
-					phys_addr_t table = pmd_page_paddr(old_pmd);
-					if (!WARN_ON_ONCE(slab_is_available()))
-						memblock_free(table, PAGE_SIZE);
-				}
-			}
+			BUG_ON(!pmd_none(old_pmd) &&
+			       ((pmd_val(old_pmd) ^ pmd_val(*pmd)) &
+				~modifiable_attr_mask) != 0);
 		} else {
 			alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys),
 				       prot, pgtable_alloc);
+
+			BUG_ON(!pmd_none(old_pmd) &&
+			       pmd_val(old_pmd) != pmd_val(*pmd));
 		}
 		phys += next - addr;
 	} while (pmd++, addr = next, addr != end);
@@ -204,33 +219,29 @@ static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
 
 	pud = pud_set_fixmap_offset(pgd, addr);
 	do {
+		pud_t old_pud = *pud;
+
 		next = pud_addr_end(addr, end);
 
 		/*
 		 * For 4K granule only, attempt to put down a 1GB block
 		 */
 		if (use_1G_block(addr, next, phys) && allow_block_mappings) {
-			pud_t old_pud = *pud;
 			pud_set_huge(pud, phys, prot);
 
 			/*
-			 * If we have an old value for a pud, it will
-			 * be pointing to a pmd table that we no longer
-			 * need (from swapper_pg_dir).
-			 *
-			 * Look up the old pmd table and free it.
+			 * After the PUD entry has been populated once, we
+			 * only allow updates to the permission attributes.
 			 */
-			if (!pud_none(old_pud)) {
-				flush_tlb_all();
-				if (pud_table(old_pud)) {
-					phys_addr_t table = pud_page_paddr(old_pud);
-					if (!WARN_ON_ONCE(slab_is_available()))
-						memblock_free(table, PAGE_SIZE);
-				}
-			}
+			BUG_ON(!pud_none(old_pud) &&
+			       ((pud_val(old_pud) ^ pud_val(*pud)) &
+				~modifiable_attr_mask) != 0);
 		} else {
 			alloc_init_pmd(pud, addr, next, phys, prot,
 				       pgtable_alloc, allow_block_mappings);
+
+			BUG_ON(!pud_none(old_pud) &&
+			       pud_val(old_pud) != pud_val(*pud));
 		}
 		phys += next - addr;
 	} while (pud++, addr = next, addr != end);
@@ -396,6 +407,9 @@ void mark_rodata_ro(void)
 	section_size = (unsigned long)__init_begin - (unsigned long)__start_rodata;
 	create_mapping_late(__pa(__start_rodata), (unsigned long)__start_rodata,
 			    section_size, PAGE_KERNEL_RO);
+
+	/* flush the TLBs after updating live kernel mappings */
+	flush_tlb_all();
 }
 
 static void __init map_kernel_segment(pgd_t *pgd, void *va_start, void *va_end,
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 0/3] arm64/mm: use the contiguous attribute for kernel mappings
From: Ard Biesheuvel @ 2016-10-11 12:39 UTC (permalink / raw)
  To: linux-arm-kernel

This 3-piece series is a followup to the single patch 'arm64: mmu: set the
contiguous for kernel mappings when appropriate' sent out on the 10th [0].

This v2 addresses the following issues:
- the contiguous attribute is also useful for contigous PMD mappings on 16k
  granule kernels (i.e., 1 GB blocks)
- the function parameter 'block_mappings_allowed' does not clearly convey
  whether contiguous page mappings should be used, so it is renamed to
  'page_mappings_only', and its meaning inverted
- instead of BUGging on changes in the PTE_CONT attribute in PMD or PTE entries
  that have been populated already, BUG on any modification except for
  permission attributes, which don't require break-before-make when changed.

[0] http://marc.info/?l=linux-arm-kernel&m=147612332130714

Ard Biesheuvel (3):
  arm64: mm: BUG on unsupported manipulations of live kernel mappings
  arm64: mm: replace 'block_mappings_allowed' with 'page_mappings_only'
  arm64: mm: set the contiguous bit for kernel mappings where
    appropriate

 arch/arm64/include/asm/mmu.h |   2 +-
 arch/arm64/kernel/efi.c      |   8 +-
 arch/arm64/mm/mmu.c          | 135 +++++++++++++-------
 3 files changed, 93 insertions(+), 52 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Adrian Hunter @ 2016-10-11 12:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e08ffb085d02a784b28456ac47fa4dc6540a9139.1475853198.git-series.gregory.clement@free-electrons.com>

On 07/10/16 18:22, Gregory CLEMENT wrote:
> From: Ziji Hu <huziji@marvell.com>
> 
> Marvell Xenon eMMC/SD/SDIO Host Controller contains PHY.
> Three types of PHYs are supported.
> 
> Add support to multiple types of PHYs init and configuration.
> Add register definitions of PHYs.
> 
> Signed-off-by: Hu Ziji <huziji@marvell.com>
> Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
>  MAINTAINERS                        |    1 +-
>  drivers/mmc/host/Makefile          |    2 +-
>  drivers/mmc/host/sdhci-xenon-phy.c | 1141 +++++++++++++++++++++++++++++-
>  drivers/mmc/host/sdhci-xenon-phy.h |  157 ++++-
>  drivers/mmc/host/sdhci-xenon.c     |    4 +-
>  drivers/mmc/host/sdhci-xenon.h     |   17 +-
>  6 files changed, 1321 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c
>  create mode 100644 drivers/mmc/host/sdhci-xenon-phy.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 859420e5dfd3..b5673c2ee5f2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7583,6 +7583,7 @@ M:	Ziji Hu <huziji@marvell.com>
>  L:	linux-mmc at vger.kernel.org
>  S:	Supported
>  F:	drivers/mmc/host/sdhci-xenon.*
> +F:	drivers/mmc/host/sdhci-xenon-phy.*
>  F:	Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
>  
>  MATROX FRAMEBUFFER DRIVER
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 75eaf743486c..4f2854556ff7 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -82,4 +82,4 @@ ifeq ($(CONFIG_CB710_DEBUG),y)
>  endif
>  
>  obj-$(CONFIG_MMC_SDHCI_XENON)	+= sdhci-xenon-driver.o
> -sdhci-xenon-driver-y		+= sdhci-xenon.o
> +sdhci-xenon-driver-y		+= sdhci-xenon.o sdhci-xenon-phy.o
> diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c
> new file mode 100644
> index 000000000000..4eb8fea1bec9
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-xenon-phy.c

<SNIP>

> +static int __xenon_emmc_delay_adj_test(struct mmc_card *card)
> +{
> +	int err;
> +	u8 *ext_csd = NULL;
> +
> +	err = mmc_get_ext_csd(card, &ext_csd);
> +	kfree(ext_csd);
> +
> +	return err;
> +}
> +
> +static int __xenon_sdio_delay_adj_test(struct mmc_card *card)
> +{
> +	struct mmc_command cmd = {0};
> +	int err;
> +
> +	cmd.opcode = SD_IO_RW_DIRECT;
> +	cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
> +
> +	err = mmc_wait_for_cmd(card->host, &cmd, 0);
> +	if (err)
> +		return err;
> +
> +	if (cmd.resp[0] & R5_ERROR)
> +		return -EIO;
> +	if (cmd.resp[0] & R5_FUNCTION_NUMBER)
> +		return -EINVAL;
> +	if (cmd.resp[0] & R5_OUT_OF_RANGE)
> +		return -ERANGE;
> +	return 0;
> +}
> +
> +static int __xenon_sd_delay_adj_test(struct mmc_card *card)
> +{
> +	struct mmc_command cmd = {0};
> +	int err;
> +
> +	cmd.opcode = MMC_SEND_STATUS;
> +	cmd.arg = card->rca << 16;
> +	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> +
> +	err = mmc_wait_for_cmd(card->host, &cmd, 0);
> +	return err;
> +}
> +
> +static int xenon_delay_adj_test(struct mmc_card *card)
> +{
> +	WARN_ON(!card);
> +	WARN_ON(!card->host);
> +
> +	if (mmc_card_mmc(card))
> +		return __xenon_emmc_delay_adj_test(card);
> +	else if (mmc_card_sd(card))
> +		return __xenon_sd_delay_adj_test(card);
> +	else if (mmc_card_sdio(card))
> +		return __xenon_sdio_delay_adj_test(card);
> +	else
> +		return -EINVAL;
> +}

So you are issuing commands from the ->set_ios() callback.  I would want to
get Ulf's OK for that before going further.

One thing: you will need to ensure you don't trigger get HS400 re-tuning
because it will call back into ->set_ios().

And you have the problem that you need to get a reference to the card before
the card device has been added.  As I wrote in response to the previous
patch, you should get Ulf's help with that too.

^ permalink raw reply

* [PATCH 6/10] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
From: Adrian Hunter @ 2016-10-11 12:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <ecde22fd342b0b2001ff5676ea1e67b7226d6946.1475853198.git-series.gregory.clement@free-electrons.com>

On 07/10/16 18:22, Gregory CLEMENT wrote:
> From: Ziji Hu <huziji@marvell.com>
> 
> Add Xenon eMMC/SD/SDIO host controller core functionality.
> Add Xenon specific intialization process.
> Add Xenon specific mmc_host_ops APIs.
> Add Xenon specific register definitions.
> 
> Add CONFIG_MMC_SDHCI_XENON support in drivers/mmc/host/Kconfig.
> 
> Marvell Xenon SDHC conforms to SD Physical Layer Specification
> Version 3.01 and is designed according to the guidelines provided
> in the SD Host Controller Standard Specification Version 3.00.
> 
> Signed-off-by: Hu Ziji <huziji@marvell.com>
> Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>

I looked at a couple of things but you need to sort out the issues with
card_candidate before going further.

> ---
>  MAINTAINERS                    |   1 +-
>  drivers/mmc/host/Kconfig       |   9 +-
>  drivers/mmc/host/Makefile      |   3 +-
>  drivers/mmc/host/sdhci-xenon.c | 599 ++++++++++++++++++++++++++++++++++-
>  drivers/mmc/host/sdhci-xenon.h | 134 ++++++++-
>  5 files changed, 746 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/mmc/host/sdhci-xenon.c
>  create mode 100644 drivers/mmc/host/sdhci-xenon.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 4aa0eac9bfc7..859420e5dfd3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7582,6 +7582,7 @@ MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
>  M:	Ziji Hu <huziji@marvell.com>
>  L:	linux-mmc at vger.kernel.org
>  S:	Supported
> +F:	drivers/mmc/host/sdhci-xenon.*
>  F:	Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
>  
>  MATROX FRAMEBUFFER DRIVER
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 5274f503a39a..85a53623526a 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -798,3 +798,12 @@ config MMC_SDHCI_BRCMSTB
>  	  Broadcom STB SoCs.
>  
>  	  If unsure, say Y.
> +
> +config MMC_SDHCI_XENON
> +	tristate "Marvell Xenon eMMC/SD/SDIO SDHCI driver"
> +	depends on MMC_SDHCI && MMC_SDHCI_PLTFM
> +	help
> +	  This selects Marvell Xenon eMMC/SD/SDIO SDHCI.
> +	  If you have a machine with integrated Marvell Xenon SDHC IP,
> +	  say Y or M here.
> +	  If unsure, say N.
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index e2bdaaf43184..75eaf743486c 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -80,3 +80,6 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
>  ifeq ($(CONFIG_CB710_DEBUG),y)
>  	CFLAGS-cb710-mmc	+= -DDEBUG
>  endif
> +
> +obj-$(CONFIG_MMC_SDHCI_XENON)	+= sdhci-xenon-driver.o
> +sdhci-xenon-driver-y		+= sdhci-xenon.o
> diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
> new file mode 100644
> index 000000000000..03ba183494d3
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-xenon.c
> @@ -0,0 +1,599 @@
> +/*
> + * Driver for Marvell SOC Platform Group Xenon SDHC as a platform device
> + *
> + * Copyright (C) 2016 Marvell, All Rights Reserved.
> + *
> + * Author:	Hu Ziji <huziji@marvell.com>
> + * Date:	2016-8-24
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * Inspired by Jisheng Zhang <jszhang@marvell.com>
> + * Special thanks to Video BG4 project team.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/sdio.h>
> +#include <linux/mmc/card.h>
> +#include <linux/mmc/host.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +
> +#include "sdhci-pltfm.h"
> +#include "sdhci.h"
> +#include "sdhci-xenon.h"
> +
> +/* Set SDCLK-off-while-idle */
> +static void xenon_set_sdclk_off_idle(struct sdhci_host *host,
> +				     unsigned char slot_idx, bool enable)
> +{
> +	u32 reg;
> +	u32 mask;
> +
> +	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
> +	/* Get the bit shift basing on the slot index */
> +	mask = (0x1 << (SDCLK_IDLEOFF_ENABLE_SHIFT + slot_idx));
> +	if (enable)
> +		reg |= mask;
> +	else
> +		reg &= ~mask;
> +
> +	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
> +}
> +
> +/* Enable/Disable the Auto Clock Gating function */
> +static void xenon_set_acg(struct sdhci_host *host, bool enable)
> +{
> +	u32 reg;
> +
> +	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
> +	if (enable)
> +		reg &= ~AUTO_CLKGATE_DISABLE_MASK;
> +	else
> +		reg |= AUTO_CLKGATE_DISABLE_MASK;
> +	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
> +}
> +
> +/* Enable this slot */
> +static void xenon_enable_slot(struct sdhci_host *host,
> +			      unsigned char slot_idx)
> +{
> +	u32 reg;
> +
> +	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
> +	reg |= (BIT(slot_idx) << SLOT_ENABLE_SHIFT);
> +	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
> +
> +	/*
> +	 * Manually set the flag which all the slots require,
> +	 * including SD, eMMC, SDIO
> +	 */
> +	host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
> +}
> +
> +/* Disable this slot */
> +static void xenon_disable_slot(struct sdhci_host *host,
> +			       unsigned char slot_idx)
> +{
> +	u32 reg;
> +
> +	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
> +	reg &= ~(BIT(slot_idx) << SLOT_ENABLE_SHIFT);
> +	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
> +}
> +
> +/* Enable Parallel Transfer Mode */
> +static void xenon_enable_slot_parallel_tran(struct sdhci_host *host,
> +					    unsigned char slot_idx)
> +{
> +	u32 reg;
> +
> +	reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
> +	reg |= BIT(slot_idx);
> +	sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
> +}
> +
> +static void xenon_slot_tuning_setup(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	u32 reg;
> +
> +	/* Disable the Re-Tuning Request functionality */
> +	reg = sdhci_readl(host, SDHC_SLOT_RETUNING_REQ_CTRL);
> +	reg &= ~RETUNING_COMPATIBLE;
> +	sdhci_writel(host, reg, SDHC_SLOT_RETUNING_REQ_CTRL);
> +
> +	/* Disbale the Re-tuning Event Signal Enable */

Disbale -> Disable

> +	reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
> +	reg &= ~SDHCI_INT_RETUNE;
> +	sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE);
> +
> +	/* Force to use Tuning Mode 1 */
> +	host->tuning_mode = SDHCI_TUNING_MODE_1;
> +	/* Set re-tuning period */
> +	host->tuning_count = 1 << (priv->tuning_count - 1);
> +}
> +
> +/*
> + * Operations inside struct sdhci_ops
> + */
> +/* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */
> +static void sdhci_xenon_reset_exit(struct sdhci_host *host,
> +				   unsigned char slot_idx, u8 mask)
> +{
> +	/* Only SOFTWARE RESET ALL will clear the register setting */
> +	if (!(mask & SDHCI_RESET_ALL))
> +		return;
> +
> +	/* Disable tuning request and auto-retuing again */

retuing -> retuning

> +	xenon_slot_tuning_setup(host);
> +
> +	xenon_set_acg(host, true);
> +
> +	xenon_set_sdclk_off_idle(host, slot_idx, false);
> +}
> +
> +static void sdhci_xenon_reset(struct sdhci_host *host, u8 mask)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +
> +	sdhci_reset(host, mask);
> +	sdhci_xenon_reset_exit(host, priv->slot_idx, mask);
> +}
> +
> +/*
> + * Xenon defines different values for HS200 and SDR104
> + * in Host_Control_2
> + */
> +static void xenon_set_uhs_signaling(struct sdhci_host *host,
> +				    unsigned int timing)
> +{
> +	u16 ctrl_2;
> +
> +	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +	/* Select Bus Speed Mode for host */
> +	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
> +	if (timing == MMC_TIMING_MMC_HS200)
> +		ctrl_2 |= XENON_SDHCI_CTRL_HS200;
> +	else if (timing == MMC_TIMING_UHS_SDR104)
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> +	else if (timing == MMC_TIMING_UHS_SDR12)
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
> +	else if (timing == MMC_TIMING_UHS_SDR25)
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> +	else if (timing == MMC_TIMING_UHS_SDR50)
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> +	else if ((timing == MMC_TIMING_UHS_DDR50) ||
> +		 (timing == MMC_TIMING_MMC_DDR52))
> +		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> +	else if (timing == MMC_TIMING_MMC_HS400)
> +		ctrl_2 |= XENON_SDHCI_CTRL_HS400;
> +	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +}
> +
> +static const struct sdhci_ops sdhci_xenon_ops = {
> +	.set_clock		= sdhci_set_clock,
> +	.set_bus_width		= sdhci_set_bus_width,
> +	.reset			= sdhci_xenon_reset,
> +	.set_uhs_signaling	= xenon_set_uhs_signaling,
> +	.get_max_clock		= sdhci_pltfm_clk_get_max_clock,
> +};
> +
> +static const struct sdhci_pltfm_data sdhci_xenon_pdata = {
> +	.ops = &sdhci_xenon_ops,
> +	.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
> +			SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 |
> +			SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
> +			SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
> +};
> +
> +/*
> + * Xenon Specific Operations in mmc_host_ops
> + */
> +static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> +	struct sdhci_host *host = mmc_priv(mmc);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	unsigned long flags;
> +
> +	/*
> +	 * HS400/HS200/eMMC HS doesn't have Preset Value register.
> +	 * However, sdhci_set_ios will read HS400/HS200 Preset register.
> +	 * Disable Preset Value register for HS400/HS200.
> +	 * eMMC HS with preset_enabled set will trigger a bug in
> +	 * get_preset_value().
> +	 */
> +	spin_lock_irqsave(&host->lock, flags);
> +	if ((ios->timing == MMC_TIMING_MMC_HS400) ||
> +	    (ios->timing == MMC_TIMING_MMC_HS200) ||
> +	    (ios->timing == MMC_TIMING_MMC_HS)) {
> +		host->preset_enabled = false;
> +		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
> +	} else {
> +		host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
> +	}
> +	spin_unlock_irqrestore(&host->lock, flags);
> +
> +	sdhci_set_ios(mmc, ios);
> +
> +	if (host->clock > DEFAULT_SDCLK_FREQ) {
> +		spin_lock_irqsave(&host->lock, flags);
> +		xenon_set_sdclk_off_idle(host, priv->slot_idx, true);
> +		spin_unlock_irqrestore(&host->lock, flags);
> +	}
> +}
> +
> +static int __emmc_signal_voltage_switch(struct mmc_host *mmc,
> +					const unsigned char signal_voltage)
> +{
> +	u32 ctrl;
> +	unsigned char voltage_code;
> +	struct sdhci_host *host = mmc_priv(mmc);
> +
> +	if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
> +		voltage_code = EMMC_VCCQ_3_3V;
> +	else if (signal_voltage == MMC_SIGNAL_VOLTAGE_180)
> +		voltage_code = EMMC_VCCQ_1_8V;
> +	else
> +		return -EINVAL;
> +
> +	/*
> +	 * This host is for eMMC, XENON self-defined
> +	 * eMMC slot control register should be accessed
> +	 * instead of Host Control 2
> +	 */
> +	ctrl = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
> +	ctrl &= ~EMMC_VCCQ_MASK;
> +	ctrl |= voltage_code;
> +	sdhci_writel(host, ctrl, SDHC_SLOT_EMMC_CTRL);
> +
> +	/* There is no standard to determine this waiting period */
> +	usleep_range(1000, 2000);
> +
> +	/* Check whether io voltage switch is done */
> +	ctrl = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
> +	ctrl &= EMMC_VCCQ_MASK;
> +	/*
> +	 * This bit is set only when regulator feeds back the voltage switch
> +	 * results to Xenon SDHC.
> +	 * However, in actaul implementation, regulator might not provide
> +	 * this feedback.
> +	 * Thus we shall not rely on this bit to determine if switch failed.
> +	 * If the bit is not set, just throw a message.
> +	 * Besides, error code should not be returned.
> +	 */
> +	if (ctrl != voltage_code)
> +		dev_info(mmc_dev(mmc), "fail to detect eMMC signal voltage stable\n");
> +	return 0;
> +}
> +
> +static int xenon_emmc_signal_voltage_switch(struct mmc_host *mmc,
> +					    struct mmc_ios *ios)
> +{
> +	unsigned char voltage = ios->signal_voltage;
> +
> +	if ((voltage == MMC_SIGNAL_VOLTAGE_330) ||
> +	    (voltage == MMC_SIGNAL_VOLTAGE_180))
> +		return __emmc_signal_voltage_switch(mmc, voltage);
> +
> +	dev_err(mmc_dev(mmc), "Unsupported signal voltage: %d\n",
> +		voltage);
> +	return -EINVAL;
> +}
> +
> +static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
> +					     struct mmc_ios *ios)
> +{
> +	struct sdhci_host *host = mmc_priv(mmc);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +
> +	/*
> +	 * Before SD/SDIO set signal voltage, SD bus clock should be
> +	 * disabled. However, sdhci_set_clock will also disable the Internal
> +	 * clock in mmc_set_signal_voltage().
> +	 * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
> +	 * Thus here manually enable internal clock.
> +	 *
> +	 * After switch completes, it is unnecessary to disable internal clock,
> +	 * since keeping internal clock active obeys SD spec.
> +	 */
> +	enable_xenon_internal_clk(host);
> +
> +	if (priv->card_candidate) {

mmc_power_up() calls __mmc_set_signal_voltage() calls
host->ops->start_signal_voltage_switch so priv->card_candidate could be an
invalid reference to an old card.

So that's not going to work if the card changes - not only for removable
cards but even for eMMC if init fails and retries.

> +		if (mmc_card_mmc(priv->card_candidate))
> +			return xenon_emmc_signal_voltage_switch(mmc, ios);

So if all you need to know is whether it is a eMMC, why can't DT tell you?

> +	}
> +
> +	return sdhci_start_signal_voltage_switch(mmc, ios);
> +}
> +
> +/*
> + * After determining which slot is used for SDIO,
> + * some additional task is required.
> + */
> +static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card)
> +{
> +	struct sdhci_host *host = mmc_priv(mmc);
> +	u32 reg;
> +	u8 slot_idx;
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +
> +	/* Link the card for delay adjustment */
> +	priv->card_candidate = card;

You really need a better way to get the card.  I suggest you take up the
issue with Ulf.  One possibility is to have mmc core set host->card = card
much earlier.

> +	/* Set tuning functionality of this slot */
> +	xenon_slot_tuning_setup(host);
> +
> +	slot_idx = priv->slot_idx;
> +	if (!mmc_card_sdio(card)) {
> +		/* Re-enable the Auto-CMD12 cap flag. */
> +		host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
> +		host->flags |= SDHCI_AUTO_CMD12;
> +
> +		/* Clear SDIO Card Inserted indication */
> +		reg = sdhci_readl(host, SDHC_SYS_CFG_INFO);
> +		reg &= ~(1 << (slot_idx + SLOT_TYPE_SDIO_SHIFT));
> +		sdhci_writel(host, reg, SDHC_SYS_CFG_INFO);
> +
> +		if (mmc_card_mmc(card)) {
> +			mmc->caps |= MMC_CAP_NONREMOVABLE;
> +			if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V))
> +				mmc->caps |= MMC_CAP_1_8V_DDR;
> +			/*
> +			 * Force to clear BUS_TEST to
> +			 * skip bus_test_pre and bus_test_post
> +			 */
> +			mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST;
> +			mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ |
> +				      MMC_CAP2_PACKED_CMD;
> +			if (mmc->caps & MMC_CAP_8_BIT_DATA)
> +				mmc->caps2 |= MMC_CAP2_HS400_1_8V;
> +		}
> +	} else {
> +		/*
> +		 * Delete the Auto-CMD12 cap flag.
> +		 * Otherwise, when sending multi-block CMD53,
> +		 * Driver will set Transfer Mode Register to enable Auto CMD12.
> +		 * However, SDIO device cannot recognize CMD12.
> +		 * Thus SDHC will time-out for waiting for CMD12 response.
> +		 */
> +		host->quirks &= ~SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
> +		host->flags &= ~SDHCI_AUTO_CMD12;

sdhci_set_transfer_mode() won't enable auto-CMD12 for CMD53 anyway, so is
this needed?

> +
> +		/*
> +		 * Set SDIO Card Inserted indication
> +		 * to inform that the current slot is for SDIO
> +		 */
> +		reg = sdhci_readl(host, SDHC_SYS_CFG_INFO);
> +		reg |= (1 << (slot_idx + SLOT_TYPE_SDIO_SHIFT));
> +		sdhci_writel(host, reg, SDHC_SYS_CFG_INFO);
> +	}
> +}
> +
> +static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode)
> +{
> +	struct sdhci_host *host = mmc_priv(mmc);
> +
> +	if (host->timing == MMC_TIMING_UHS_DDR50)
> +		return 0;
> +
> +	return sdhci_execute_tuning(mmc, opcode);
> +}
> +
> +static void xenon_replace_mmc_host_ops(struct sdhci_host *host)
> +{
> +	host->mmc_host_ops.set_ios = xenon_set_ios;
> +	host->mmc_host_ops.start_signal_voltage_switch =
> +			xenon_start_signal_voltage_switch;
> +	host->mmc_host_ops.init_card = xenon_init_card;
> +	host->mmc_host_ops.execute_tuning = xenon_execute_tuning;
> +}
> +
> +static int xenon_probe_dt(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct sdhci_host *host = platform_get_drvdata(pdev);
> +	struct mmc_host *mmc = host->mmc;
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	int err;
> +	u32 slot_idx, nr_slot;
> +	u32 tuning_count;
> +	u32 reg;
> +
> +	/* Standard MMC property */
> +	err = mmc_of_parse(mmc);
> +	if (err)
> +		return err;
> +
> +	/* Standard SDHCI property */
> +	sdhci_get_of_property(pdev);
> +
> +	/*
> +	 * Xenon Specific property:
> +	 * slotno: the index of slot. Refer to SDHC_SYS_CFG_INFO register
> +	 * tuning-count: the interval between re-tuning
> +	 * PHY type: "sdhc phy", "emmc phy 5.0" or "emmc phy 5.1"
> +	 */
> +	if (!of_property_read_u32(np, "xenon,slotno", &slot_idx)) {
> +		nr_slot = sdhci_readl(host, SDHC_SYS_CFG_INFO);
> +		nr_slot &= NR_SUPPORTED_SLOT_MASK;
> +		if (unlikely(slot_idx > nr_slot)) {
> +			dev_err(mmc_dev(mmc), "Slot Index %d exceeds Number of slots %d\n",
> +				slot_idx, nr_slot);
> +			return -EINVAL;
> +		}
> +	} else {
> +		priv->slot_idx = 0x0;
> +	}
> +
> +	if (!of_property_read_u32(np, "xenon,tuning-count", &tuning_count)) {
> +		if (unlikely(tuning_count >= TMR_RETUN_NO_PRESENT)) {
> +			dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n",
> +				DEF_TUNING_COUNT);
> +			tuning_count = DEF_TUNING_COUNT;
> +		}
> +	} else {
> +		priv->tuning_count = DEF_TUNING_COUNT;
> +	}
> +
> +	if (of_property_read_bool(np, "xenon,mask-conflict-err")) {
> +		reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
> +		reg |= MASK_CMD_CONFLICT_ERROR;
> +		sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
> +	}
> +
> +	return err;
> +}
> +
> +static int xenon_slot_probe(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	u8 slot_idx = priv->slot_idx;
> +
> +	/* Enable slot */
> +	xenon_enable_slot(host, slot_idx);
> +
> +	/* Enable ACG */
> +	xenon_set_acg(host, true);
> +
> +	/* Enable Parallel Transfer Mode */
> +	xenon_enable_slot_parallel_tran(host, slot_idx);
> +
> +	priv->timing = MMC_TIMING_FAKE;
> +	priv->clock = 0;
> +
> +	return 0;
> +}
> +
> +static void xenon_slot_remove(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	u8 slot_idx = priv->slot_idx;
> +
> +	/* disable slot */
> +	xenon_disable_slot(host, slot_idx);
> +}
> +
> +static int sdhci_xenon_probe(struct platform_device *pdev)
> +{
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct sdhci_host *host;
> +	struct clk *clk, *axi_clk;
> +	struct sdhci_xenon_priv *priv;
> +	int err;
> +
> +	host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata,
> +				sizeof(struct sdhci_xenon_priv));
> +	if (IS_ERR(host))
> +		return PTR_ERR(host);
> +
> +	pltfm_host = sdhci_priv(host);
> +	priv = sdhci_pltfm_priv(pltfm_host);
> +
> +	xenon_set_acg(host, false);
> +
> +	/*
> +	 * Link Xenon specific mmc_host_ops function,
> +	 * to replace standard ones in sdhci_ops.
> +	 */
> +	xenon_replace_mmc_host_ops(host);
> +
> +	clk = devm_clk_get(&pdev->dev, "core");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Failed to setup input clk.\n");
> +		err = PTR_ERR(clk);
> +		goto free_pltfm;
> +	}
> +	clk_prepare_enable(clk);
> +	pltfm_host->clk = clk;
> +
> +	/*
> +	 * Some SOCs require additional clock to
> +	 * manage AXI bus clock.
> +	 * It is optional.
> +	 */
> +	axi_clk = devm_clk_get(&pdev->dev, "axi");
> +	if (!IS_ERR(axi_clk)) {
> +		clk_prepare_enable(axi_clk);
> +		priv->axi_clk = axi_clk;
> +	}
> +
> +	err = xenon_probe_dt(pdev);
> +	if (err)
> +		goto err_clk;
> +
> +	err = xenon_slot_probe(host);
> +	if (err)
> +		goto err_clk;
> +
> +	err = sdhci_add_host(host);
> +	if (err)
> +		goto remove_slot;
> +
> +	return 0;
> +
> +remove_slot:
> +	xenon_slot_remove(host);
> +err_clk:
> +	clk_disable_unprepare(pltfm_host->clk);
> +	if (!IS_ERR(axi_clk))
> +		clk_disable_unprepare(axi_clk);
> +free_pltfm:
> +	sdhci_pltfm_free(pdev);
> +	return err;
> +}
> +
> +static int sdhci_xenon_remove(struct platform_device *pdev)
> +{
> +	struct sdhci_host *host = platform_get_drvdata(pdev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xFFFFFFFF);
> +
> +	xenon_slot_remove(host);
> +
> +	sdhci_remove_host(host, dead);
> +
> +	clk_disable_unprepare(pltfm_host->clk);
> +	clk_disable_unprepare(priv->axi_clk);
> +
> +	sdhci_pltfm_free(pdev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id sdhci_xenon_dt_ids[] = {
> +	{ .compatible = "marvell,sdhci-xenon",},
> +	{ .compatible = "marvell,armada-3700-sdhci",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
> +
> +static struct platform_driver sdhci_xenon_driver = {
> +	.driver	= {
> +		.name	= "sdhci-xenon",
> +		.of_match_table = sdhci_xenon_dt_ids,
> +		.pm = &sdhci_pltfm_pmops,
> +	},
> +	.probe	= sdhci_xenon_probe,
> +	.remove	= sdhci_xenon_remove,
> +};
> +
> +module_platform_driver(sdhci_xenon_driver);
> +
> +MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC");
> +MODULE_AUTHOR("Hu Ziji <huziji@marvell.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
> new file mode 100644
> index 000000000000..c2370493fbe8
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-xenon.h
> @@ -0,0 +1,134 @@
> +/*
> + * Copyright (C) 2016 Marvell, All Rights Reserved.
> + *
> + * Author:	Hu Ziji <huziji@marvell.com>
> + * Date:	2016-8-24
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + */
> +#ifndef SDHCI_XENON_H_
> +#define SDHCI_XENON_H_
> +
> +#include <linux/clk.h>
> +#include <linux/mmc/card.h>
> +#include <linux/of.h>
> +#include "sdhci.h"
> +
> +/* Register Offset of SD Host Controller SOCP self-defined register */
> +#define SDHC_SYS_CFG_INFO			0x0104
> +#define SLOT_TYPE_SDIO_SHIFT			24
> +#define SLOT_TYPE_EMMC_MASK			0xFF
> +#define SLOT_TYPE_EMMC_SHIFT			16
> +#define SLOT_TYPE_SD_SDIO_MMC_MASK		0xFF
> +#define SLOT_TYPE_SD_SDIO_MMC_SHIFT		8
> +#define NR_SUPPORTED_SLOT_MASK			0x7
> +
> +#define SDHC_SYS_OP_CTRL			0x0108
> +#define AUTO_CLKGATE_DISABLE_MASK		BIT(20)
> +#define SDCLK_IDLEOFF_ENABLE_SHIFT		8
> +#define SLOT_ENABLE_SHIFT			0
> +
> +#define SDHC_SYS_EXT_OP_CTRL			0x010C
> +#define MASK_CMD_CONFLICT_ERROR			BIT(8)
> +
> +#define SDHC_SLOT_OP_STATUS_CTRL		0x0128
> +#define DELAY_90_DEGREE_MASK_EMMC5		BIT(7)
> +#define DELAY_90_DEGREE_SHIFT_EMMC5		7
> +#define EMMC_5_0_PHY_FIXED_DELAY_MASK		0x7F
> +#define EMMC_PHY_FIXED_DELAY_MASK		0xFF
> +#define EMMC_PHY_FIXED_DELAY_WINDOW_MIN		(EMMC_PHY_FIXED_DELAY_MASK >> 3)
> +#define SDH_PHY_FIXED_DELAY_MASK		0x1FF
> +#define SDH_PHY_FIXED_DELAY_WINDOW_MIN		(SDH_PHY_FIXED_DELAY_MASK >> 4)
> +
> +#define TUN_CONSECUTIVE_TIMES_SHIFT		16
> +#define TUN_CONSECUTIVE_TIMES_MASK		0x7
> +#define TUN_CONSECUTIVE_TIMES			0x4
> +#define TUNING_STEP_SHIFT			12
> +#define TUNING_STEP_MASK			0xF
> +#define TUNING_STEP_DIVIDER			BIT(6)
> +
> +#define FORCE_SEL_INVERSE_CLK_SHIFT		11
> +
> +#define SDHC_SLOT_EMMC_CTRL			0x0130
> +#define ENABLE_DATA_STROBE			BIT(24)
> +#define SET_EMMC_RSTN				BIT(16)
> +#define DISABLE_RD_DATA_CRC			BIT(14)
> +#define DISABLE_CRC_STAT_TOKEN			BIT(13)
> +#define EMMC_VCCQ_MASK				0x3
> +#define EMMC_VCCQ_1_8V				0x1
> +#define EMMC_VCCQ_3_3V				0x3
> +
> +#define SDHC_SLOT_RETUNING_REQ_CTRL		0x0144
> +/* retuning compatible */
> +#define RETUNING_COMPATIBLE			0x1
> +
> +#define SDHC_SLOT_EXT_PRESENT_STATE		0x014C
> +#define LOCK_STATE				0x1
> +
> +#define SDHC_SLOT_DLL_CUR_DLY_VAL		0x0150
> +
> +/* Tuning Parameter */
> +#define TMR_RETUN_NO_PRESENT			0xF
> +#define DEF_TUNING_COUNT			0x9
> +
> +#define MMC_TIMING_FAKE				0xFF
> +
> +#define DEFAULT_SDCLK_FREQ			(400000)
> +
> +/* Xenon specific Mode Select value */
> +#define XENON_SDHCI_CTRL_HS200			0x5
> +#define XENON_SDHCI_CTRL_HS400			0x6
> +
> +struct sdhci_xenon_priv {
> +	/*
> +	 * The bus_width, timing, and clock fields in below
> +	 * record the current setting of Xenon SDHC.
> +	 * Driver will call a Sampling Fixed Delay Adjustment
> +	 * if any setting is changed.
> +	 */
> +	unsigned char	bus_width;
> +	unsigned char	timing;
> +	unsigned char	tuning_count;
> +	unsigned int	clock;
> +	struct clk	*axi_clk;
> +
> +	/* Slot idx */
> +	u8		slot_idx;
> +
> +	/*
> +	 * When initializing card, Xenon has to determine card type and
> +	 * adjust Sampling Fixed delay.
> +	 * However, at that time, card structure is not linked to mmc_host.
> +	 * Thus a card pointer is added here to provide
> +	 * the delay adjustment function with the card structure
> +	 * of the card during initialization
> +	 */
> +	struct mmc_card *card_candidate;
> +};
> +
> +static inline int enable_xenon_internal_clk(struct sdhci_host *host)
> +{
> +	u32 reg;
> +	u8 timeout;
> +
> +	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
> +	reg |= SDHCI_CLOCK_INT_EN;
> +	sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
> +	/* Wait max 20 ms */
> +	timeout = 20;
> +	while (!((reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
> +			& SDHCI_CLOCK_INT_STABLE)) {
> +		if (timeout == 0) {
> +			pr_err("%s: Internal clock never stabilised.\n",
> +			       mmc_hostname(host->mmc));
> +			return -ETIMEDOUT;
> +		}
> +		timeout--;
> +		mdelay(1);
> +	}
> +
> +	return 0;
> +}
> +#endif
> 

^ permalink raw reply

* [PATCH v2 5/8] dt/bindings: Update binding for PM domain idle states
From: Sudeep Holla @ 2016-10-11 11:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161010221332.GD44885@linaro.org>



On 10/10/16 23:13, Lina Iyer wrote:
> On Mon, Oct 10 2016 at 11:13 -0600, Sudeep Holla wrote:

[...]

>> Either we say this binding is ARM CPU specific or generic, I can't
>> understand this mix 'n' match really. You have removed all the
>> CPUIdle stuff from this series which is good and makes it simpler,
>> but linking it to only "arm,idle-state" make be feel it's not
>> generic. OK I will have a look at the RFC as why generic compatible
>> was rejected.
>>
> I will look for the discussion around it as well. A initial look
> through didn't get me the thread I was looking for.
>

Sure

[...]

>>
>> I understand that but it's not that simple which I assume you *do*
>> agree. Hence may need bit of an explanation in the binding(not here
>> of-course as I mentioned earlier, but in the CPU Idle bindings).
>> Please consider DT bindings as any other specification. All I am
>> asking is more description in the binding.
>>
> Any ideas of what description you would like to see? It seemed fairly
> explanatory in the idle-states.txt, so I didn't go into depth here.
>

Various use cases we discussed and what takes precedence,... etc
E.g.: if the Renasas example I pointed out had cpu-idle-states and
power-domain but no domain-idle-states which is perfectly valid without
this bindings.

Basically all the important this we have discussed so far. Even the
OSC/PCC is worth mentioning so that we are explicitly clear that this
binding has no affiliation to those PSCI methods. In short it should be
able to answer any question one might get if is completely new to this
binding but is aware of old one.

[...]

>>
>> Agreed and sorry if I created any confusion. But this binding doesn't
>> clearly state how to build up the hierarchy if the leaf node is not a
>> power-domain node and I am just trying have those clarifications in the
>> binding. It would be good if those details are *explicitly* mentioned in
>> the binding, not this particularly, but in CPU Idle one when you
>> introduce the user of that.
>>
> As we have today, devices have their own way of figuring out their idle
> states, they are not represented in DT (CPU being an exception).

I understand that, and I assume this binding will provide a way to
represent that for devices too if required. No ? Otherwise I see no
point in just saying it's generic.

-- 
Regards,
Sudeep

^ permalink raw reply

* [PATCH] arm64: mmu: set the contiguous for kernel mappings when appropriate
From: Ard Biesheuvel @ 2016-10-11 11:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAKv+Gu-e6HT2YNvXJV2AbAzLrEeV6OvM6-hfTsCH0oXqH8zbKQ@mail.gmail.com>

On 11 October 2016 at 10:09, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> On 11 October 2016 at 09:48, Steve Capper <steve.capper@linaro.org> wrote:
>>
>>
>> On 11 October 2016 at 08:44, Mark Rutland <mark.rutland@arm.com> wrote:
>>>
>>> Hi Ard,
>>>
>>> On Mon, Oct 10, 2016 at 07:12:44PM +0100, Ard Biesheuvel wrote:
>>> > Now that we no longer allow live kernel PMDs to be split, it is safe to
>>> > start using the contiguous bit for kernel mappings. So set the
>>> > contiguous
>>> > bit in the kernel page mappings for regions whose size and alignment are
>>> > suitable for this.
>>> >
>>> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>>
>>> Given the splitting is now gone, using the contiguous bit makes sense to
>>> me.
>>>
>>> With 16K pages, we can have contiguous PMD entries. Should we handle
>>> those,
>>> too? e.g. have separate {PMD,PTE}_CONT{,_SIZE}?
>>>
>>> Otherwise, I have some comments below.
>>
>>
>> Hi,
>>
>> So in arch/arm64/include/asm/pgtable-hwdef.h, we have:
>> CONT_PTE_SHIFT
>> CONT_PMD_SHIFT
>> CONT_PTES
>> CONT_PMDS
>> CONT_PTE_SIZE
>> CONT_PTE_MASK
>> ...
>>
>> which are used by the contiguous hint HugeTLB code.
>> Can those be adopted instead of CONT_MASK and CONT_SIZE?
>>

Looking at the hugetlb code, it appears to support contiguous PMDs for
4k and 64k pages as well, while the ARM ARM only defines it for 16k
pages. I suppose the contiguous bit is simply ignored for level 2
entries when using 4k or 64k pages kernels, but I think it would be
better for the code to reflect this as well.

^ permalink raw reply


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