Merge branches 'clk-ti', 'clk-ingenic', 'clk-typo', 'clk-at91', 'clk-mmp2' and 'clk...
authorStephen Boyd <sboyd@kernel.org>
Fri, 3 Apr 2020 22:09:55 +0000 (15:09 -0700)
committerStephen Boyd <sboyd@kernel.org>
Fri, 3 Apr 2020 22:09:55 +0000 (15:09 -0700)
 - EHRPWM's TimeBase clock(TBCLK) for TI AM654 SoCs
 - Support PMC clks on at91sam9n12, at91rm9200, sama5d3, and at91sam9g45 SoCs
 - Fixes and improvements for the Marvell MMP2/MMP3 SoC clk drivers

* clk-ti:
  clk: keystone: Add new driver to handle syscon based clocks
  dt-bindings: clock: Add binding documentation for TI EHRPWM TBCLK

* clk-ingenic:
  clk: ingenic/TCU: Fix round_rate returning error
  clk: ingenic/jz4770: Exit with error if CGU init failed
  clk: JZ4780: Add function for enable the second core.
  clk: Ingenic: Add support for TCU of X1000.

* clk-typo:
  clk: Fix trivia typo in comment exlusive => exclusive

* clk-at91:
  clk: at91: add at91rm9200 pmc driver
  clk: at91: add at91sam9n12 pmc driver
  clk: at91: add sama5d3 pmc driver
  clk: at91: add at91sam9g45 pmc driver
  clk: at91: usb: introduce num_parents in driver's structure
  clk: at91: usb: use proper usbs_mask
  clk: at91: sam9x60: fix usb clock parents
  clk: at91: usb: continue if clk_hw_round_rate() return zero
  clk: at91: sam9x60: Don't use audio PLL

* clk-mmp2:
  clk: mmp2: Fix bit masks for LCDC I/O and pixel clocks
  clk: mmp2: Add clock for fifth SD HCI on MMP3
  dt-bindings: marvell,mmp2: Add clock id for the fifth SD HCI on MMP3
  clk: mmp2: Add clocks for the thermal sensors
  dt-bindings: marvell,mmp2: Add clock ids for the thermal sensors
  clk: mmp2: add the GPU clocks
  dt-bindings: marvell,mmp2: Add clock ids for the GPU clocks
  clk: mmp2: Add PLLs that are available on MMP3
  dt-bindings: marvell,mmp2: Add clock ids for MMP3 PLLs
  clk: mmp2: Check for MMP3
  dt-bindings: clock: Add MMP3 compatible string
  clk: mmp2: Stop pretending PLL outputs are constant
  clk: mmp2: Add support for PLL clock sources
  dt-bindings: clock: Convert marvell,mmp2-clock to json-schema
  clk: mmp2: Constify some strings
  clk: mmp2: Remove a unused prototype

* clk-arm-icst:
  MAINTAINERS: dt: update reference for arm-integrator.txt
  clk: versatile: Add device tree probing for IM-PD1 clocks
  clk: versatile: Export icst_clk_setup()
  dt-bindings: clock: Create YAML schema for ICST clocks

31 files changed:
Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/clock/arm-integrator.txt [deleted file]
Documentation/devicetree/bindings/clock/arm-syscon-icst.txt [deleted file]
Documentation/devicetree/bindings/clock/marvell,mmp2-clock.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/clock/marvell,mmp2.txt [deleted file]
Documentation/devicetree/bindings/clock/ti,am654-ehrpwm-tbclk.yaml [new file with mode: 0644]
MAINTAINERS
drivers/clk/at91/Makefile
drivers/clk/at91/at91rm9200.c [new file with mode: 0644]
drivers/clk/at91/at91sam9g45.c [new file with mode: 0644]
drivers/clk/at91/at91sam9n12.c [new file with mode: 0644]
drivers/clk/at91/clk-usb.c
drivers/clk/at91/sam9x60.c
drivers/clk/at91/sama5d3.c [new file with mode: 0644]
drivers/clk/clk.c
drivers/clk/ingenic/jz4770-cgu.c
drivers/clk/ingenic/jz4780-cgu.c
drivers/clk/ingenic/tcu.c
drivers/clk/keystone/Kconfig
drivers/clk/keystone/Makefile
drivers/clk/keystone/syscon-clk.c [new file with mode: 0644]
drivers/clk/mmp/Makefile
drivers/clk/mmp/clk-mix.c
drivers/clk/mmp/clk-of-mmp2.c
drivers/clk/mmp/clk-pll.c [new file with mode: 0644]
drivers/clk/mmp/clk.c
drivers/clk/mmp/clk.h
drivers/clk/versatile/clk-icst.c
drivers/clk/versatile/clk-icst.h
drivers/clk/versatile/clk-impd1.c
include/dt-bindings/clock/marvell,mmp2.h

diff --git a/Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml b/Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml
new file mode 100644 (file)
index 0000000..de9a465
--- /dev/null
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/arm,syscon-icst.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM System Controller ICST Clocks
+
+maintainers:
+  - Linus Walleij <linusw@kernel.org>
+
+description: |
+  The ICS525 and ICS307 oscillators are produced by Integrated
+  Devices Technology (IDT). ARM integrated these oscillators deeply into their
+  reference designs by adding special control registers that manage such
+  oscillators to their system controllers.
+
+  The various ARM system controllers contain logic to serialize and initialize
+  an ICST clock request after a write to the 32 bit register at an offset
+  into the system controller. Furthermore, to even be able to alter one of
+  these frequencies, the system controller must first be unlocked by
+  writing a special token to another offset in the system controller.
+
+  Some ARM hardware contain special versions of the serial interface that only
+  connects the low 8 bits of the VDW (missing one bit), hard-wires RDW to
+  different values and sometimes also hard-wires the output divider. They
+  therefore have special compatible strings as per this table (the OD value is
+  the value on the pins, not the resulting output divider).
+
+  In the core modules and logic tiles, the ICST is a configurable clock fed
+  from a 24 MHz clock on the motherboard (usually the main crystal) used for
+  generating e.g. video clocks. It is located on the core module and there is
+  only one of these. This clock node must be a subnode of the core module.
+
+  Hardware variant         RDW     OD          VDW
+
+  Integrator/AP            22      1           Bit 8 0, rest variable
+  integratorap-cm
+
+  Integrator/AP            46      3           Bit 8 0, rest variable
+  integratorap-sys
+
+  Integrator/AP            22 or   1           17 or (33 or 25 MHz)
+  integratorap-pci         14      1           14
+
+  Integrator/CP            22      variable    Bit 8 0, rest variable
+  integratorcp-cm-core
+
+  Integrator/CP            22      variable    Bit 8 0, rest variable
+  integratorcp-cm-mem
+
+  The ICST oscillator must be provided inside a system controller node.
+
+properties:
+  "#clock-cells":
+    const: 0
+
+  compatible:
+    enum:
+      - arm,syscon-icst525
+      - arm,syscon-icst307
+      - arm,syscon-icst525-integratorap-cm
+      - arm,syscon-icst525-integratorap-sys
+      - arm,syscon-icst525-integratorap-pci
+      - arm,syscon-icst525-integratorcp-cm-core
+      - arm,syscon-icst525-integratorcp-cm-mem
+      - arm,integrator-cm-auxosc
+      - arm,versatile-cm-auxosc
+      - arm,impd-vco1
+      - arm,impd-vco2
+
+  clocks:
+    description: Parent clock for the ICST VCO
+    maxItems: 1
+
+  clock-output-names:
+    maxItems: 1
+
+  lock-offset:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: Offset to the unlocking register for the oscillator
+
+  vco-offset:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: Offset to the VCO register for the oscillator
+
+required:
+  - "#clock-cells"
+  - compatible
+  - clocks
+
+examples:
+  - |
+    vco1: clock@00 {
+      compatible = "arm,impd1-vco1";
+      #clock-cells = <0>;
+      lock-offset = <0x08>;
+      vco-offset = <0x00>;
+      clocks = <&sysclk>;
+      clock-output-names = "IM-PD1-VCO1";
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/clock/arm-integrator.txt b/Documentation/devicetree/bindings/clock/arm-integrator.txt
deleted file mode 100644 (file)
index 11f5f95..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-Clock bindings for ARM Integrator and Versatile Core Module clocks
-
-Auxiliary Oscillator Clock
-
-This is a configurable clock fed from a 24 MHz chrystal,
-used for generating e.g. video clocks. It is located on the
-core module and there is only one of these.
-
-This clock node *must* be a subnode of the core module, since
-it obtains the base address for it's address range from its
-parent node.
-
-
-Required properties:
-- compatible: must be "arm,integrator-cm-auxosc" or "arm,versatile-cm-auxosc"
-- #clock-cells: must be <0>
-
-Optional properties:
-- clocks: parent clock(s)
-
-Example:
-
-core-module@10000000 {
-       xtal24mhz: xtal24mhz@24M {
-               #clock-cells = <0>;
-               compatible = "fixed-clock";
-               clock-frequency = <24000000>;
-       };
-       auxosc: cm_aux_osc@25M {
-               #clock-cells = <0>;
-               compatible = "arm,integrator-cm-auxosc";
-               clocks = <&xtal24mhz>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/clock/arm-syscon-icst.txt b/Documentation/devicetree/bindings/clock/arm-syscon-icst.txt
deleted file mode 100644 (file)
index 4cd8174..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-ARM System Controller ICST clocks
-
-The ICS525 and ICS307 oscillators are produced by Integrated Devices
-Technology (IDT). ARM integrated these oscillators deeply into their
-reference designs by adding special control registers that manage such
-oscillators to their system controllers.
-
-The various ARM system controllers contain logic to serialize and initialize
-an ICST clock request after a write to the 32 bit register at an offset
-into the system controller. Furthermore, to even be able to alter one of
-these frequencies, the system controller must first be unlocked by
-writing a special token to another offset in the system controller.
-
-Some ARM hardware contain special versions of the serial interface that only
-connects the low 8 bits of the VDW (missing one bit), hardwires RDW to
-different values and sometimes also hardwire the output divider. They
-therefore have special compatible strings as per this table (the OD value is
-the value on the pins, not the resulting output divider):
-
-Hardware variant:        RDW     OD          VDW
-
-Integrator/AP            22      1           Bit 8 0, rest variable
-integratorap-cm
-
-Integrator/AP            46      3           Bit 8 0, rest variable
-integratorap-sys
-
-Integrator/AP            22 or   1           17 or (33 or 25 MHz)
-integratorap-pci         14      1           14
-
-Integrator/CP            22      variable    Bit 8 0, rest variable
-integratorcp-cm-core
-
-Integrator/CP            22      variable    Bit 8 0, rest variable
-integratorcp-cm-mem
-
-The ICST oscillator must be provided inside a system controller node.
-
-Required properties:
-- compatible: must be one of
-  "arm,syscon-icst525"
-  "arm,syscon-icst307"
-  "arm,syscon-icst525-integratorap-cm"
-  "arm,syscon-icst525-integratorap-sys"
-  "arm,syscon-icst525-integratorap-pci"
-  "arm,syscon-icst525-integratorcp-cm-core"
-  "arm,syscon-icst525-integratorcp-cm-mem"
-- lock-offset: the offset address into the system controller where the
-  unlocking register is located
-- vco-offset: the offset address into the system controller where the
-  ICST control register is located (even 32 bit address)
-- #clock-cells: must be <0>
-- clocks: parent clock, since the ICST needs a parent clock to derive its
-  frequency from, this attribute is compulsory.
-
-Example:
-
-syscon: syscon@10000000 {
-       compatible = "syscon";
-       reg = <0x10000000 0x1000>;
-
-       oscclk0: osc0@c {
-               compatible = "arm,syscon-icst307";
-               #clock-cells = <0>;
-               lock-offset = <0x20>;
-               vco-offset = <0x0c>;
-               clocks = <&xtal24mhz>;
-       };
-       (...)
-};
diff --git a/Documentation/devicetree/bindings/clock/marvell,mmp2-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,mmp2-clock.yaml
new file mode 100644 (file)
index 0000000..e2b6ac9
--- /dev/null
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/marvell,mmp2-clock.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell MMP2 and MMP3 Clock Controller
+
+maintainers:
+  - Lubomir Rintel <lkundrak@v3.sk>
+
+description: |
+  The clock subsystem on MMP2 or MMP3 generates and supplies clock to various
+  controllers within the SoC.
+
+  Each clock is assigned an identifier and client nodes use this identifier
+  to specify the clock which they consume.
+
+  All these identifiers could be found in <dt-bindings/clock/marvell,mmp2.h>.
+
+properties:
+  compatible:
+    enum:
+      - marvell,mmp2-clock # controller compatible with MMP2 SoC
+      - marvell,mmp3-clock # controller compatible with MMP3 SoC
+
+  reg:
+    items:
+      - description: MPMU register region
+      - description: APMU register region
+      - description: APBC register region
+
+  reg-names:
+    items:
+      - const: mpmu
+      - const: apmu
+      - const: apbc
+
+  '#clock-cells':
+    const: 1
+
+  '#reset-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - '#clock-cells'
+  - '#reset-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@d4050000 {
+      compatible = "marvell,mmp2-clock";
+      reg = <0xd4050000 0x1000>,
+            <0xd4282800 0x400>,
+            <0xd4015000 0x1000>;
+      reg-names = "mpmu", "apmu", "apbc";
+      #clock-cells = <1>;
+      #reset-cells = <1>;
+    };
diff --git a/Documentation/devicetree/bindings/clock/marvell,mmp2.txt b/Documentation/devicetree/bindings/clock/marvell,mmp2.txt
deleted file mode 100644 (file)
index 23b52dc..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-* Marvell MMP2 Clock Controller
-
-The MMP2 clock subsystem generates and supplies clock to various
-controllers within the MMP2 SoC.
-
-Required Properties:
-
-- compatible: should be one of the following.
-  - "marvell,mmp2-clock" - controller compatible with MMP2 SoC.
-
-- reg: physical base address of the clock subsystem and length of memory mapped
-  region. There are 3 places in SOC has clock control logic:
-  "mpmu", "apmu", "apbc". So three reg spaces need to be defined.
-
-- #clock-cells: should be 1.
-- #reset-cells: should be 1.
-
-Each clock is assigned an identifier and client nodes use this identifier
-to specify the clock which they consume.
-
-All these identifiers could be found in <dt-bindings/clock/marvell,mmp2.h>.
diff --git a/Documentation/devicetree/bindings/clock/ti,am654-ehrpwm-tbclk.yaml b/Documentation/devicetree/bindings/clock/ti,am654-ehrpwm-tbclk.yaml
new file mode 100644 (file)
index 0000000..869b18a
--- /dev/null
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/ti,am654-ehrpwm-tbclk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI EHRPWM Time Base Clock
+
+maintainers:
+  - Vignesh Raghavendra <vigneshr@ti.com>
+
+properties:
+  compatible:
+    items:
+      - const: ti,am654-ehrpwm-tbclk
+      - const: syscon
+
+  "#clock-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#clock-cells"
+  - reg
+
+examples:
+  - |
+    ehrpwm_tbclk: syscon@4140 {
+        compatible = "ti,am654-ehrpwm-tbclk", "syscon";
+        reg = <0x4140 0x18>;
+        #clock-cells = <1>;
+    };
index 38fe2f3f7b6f290e67168db75a1881cfbcc5be8b..04499e55218b9adcb0a6bf7d706e53fc0f24fdb2 100644 (file)
@@ -1280,7 +1280,7 @@ L:        linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     Documentation/devicetree/bindings/arm/arm-boards
 F:     Documentation/devicetree/bindings/auxdisplay/arm-charlcd.txt
-F:     Documentation/devicetree/bindings/clock/arm-integrator.txt
+F:     Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml
 F:     Documentation/devicetree/bindings/i2c/i2c-versatile.txt
 F:     Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
 F:     Documentation/devicetree/bindings/mtd/arm-versatile.txt
index 3732241352cea202c7ce94602638b7938074791f..8b90357f2a93cc66f6924423ff41855b3dce0c6a 100644 (file)
@@ -15,7 +15,11 @@ obj-$(CONFIG_HAVE_AT91_H32MX)                += clk-h32mx.o
 obj-$(CONFIG_HAVE_AT91_GENERATED_CLK)  += clk-generated.o
 obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK)    += clk-i2s-mux.o
 obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL)    += clk-sam9x60-pll.o
+obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o
 obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9g45.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9n12.o at91sam9x5.o
 obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
+obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o
 obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
 obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
diff --git a/drivers/clk/at91/at91rm9200.c b/drivers/clk/at91/at91rm9200.c
new file mode 100644 (file)
index 0000000..c44a431
--- /dev/null
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+struct sck {
+       char *n;
+       char *p;
+       u8 id;
+};
+
+struct pck {
+       char *n;
+       u8 id;
+};
+
+static const struct clk_master_characteristics rm9200_mck_characteristics = {
+       .output = { .min = 0, .max = 80000000 },
+       .divisors = { 1, 2, 3, 4 },
+};
+
+static u8 rm9200_pll_out[] = { 0, 2 };
+
+static const struct clk_range rm9200_pll_outputs[] = {
+       { .min = 80000000, .max = 160000000 },
+       { .min = 150000000, .max = 180000000 },
+};
+
+static const struct clk_pll_characteristics rm9200_pll_characteristics = {
+       .input = { .min = 1000000, .max = 32000000 },
+       .num_output = ARRAY_SIZE(rm9200_pll_outputs),
+       .output = rm9200_pll_outputs,
+       .out = rm9200_pll_out,
+};
+
+static const struct sck at91rm9200_systemck[] = {
+       { .n = "udpck", .p = "usbck",    .id = 2 },
+       { .n = "uhpck", .p = "usbck",    .id = 4 },
+       { .n = "pck0",  .p = "prog0",    .id = 8 },
+       { .n = "pck1",  .p = "prog1",    .id = 9 },
+       { .n = "pck2",  .p = "prog2",    .id = 10 },
+       { .n = "pck3",  .p = "prog3",    .id = 11 },
+};
+
+static const struct pck at91rm9200_periphck[] = {
+       { .n = "pioA_clk",   .id = 2 },
+       { .n = "pioB_clk",   .id = 3 },
+       { .n = "pioC_clk",   .id = 4 },
+       { .n = "pioD_clk",   .id = 5 },
+       { .n = "usart0_clk", .id = 6 },
+       { .n = "usart1_clk", .id = 7 },
+       { .n = "usart2_clk", .id = 8 },
+       { .n = "usart3_clk", .id = 9 },
+       { .n = "mci0_clk",   .id = 10 },
+       { .n = "udc_clk",    .id = 11 },
+       { .n = "twi0_clk",   .id = 12 },
+       { .n = "spi0_clk",   .id = 13 },
+       { .n = "ssc0_clk",   .id = 14 },
+       { .n = "ssc1_clk",   .id = 15 },
+       { .n = "ssc2_clk",   .id = 16 },
+       { .n = "tc0_clk",    .id = 17 },
+       { .n = "tc1_clk",    .id = 18 },
+       { .n = "tc2_clk",    .id = 19 },
+       { .n = "tc3_clk",    .id = 20 },
+       { .n = "tc4_clk",    .id = 21 },
+       { .n = "tc5_clk",    .id = 22 },
+       { .n = "ohci_clk",   .id = 23 },
+       { .n = "macb0_clk",  .id = 24 },
+};
+
+static void __init at91rm9200_pmc_setup(struct device_node *np)
+{
+       const char *slowxtal_name, *mainxtal_name;
+       struct pmc_data *at91rm9200_pmc;
+       u32 usb_div[] = { 1, 2, 0, 0 };
+       const char *parent_names[6];
+       struct regmap *regmap;
+       struct clk_hw *hw;
+       int i;
+       bool bypass;
+
+       i = of_property_match_string(np, "clock-names", "slow_xtal");
+       if (i < 0)
+               return;
+
+       slowxtal_name = of_clk_get_parent_name(np, i);
+
+       i = of_property_match_string(np, "clock-names", "main_xtal");
+       if (i < 0)
+               return;
+       mainxtal_name = of_clk_get_parent_name(np, i);
+
+       regmap = device_node_to_regmap(np);
+       if (IS_ERR(regmap))
+               return;
+
+       at91rm9200_pmc = pmc_data_allocate(PMC_MAIN + 1,
+                                           nck(at91rm9200_systemck),
+                                           nck(at91rm9200_periphck), 0);
+       if (!at91rm9200_pmc)
+               return;
+
+       bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+       hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+                                       bypass);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       at91rm9200_pmc->chws[PMC_MAIN] = hw;
+
+       hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+                                  &at91rm9200_pll_layout,
+                                  &rm9200_pll_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
+                                  &at91rm9200_pll_layout,
+                                  &rm9200_pll_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = slowxtal_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "pllack";
+       parent_names[3] = "pllbck";
+       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+                                     &at91rm9200_master_layout,
+                                     &rm9200_mck_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       at91rm9200_pmc->chws[PMC_MCK] = hw;
+
+       hw = at91rm9200_clk_register_usb(regmap, "usbck", "pllbck", usb_div);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = slowxtal_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "pllack";
+       parent_names[3] = "pllbck";
+       for (i = 0; i < 4; i++) {
+               char name[6];
+
+               snprintf(name, sizeof(name), "prog%d", i);
+
+               hw = at91_clk_register_programmable(regmap, name,
+                                                   parent_names, 4, i,
+                                                   &at91rm9200_programmable_layout);
+               if (IS_ERR(hw))
+                       goto err_free;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(at91rm9200_systemck); i++) {
+               hw = at91_clk_register_system(regmap, at91rm9200_systemck[i].n,
+                                             at91rm9200_systemck[i].p,
+                                             at91rm9200_systemck[i].id);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               at91rm9200_pmc->shws[at91rm9200_systemck[i].id] = hw;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(at91rm9200_periphck); i++) {
+               hw = at91_clk_register_peripheral(regmap,
+                                                 at91rm9200_periphck[i].n,
+                                                 "masterck",
+                                                 at91rm9200_periphck[i].id);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               at91rm9200_pmc->phws[at91rm9200_periphck[i].id] = hw;
+       }
+
+       of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91rm9200_pmc);
+
+       return;
+
+err_free:
+       pmc_data_free(at91rm9200_pmc);
+}
+/*
+ * While the TCB can be used as the clocksource, the system timer is most likely
+ * to be used instead. However, the pinctrl driver doesn't support probe
+ * deferring properly. Once this is fixed, this can be switched to a platform
+ * driver.
+ */
+CLK_OF_DECLARE_DRIVER(at91rm9200_pmc, "atmel,at91rm9200-pmc",
+                     at91rm9200_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9g45.c b/drivers/clk/at91/at91sam9g45.c
new file mode 100644 (file)
index 0000000..38a7d2d
--- /dev/null
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static const struct clk_master_characteristics mck_characteristics = {
+       .output = { .min = 0, .max = 133333333 },
+       .divisors = { 1, 2, 4, 3 },
+};
+
+static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
+
+static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
+
+static const struct clk_range plla_outputs[] = {
+       { .min = 745000000, .max = 800000000 },
+       { .min = 695000000, .max = 750000000 },
+       { .min = 645000000, .max = 700000000 },
+       { .min = 595000000, .max = 650000000 },
+       { .min = 545000000, .max = 600000000 },
+       { .min = 495000000, .max = 555000000 },
+       { .min = 445000000, .max = 500000000 },
+       { .min = 400000000, .max = 450000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+       .input = { .min = 2000000, .max = 32000000 },
+       .num_output = ARRAY_SIZE(plla_outputs),
+       .output = plla_outputs,
+       .icpll = plla_icpll,
+       .out = plla_out,
+};
+
+static const struct {
+       char *n;
+       char *p;
+       u8 id;
+} at91sam9g45_systemck[] = {
+       { .n = "ddrck", .p = "masterck", .id = 2 },
+       { .n = "uhpck", .p = "usbck",    .id = 6 },
+       { .n = "pck0",  .p = "prog0",    .id = 8 },
+       { .n = "pck1",  .p = "prog1",    .id = 9 },
+};
+
+static const struct clk_pcr_layout at91sam9g45_pcr_layout = {
+       .offset = 0x10c,
+       .cmd = BIT(12),
+       .pid_mask = GENMASK(5, 0),
+       .div_mask = GENMASK(17, 16),
+};
+
+struct pck {
+       char *n;
+       u8 id;
+};
+
+static const struct pck at91sam9g45_periphck[] = {
+       { .n = "pioA_clk",       .id = 2, },
+       { .n = "pioB_clk",       .id = 3, },
+       { .n = "pioC_clk",       .id = 4, },
+       { .n = "pioDE_clk",      .id = 5, },
+       { .n = "trng_clk",       .id = 6, },
+       { .n = "usart0_clk",     .id = 7, },
+       { .n = "usart1_clk",     .id = 8, },
+       { .n = "usart2_clk",     .id = 9, },
+       { .n = "usart3_clk",     .id = 10, },
+       { .n = "mci0_clk",       .id = 11, },
+       { .n = "twi0_clk",       .id = 12, },
+       { .n = "twi1_clk",       .id = 13, },
+       { .n = "spi0_clk",       .id = 14, },
+       { .n = "spi1_clk",       .id = 15, },
+       { .n = "ssc0_clk",       .id = 16, },
+       { .n = "ssc1_clk",       .id = 17, },
+       { .n = "tcb0_clk",       .id = 18, },
+       { .n = "pwm_clk",        .id = 19, },
+       { .n = "adc_clk",        .id = 20, },
+       { .n = "dma0_clk",       .id = 21, },
+       { .n = "uhphs_clk",      .id = 22, },
+       { .n = "lcd_clk",        .id = 23, },
+       { .n = "ac97_clk",       .id = 24, },
+       { .n = "macb0_clk",      .id = 25, },
+       { .n = "isi_clk",        .id = 26, },
+       { .n = "udphs_clk",      .id = 27, },
+       { .n = "aestdessha_clk", .id = 28, },
+       { .n = "mci1_clk",       .id = 29, },
+       { .n = "vdec_clk",       .id = 30, },
+};
+
+static void __init at91sam9g45_pmc_setup(struct device_node *np)
+{
+       const char *slck_name, *mainxtal_name;
+       struct pmc_data *at91sam9g45_pmc;
+       const char *parent_names[6];
+       struct regmap *regmap;
+       struct clk_hw *hw;
+       int i;
+       bool bypass;
+
+       i = of_property_match_string(np, "clock-names", "slow_clk");
+       if (i < 0)
+               return;
+
+       slck_name = of_clk_get_parent_name(np, i);
+
+       i = of_property_match_string(np, "clock-names", "main_xtal");
+       if (i < 0)
+               return;
+       mainxtal_name = of_clk_get_parent_name(np, i);
+
+       regmap = syscon_node_to_regmap(np);
+       if (IS_ERR(regmap))
+               return;
+
+       at91sam9g45_pmc = pmc_data_allocate(PMC_MAIN + 1,
+                                           nck(at91sam9g45_systemck),
+                                           nck(at91sam9g45_periphck), 0);
+       if (!at91sam9g45_pmc)
+               return;
+
+       bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+       hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+                                       bypass);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       at91sam9g45_pmc->chws[PMC_MAIN] = hw;
+
+       hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+                                  &at91rm9200_pll_layout, &plla_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       at91sam9g45_pmc->chws[PMC_UTMI] = hw;
+
+       parent_names[0] = slck_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "plladivck";
+       parent_names[3] = "utmick";
+       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+                                     &at91rm9200_master_layout,
+                                     &mck_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       at91sam9g45_pmc->chws[PMC_MCK] = hw;
+
+       parent_names[0] = "plladivck";
+       parent_names[1] = "utmick";
+       hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = slck_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "plladivck";
+       parent_names[3] = "utmick";
+       parent_names[4] = "masterck";
+       for (i = 0; i < 2; i++) {
+               char name[6];
+
+               snprintf(name, sizeof(name), "prog%d", i);
+
+               hw = at91_clk_register_programmable(regmap, name,
+                                                   parent_names, 5, i,
+                                                   &at91sam9g45_programmable_layout);
+               if (IS_ERR(hw))
+                       goto err_free;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(at91sam9g45_systemck); i++) {
+               hw = at91_clk_register_system(regmap, at91sam9g45_systemck[i].n,
+                                             at91sam9g45_systemck[i].p,
+                                             at91sam9g45_systemck[i].id);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               at91sam9g45_pmc->shws[at91sam9g45_systemck[i].id] = hw;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(at91sam9g45_periphck); i++) {
+               hw = at91_clk_register_peripheral(regmap,
+                                                 at91sam9g45_periphck[i].n,
+                                                 "masterck",
+                                                 at91sam9g45_periphck[i].id);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               at91sam9g45_pmc->phws[at91sam9g45_periphck[i].id] = hw;
+       }
+
+       of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9g45_pmc);
+
+       return;
+
+err_free:
+       pmc_data_free(at91sam9g45_pmc);
+}
+/*
+ * The TCB is used as the clocksource so its clock is needed early. This means
+ * this can't be a platform driver.
+ */
+CLK_OF_DECLARE_DRIVER(at91sam9g45_pmc, "atmel,at91sam9g45-pmc",
+                     at91sam9g45_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c
new file mode 100644 (file)
index 0000000..8bb39d2
--- /dev/null
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static const struct clk_master_characteristics mck_characteristics = {
+       .output = { .min = 0, .max = 133333333 },
+       .divisors = { 1, 2, 4, 3 },
+       .have_div3_pres = 1,
+};
+
+static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
+
+static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
+
+static const struct clk_range plla_outputs[] = {
+       { .min = 745000000, .max = 800000000 },
+       { .min = 695000000, .max = 750000000 },
+       { .min = 645000000, .max = 700000000 },
+       { .min = 595000000, .max = 650000000 },
+       { .min = 545000000, .max = 600000000 },
+       { .min = 495000000, .max = 555000000 },
+       { .min = 445000000, .max = 500000000 },
+       { .min = 400000000, .max = 450000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+       .input = { .min = 2000000, .max = 32000000 },
+       .num_output = ARRAY_SIZE(plla_outputs),
+       .output = plla_outputs,
+       .icpll = plla_icpll,
+       .out = plla_out,
+};
+
+static u8 pllb_out[] = { 0 };
+
+static const struct clk_range pllb_outputs[] = {
+       { .min = 30000000, .max = 100000000 },
+};
+
+static const struct clk_pll_characteristics pllb_characteristics = {
+       .input = { .min = 2000000, .max = 32000000 },
+       .num_output = ARRAY_SIZE(pllb_outputs),
+       .output = pllb_outputs,
+       .out = pllb_out,
+};
+
+static const struct {
+       char *n;
+       char *p;
+       u8 id;
+} at91sam9n12_systemck[] = {
+       { .n = "ddrck", .p = "masterck", .id = 2 },
+       { .n = "lcdck", .p = "masterck", .id = 3 },
+       { .n = "uhpck", .p = "usbck",    .id = 6 },
+       { .n = "udpck", .p = "usbck",    .id = 7 },
+       { .n = "pck0",  .p = "prog0",    .id = 8 },
+       { .n = "pck1",  .p = "prog1",    .id = 9 },
+};
+
+static const struct clk_pcr_layout at91sam9n12_pcr_layout = {
+       .offset = 0x10c,
+       .cmd = BIT(12),
+       .pid_mask = GENMASK(5, 0),
+       .div_mask = GENMASK(17, 16),
+};
+
+struct pck {
+       char *n;
+       u8 id;
+};
+
+static const struct pck at91sam9n12_periphck[] = {
+       { .n = "pioAB_clk",  .id = 2, },
+       { .n = "pioCD_clk",  .id = 3, },
+       { .n = "fuse_clk",   .id = 4, },
+       { .n = "usart0_clk", .id = 5, },
+       { .n = "usart1_clk", .id = 6, },
+       { .n = "usart2_clk", .id = 7, },
+       { .n = "usart3_clk", .id = 8, },
+       { .n = "twi0_clk",   .id = 9, },
+       { .n = "twi1_clk",   .id = 10, },
+       { .n = "mci0_clk",   .id = 12, },
+       { .n = "spi0_clk",   .id = 13, },
+       { .n = "spi1_clk",   .id = 14, },
+       { .n = "uart0_clk",  .id = 15, },
+       { .n = "uart1_clk",  .id = 16, },
+       { .n = "tcb_clk",    .id = 17, },
+       { .n = "pwm_clk",    .id = 18, },
+       { .n = "adc_clk",    .id = 19, },
+       { .n = "dma0_clk",   .id = 20, },
+       { .n = "uhphs_clk",  .id = 22, },
+       { .n = "udphs_clk",  .id = 23, },
+       { .n = "lcdc_clk",   .id = 25, },
+       { .n = "sha_clk",    .id = 27, },
+       { .n = "ssc0_clk",   .id = 28, },
+       { .n = "aes_clk",    .id = 29, },
+       { .n = "trng_clk",   .id = 30, },
+};
+
+static void __init at91sam9n12_pmc_setup(struct device_node *np)
+{
+       struct clk_range range = CLK_RANGE(0, 0);
+       const char *slck_name, *mainxtal_name;
+       struct pmc_data *at91sam9n12_pmc;
+       const char *parent_names[6];
+       struct regmap *regmap;
+       struct clk_hw *hw;
+       int i;
+       bool bypass;
+
+       i = of_property_match_string(np, "clock-names", "slow_clk");
+       if (i < 0)
+               return;
+
+       slck_name = of_clk_get_parent_name(np, i);
+
+       i = of_property_match_string(np, "clock-names", "main_xtal");
+       if (i < 0)
+               return;
+       mainxtal_name = of_clk_get_parent_name(np, i);
+
+       regmap = syscon_node_to_regmap(np);
+       if (IS_ERR(regmap))
+               return;
+
+       at91sam9n12_pmc = pmc_data_allocate(PMC_MAIN + 1,
+                                          nck(at91sam9n12_systemck), 31, 0);
+       if (!at91sam9n12_pmc)
+               return;
+
+       hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+                                          50000000);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+       hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+                                       bypass);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = "main_rc_osc";
+       parent_names[1] = "main_osc";
+       hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       at91sam9n12_pmc->chws[PMC_MAIN] = hw;
+
+       hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+                                  &at91rm9200_pll_layout, &plla_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
+                                  &at91rm9200_pll_layout, &pllb_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = slck_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "plladivck";
+       parent_names[3] = "pllbck";
+       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+                                     &at91sam9x5_master_layout,
+                                     &mck_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       at91sam9n12_pmc->chws[PMC_MCK] = hw;
+
+       hw = at91sam9n12_clk_register_usb(regmap, "usbck", "pllbck");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = slck_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "plladivck";
+       parent_names[3] = "pllbck";
+       parent_names[4] = "masterck";
+       for (i = 0; i < 2; i++) {
+               char name[6];
+
+               snprintf(name, sizeof(name), "prog%d", i);
+
+               hw = at91_clk_register_programmable(regmap, name,
+                                                   parent_names, 5, i,
+                                                   &at91sam9x5_programmable_layout);
+               if (IS_ERR(hw))
+                       goto err_free;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(at91sam9n12_systemck); i++) {
+               hw = at91_clk_register_system(regmap, at91sam9n12_systemck[i].n,
+                                             at91sam9n12_systemck[i].p,
+                                             at91sam9n12_systemck[i].id);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               at91sam9n12_pmc->shws[at91sam9n12_systemck[i].id] = hw;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(at91sam9n12_periphck); i++) {
+               hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+                                                        &at91sam9n12_pcr_layout,
+                                                        at91sam9n12_periphck[i].n,
+                                                        "masterck",
+                                                        at91sam9n12_periphck[i].id,
+                                                        &range);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               at91sam9n12_pmc->phws[at91sam9n12_periphck[i].id] = hw;
+       }
+
+       of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9n12_pmc);
+
+       return;
+
+err_free:
+       pmc_data_free(at91sam9n12_pmc);
+}
+/*
+ * The TCB is used as the clocksource so its clock is needed early. This means
+ * this can't be a platform driver.
+ */
+CLK_OF_DECLARE_DRIVER(at91sam9n12_pmc, "atmel,at91sam9n12-pmc",
+                     at91sam9n12_pmc_setup);
index 22aede42a3362b9880bbbc36a3730055bca6b0ff..31d5c45e30d7a375f1c48e0ff004aac4d6f122ef 100644 (file)
@@ -25,6 +25,7 @@ struct at91sam9x5_clk_usb {
        struct clk_hw hw;
        struct regmap *regmap;
        u32 usbs_mask;
+       u8 num_parents;
 };
 
 #define to_at91sam9x5_clk_usb(hw) \
@@ -75,6 +76,9 @@ static int at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw,
                        tmp_parent_rate = req->rate * div;
                        tmp_parent_rate = clk_hw_round_rate(parent,
                                                           tmp_parent_rate);
+                       if (!tmp_parent_rate)
+                               continue;
+
                        tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div);
                        if (tmp_rate < req->rate)
                                tmp_diff = req->rate - tmp_rate;
@@ -107,7 +111,7 @@ static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
 {
        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 
-       if (index > 1)
+       if (index >= usb->num_parents)
                return -EINVAL;
 
        regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
@@ -211,7 +215,8 @@ _at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
 
        usb->hw.init = &init;
        usb->regmap = regmap;
-       usb->usbs_mask = SAM9X5_USBS_MASK;
+       usb->usbs_mask = usbs_mask;
+       usb->num_parents = num_parents;
 
        hw = &usb->hw;
        ret = clk_hw_register(NULL, &usb->hw);
index 77398aefeb6db45396f23db2ecdf16c13c6f0769..cc19e8fb83bec717d3fd360928ed1088a9c2f06c 100644 (file)
@@ -124,7 +124,6 @@ static const struct {
        char *n;
        u8 id;
        struct clk_range r;
-       bool pll;
 } sam9x60_gck[] = {
        { .n = "flex0_gclk",  .id = 5, },
        { .n = "flex1_gclk",  .id = 6, },
@@ -144,11 +143,9 @@ static const struct {
        { .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, },
        { .n = "flex11_gclk", .id = 32, },
        { .n = "flex12_gclk", .id = 33, },
-       { .n = "i2s_gclk",    .id = 34, .r = { .min = 0, .max = 105000000 },
-               .pll = true, },
+       { .n = "i2s_gclk",    .id = 34, .r = { .min = 0, .max = 105000000 }, },
        { .n = "pit64b_gclk", .id = 37, },
-       { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 },
-               .pll = true, },
+       { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, },
        { .n = "tcb1_gclk",   .id = 45, },
        { .n = "dbgu_gclk",   .id = 47, },
 };
@@ -237,9 +234,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
 
        parent_names[0] = "pllack";
        parent_names[1] = "upllck";
-       parent_names[2] = "mainck";
-       parent_names[3] = "mainck";
-       hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 4);
+       parent_names[2] = "main_osc";
+       hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -290,7 +286,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
                                                 sam9x60_gck[i].n,
                                                 parent_names, 6,
                                                 sam9x60_gck[i].id,
-                                                sam9x60_gck[i].pll,
+                                                false,
                                                 &sam9x60_gck[i].r);
                if (IS_ERR(hw))
                        goto err_free;
diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
new file mode 100644 (file)
index 0000000..88506f9
--- /dev/null
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static const struct clk_master_characteristics mck_characteristics = {
+       .output = { .min = 0, .max = 166000000 },
+       .divisors = { 1, 2, 4, 3 },
+};
+
+static u8 plla_out[] = { 0 };
+
+static u16 plla_icpll[] = { 0 };
+
+static const struct clk_range plla_outputs[] = {
+       { .min = 400000000, .max = 1000000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+       .input = { .min = 8000000, .max = 50000000 },
+       .num_output = ARRAY_SIZE(plla_outputs),
+       .output = plla_outputs,
+       .icpll = plla_icpll,
+       .out = plla_out,
+};
+
+static const struct clk_pcr_layout sama5d3_pcr_layout = {
+       .offset = 0x10c,
+       .cmd = BIT(12),
+       .pid_mask = GENMASK(6, 0),
+       .div_mask = GENMASK(17, 16),
+};
+
+static const struct {
+       char *n;
+       char *p;
+       u8 id;
+} sama5d3_systemck[] = {
+       { .n = "ddrck", .p = "masterck", .id = 2 },
+       { .n = "lcdck", .p = "masterck", .id = 3 },
+       { .n = "smdck", .p = "smdclk",   .id = 4 },
+       { .n = "uhpck", .p = "usbck",    .id = 6 },
+       { .n = "udpck", .p = "usbck",    .id = 7 },
+       { .n = "pck0",  .p = "prog0",    .id = 8 },
+       { .n = "pck1",  .p = "prog1",    .id = 9 },
+       { .n = "pck2",  .p = "prog2",    .id = 10 },
+};
+
+static const struct {
+       char *n;
+       u8 id;
+       struct clk_range r;
+} sama5d3_periphck[] = {
+       { .n = "dbgu_clk", .id = 2, },
+       { .n = "hsmc_clk", .id = 5, },
+       { .n = "pioA_clk", .id = 6, },
+       { .n = "pioB_clk", .id = 7, },
+       { .n = "pioC_clk", .id = 8, },
+       { .n = "pioD_clk", .id = 9, },
+       { .n = "pioE_clk", .id = 10, },
+       { .n = "usart0_clk", .id = 12, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "usart1_clk", .id = 13, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "usart2_clk", .id = 14, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "usart3_clk", .id = 15, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "uart0_clk", .id = 16, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "uart1_clk", .id = 17, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "twi0_clk", .id = 18, .r = { .min = 0, .max = 41500000 }, },
+       { .n = "twi1_clk", .id = 19, .r = { .min = 0, .max = 41500000 }, },
+       { .n = "twi2_clk", .id = 20, .r = { .min = 0, .max = 41500000 }, },
+       { .n = "mci0_clk", .id = 21, },
+       { .n = "mci1_clk", .id = 22, },
+       { .n = "mci2_clk", .id = 23, },
+       { .n = "spi0_clk", .id = 24, .r = { .min = 0, .max = 166000000 }, },
+       { .n = "spi1_clk", .id = 25, .r = { .min = 0, .max = 166000000 }, },
+       { .n = "tcb0_clk", .id = 26, .r = { .min = 0, .max = 166000000 }, },
+       { .n = "tcb1_clk", .id = 27, .r = { .min = 0, .max = 166000000 }, },
+       { .n = "pwm_clk", .id = 28, },
+       { .n = "adc_clk", .id = 29, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "dma0_clk", .id = 30, },
+       { .n = "dma1_clk", .id = 31, },
+       { .n = "uhphs_clk", .id = 32, },
+       { .n = "udphs_clk", .id = 33, },
+       { .n = "macb0_clk", .id = 34, },
+       { .n = "macb1_clk", .id = 35, },
+       { .n = "lcdc_clk", .id = 36, },
+       { .n = "isi_clk", .id = 37, },
+       { .n = "ssc0_clk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "ssc1_clk", .id = 39, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "can0_clk", .id = 40, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "can1_clk", .id = 41, .r = { .min = 0, .max = 83000000 }, },
+       { .n = "sha_clk", .id = 42, },
+       { .n = "aes_clk", .id = 43, },
+       { .n = "tdes_clk", .id = 44, },
+       { .n = "trng_clk", .id = 45, },
+       { .n = "fuse_clk", .id = 48, },
+       { .n = "mpddr_clk", .id = 49, },
+};
+
+static void __init sama5d3_pmc_setup(struct device_node *np)
+{
+       const char *slck_name, *mainxtal_name;
+       struct pmc_data *sama5d3_pmc;
+       const char *parent_names[5];
+       struct regmap *regmap;
+       struct clk_hw *hw;
+       int i;
+       bool bypass;
+
+       i = of_property_match_string(np, "clock-names", "slow_clk");
+       if (i < 0)
+               return;
+
+       slck_name = of_clk_get_parent_name(np, i);
+
+       i = of_property_match_string(np, "clock-names", "main_xtal");
+       if (i < 0)
+               return;
+       mainxtal_name = of_clk_get_parent_name(np, i);
+
+       regmap = syscon_node_to_regmap(np);
+       if (IS_ERR(regmap))
+               return;
+
+       sama5d3_pmc = pmc_data_allocate(PMC_MAIN + 1,
+                                       nck(sama5d3_systemck),
+                                       nck(sama5d3_periphck), 0);
+       if (!sama5d3_pmc)
+               return;
+
+       hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+                                          50000000);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+       hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+                                       bypass);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = "main_rc_osc";
+       parent_names[1] = "main_osc";
+       hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+                                  &sama5d3_pll_layout, &plla_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+       if (IS_ERR(hw))
+               goto err_free;
+
+       sama5d3_pmc->chws[PMC_UTMI] = hw;
+
+       parent_names[0] = slck_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "plladivck";
+       parent_names[3] = "utmick";
+       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+                                     &at91sam9x5_master_layout,
+                                     &mck_characteristics);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       sama5d3_pmc->chws[PMC_MCK] = hw;
+
+       parent_names[0] = "plladivck";
+       parent_names[1] = "utmick";
+       hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       parent_names[0] = slck_name;
+       parent_names[1] = "mainck";
+       parent_names[2] = "plladivck";
+       parent_names[3] = "utmick";
+       parent_names[4] = "masterck";
+       for (i = 0; i < 3; i++) {
+               char name[6];
+
+               snprintf(name, sizeof(name), "prog%d", i);
+
+               hw = at91_clk_register_programmable(regmap, name,
+                                                   parent_names, 5, i,
+                                                   &at91sam9x5_programmable_layout);
+               if (IS_ERR(hw))
+                       goto err_free;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(sama5d3_systemck); i++) {
+               hw = at91_clk_register_system(regmap, sama5d3_systemck[i].n,
+                                             sama5d3_systemck[i].p,
+                                             sama5d3_systemck[i].id);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               sama5d3_pmc->shws[sama5d3_systemck[i].id] = hw;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(sama5d3_periphck); i++) {
+               hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+                                                        &sama5d3_pcr_layout,
+                                                        sama5d3_periphck[i].n,
+                                                        "masterck",
+                                                        sama5d3_periphck[i].id,
+                                                        &sama5d3_periphck[i].r);
+               if (IS_ERR(hw))
+                       goto err_free;
+
+               sama5d3_pmc->phws[sama5d3_periphck[i].id] = hw;
+       }
+
+       of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d3_pmc);
+
+       return;
+
+err_free:
+       pmc_data_free(sama5d3_pmc);
+}
+/*
+ * The TCB is used as the clocksource so its clock is needed early. This means
+ * this can't be a platform driver.
+ */
+CLK_OF_DECLARE_DRIVER(sama5d3_pmc, "atmel,sama5d3-pmc", sama5d3_pmc_setup);
index 766c3e58becce99a9f7c4fb573e471bbcf40d7e1..f2c1377ef32401c5009195f127bd2c42c2a3d3a2 100644 (file)
@@ -774,7 +774,7 @@ static void clk_core_rate_restore_protect(struct clk_core *core, int count)
  * clk_rate_exclusive_get - get exclusivity over the clk rate control
  * @clk: the clk over which the exclusity of rate control is requested
  *
- * clk_rate_exlusive_get() begins a critical section during which a clock
+ * clk_rate_exclusive_get() begins a critical section during which a clock
  * consumer cannot tolerate any other consumer making any operation on the
  * clock which could result in a rate change or rate glitch. Exclusive clocks
  * cannot have their rate changed, either directly or indirectly due to changes
index 956dd653a43d22a4cc6975af01db626a75adcbca..c051ecba5cf8e7079c7811d8b7b98e1d7502696b 100644 (file)
@@ -432,8 +432,10 @@ static void __init jz4770_cgu_init(struct device_node *np)
 
        cgu = ingenic_cgu_new(jz4770_cgu_clocks,
                              ARRAY_SIZE(jz4770_cgu_clocks), np);
-       if (!cgu)
+       if (!cgu) {
                pr_err("%s: failed to initialise CGU\n", __func__);
+               return;
+       }
 
        retval = ingenic_cgu_register_clocks(cgu);
        if (retval)
index ea905ff72bf03492b2dc5201509d42cbf2711d61..c758f16430677a22c75399774b3520e3d6e04765 100644 (file)
@@ -9,14 +9,16 @@
 #include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/of.h>
+
 #include <dt-bindings/clock/jz4780-cgu.h>
 #include "cgu.h"
 #include "pm.h"
 
 /* CGU register offsets */
 #define CGU_REG_CLOCKCONTROL   0x00
-#define CGU_REG_PLLCONTROL     0x0c
+#define CGU_REG_LCR                    0x04
 #define CGU_REG_APLL           0x10
 #define CGU_REG_MPLL           0x14
 #define CGU_REG_EPLL           0x18
@@ -46,8 +48,8 @@
 #define CGU_REG_CLOCKSTATUS    0xd4
 
 /* bits within the OPCR register */
-#define OPCR_SPENDN0           (1 << 7)
-#define OPCR_SPENDN1           (1 << 6)
+#define OPCR_SPENDN0           BIT(7)
+#define OPCR_SPENDN1           BIT(6)
 
 /* bits within the USBPCR register */
 #define USBPCR_USB_MODE                BIT(31)
 #define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT)
 #define USBVBFIL_USBVBFIL_MASK (0xffff)
 
+/* bits within the LCR register */
+#define LCR_PD_SCPU                    BIT(31)
+#define LCR_SCPUS                      BIT(27)
+
+/* bits within the CLKGR1 register */
+#define CLKGR1_CORE1           BIT(15)
+
 static struct ingenic_cgu *cgu;
 
 static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw)
@@ -205,6 +214,42 @@ static const struct clk_ops jz4780_otg_phy_ops = {
        .set_rate = jz4780_otg_phy_set_rate,
 };
 
+static int jz4780_core1_enable(struct clk_hw *hw)
+{
+       struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+       struct ingenic_cgu *cgu = ingenic_clk->cgu;
+       const unsigned int timeout = 5000;
+       unsigned long flags;
+       int retval;
+       u32 lcr, clkgr1;
+
+       spin_lock_irqsave(&cgu->lock, flags);
+
+       lcr = readl(cgu->base + CGU_REG_LCR);
+       lcr &= ~LCR_PD_SCPU;
+       writel(lcr, cgu->base + CGU_REG_LCR);
+
+       clkgr1 = readl(cgu->base + CGU_REG_CLKGR1);
+       clkgr1 &= ~CLKGR1_CORE1;
+       writel(clkgr1, cgu->base + CGU_REG_CLKGR1);
+
+       spin_unlock_irqrestore(&cgu->lock, flags);
+
+       /* wait for the CPU to be powered up */
+       retval = readl_poll_timeout(cgu->base + CGU_REG_LCR, lcr,
+                                !(lcr & LCR_SCPUS), 10, timeout);
+       if (retval == -ETIMEDOUT) {
+               pr_err("%s: Wait for power up core1 timeout\n", __func__);
+               return retval;
+       }
+
+       return 0;
+}
+
+static const struct clk_ops jz4780_core1_ops = {
+       .enable = jz4780_core1_enable,
+};
+
 static const s8 pll_od_encoding[16] = {
        0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
        0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
@@ -699,9 +744,9 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = {
        },
 
        [JZ4780_CLK_CORE1] = {
-               "core1", CGU_CLK_GATE,
+               "core1", CGU_CLK_CUSTOM,
                .parents = { JZ4780_CLK_CPU, -1, -1, -1 },
-               .gate = { CGU_REG_CLKGR1, 15 },
+               .custom = { &jz4780_core1_ops },
        },
 
 };
index ad7daa494fd4a6c5acc0c6542a739b75442eab94..153a954b0d2fd7c492570ac5d97cba75bf665a24 100644 (file)
@@ -189,7 +189,7 @@ static long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate,
        u8 prescale;
 
        if (req_rate > rate)
-               return -EINVAL;
+               return rate;
 
        prescale = ingenic_tcu_get_prescale(rate, req_rate);
 
@@ -317,10 +317,17 @@ static const struct ingenic_soc_info jz4770_soc_info = {
        .has_tcu_clk = false,
 };
 
+static const struct ingenic_soc_info x1000_soc_info = {
+       .num_channels = 8,
+       .has_ost = false, /* X1000 has OST, but it not belong TCU */
+       .has_tcu_clk = false,
+};
+
 static const struct of_device_id ingenic_tcu_of_match[] __initconst = {
        { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, },
        { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, },
        { .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, },
+       { .compatible = "ingenic,x1000-tcu", .data = &x1000_soc_info, },
        { /* sentinel */ }
 };
 
@@ -471,3 +478,4 @@ static void __init ingenic_tcu_init(struct device_node *np)
 CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);
 CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init);
 CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init);
+CLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-tcu", ingenic_tcu_init);
index 38aeefb1e80808281d3d5110cd563c8ded9f94df..ab613f28b50282c66d9041b11b07a4327df67885 100644 (file)
@@ -26,3 +26,11 @@ config TI_SCI_CLK_PROBE_FROM_FW
          This is mostly only useful for debugging purposes, and will
          increase the boot time of the device. If you want the clocks probed
          from firmware, say Y. Otherwise, say N.
+
+config TI_SYSCON_CLK
+       tristate "Syscon based clock driver for K2/K3 SoCs"
+       depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
+       default ARCH_KEYSTONE || ARCH_K3
+       help
+         This adds clock driver support for syscon based gate
+         clocks on TI's K2 and K3 SoCs.
index d044de6f965cd29fd1b855dfdf05c3028bc4dd19..0e426e648f7cd2f525aa3d70752e865941607ee6 100644 (file)
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_COMMON_CLK_KEYSTONE)      += pll.o gate.o
 obj-$(CONFIG_TI_SCI_CLK)               += sci-clk.o
+obj-$(CONFIG_TI_SYSCON_CLK)            += syscon-clk.o
diff --git a/drivers/clk/keystone/syscon-clk.c b/drivers/clk/keystone/syscon-clk.c
new file mode 100644 (file)
index 0000000..8d7dbea
--- /dev/null
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct ti_syscon_gate_clk_priv {
+       struct clk_hw hw;
+       struct regmap *regmap;
+       u32 reg;
+       u32 idx;
+};
+
+struct ti_syscon_gate_clk_data {
+       char *name;
+       u32 offset;
+       u32 bit_idx;
+};
+
+static struct
+ti_syscon_gate_clk_priv *to_ti_syscon_gate_clk_priv(struct clk_hw *hw)
+{
+       return container_of(hw, struct ti_syscon_gate_clk_priv, hw);
+}
+
+static int ti_syscon_gate_clk_enable(struct clk_hw *hw)
+{
+       struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
+
+       return regmap_write_bits(priv->regmap, priv->reg, priv->idx,
+                                priv->idx);
+}
+
+static void ti_syscon_gate_clk_disable(struct clk_hw *hw)
+{
+       struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
+
+       regmap_write_bits(priv->regmap, priv->reg, priv->idx, 0);
+}
+
+static int ti_syscon_gate_clk_is_enabled(struct clk_hw *hw)
+{
+       unsigned int val;
+       struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
+
+       regmap_read(priv->regmap, priv->reg, &val);
+
+       return !!(val & priv->idx);
+}
+
+static const struct clk_ops ti_syscon_gate_clk_ops = {
+       .enable         = ti_syscon_gate_clk_enable,
+       .disable        = ti_syscon_gate_clk_disable,
+       .is_enabled     = ti_syscon_gate_clk_is_enabled,
+};
+
+static struct clk_hw
+*ti_syscon_gate_clk_register(struct device *dev, struct regmap *regmap,
+                            const struct ti_syscon_gate_clk_data *data)
+{
+       struct ti_syscon_gate_clk_priv *priv;
+       struct clk_init_data init;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = data->name;
+       init.ops = &ti_syscon_gate_clk_ops;
+       init.parent_names = NULL;
+       init.num_parents = 0;
+       init.flags = 0;
+
+       priv->regmap = regmap;
+       priv->reg = data->offset;
+       priv->idx = BIT(data->bit_idx);
+       priv->hw.init = &init;
+
+       ret = devm_clk_hw_register(dev, &priv->hw);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return &priv->hw;
+}
+
+static int ti_syscon_gate_clk_probe(struct platform_device *pdev)
+{
+       const struct ti_syscon_gate_clk_data *data, *p;
+       struct clk_hw_onecell_data *hw_data;
+       struct device *dev = &pdev->dev;
+       struct regmap *regmap;
+       int num_clks, i;
+
+       data = device_get_match_data(dev);
+       if (!data)
+               return -EINVAL;
+
+       regmap = syscon_node_to_regmap(dev->of_node);
+       if (IS_ERR(regmap)) {
+               if (PTR_ERR(regmap) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               dev_err(dev, "failed to find parent regmap\n");
+               return PTR_ERR(regmap);
+       }
+
+       num_clks = 0;
+       for (p = data; p->name; p++)
+               num_clks++;
+
+       hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks),
+                              GFP_KERNEL);
+       if (!hw_data)
+               return -ENOMEM;
+
+       hw_data->num = num_clks;
+
+       for (i = 0; i < num_clks; i++) {
+               hw_data->hws[i] = ti_syscon_gate_clk_register(dev, regmap,
+                                                             &data[i]);
+               if (IS_ERR(hw_data->hws[i]))
+                       dev_warn(dev, "failed to register %s\n",
+                                data[i].name);
+       }
+
+       return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+                                          hw_data);
+}
+
+#define TI_SYSCON_CLK_GATE(_name, _offset, _bit_idx)   \
+       {                                               \
+               .name = _name,                          \
+               .offset = (_offset),                    \
+               .bit_idx = (_bit_idx),                  \
+       }
+
+static const struct ti_syscon_gate_clk_data am654_clk_data[] = {
+       TI_SYSCON_CLK_GATE("ehrpwm_tbclk0", 0x0, 0),
+       TI_SYSCON_CLK_GATE("ehrpwm_tbclk1", 0x4, 0),
+       TI_SYSCON_CLK_GATE("ehrpwm_tbclk2", 0x8, 0),
+       TI_SYSCON_CLK_GATE("ehrpwm_tbclk3", 0xc, 0),
+       TI_SYSCON_CLK_GATE("ehrpwm_tbclk4", 0x10, 0),
+       TI_SYSCON_CLK_GATE("ehrpwm_tbclk5", 0x14, 0),
+       { /* Sentinel */ },
+};
+
+static const struct of_device_id ti_syscon_gate_clk_ids[] = {
+       {
+               .compatible = "ti,am654-ehrpwm-tbclk",
+               .data = &am654_clk_data,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ti_syscon_gate_clk_ids);
+
+static struct platform_driver ti_syscon_gate_clk_driver = {
+       .probe = ti_syscon_gate_clk_probe,
+       .driver = {
+               .name = "ti-syscon-gate-clk",
+               .of_match_table = ti_syscon_gate_clk_ids,
+       },
+};
+module_platform_driver(ti_syscon_gate_clk_driver);
+
+MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
+MODULE_DESCRIPTION("Syscon backed gate-clock driver");
+MODULE_LICENSE("GPL");
index acc141adf087c4704eeb2a8aeeb7d3cc0eff1938..14dc8a8a9d087d2de9642759d20067adb5e95f8b 100644 (file)
@@ -8,7 +8,7 @@ obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mix.o clk-gate.o clk.o
 obj-$(CONFIG_RESET_CONTROLLER) += reset.o
 
 obj-$(CONFIG_MACH_MMP_DT) += clk-of-pxa168.o clk-of-pxa910.o
-obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o
+obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o clk-pll.o
 
 obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
 obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
index d2cd36c54474f635f7273ec43f7ccd60a66fbf65..7a351ec65564ee4f422c44963fa92d1b9ebb930e 100644 (file)
@@ -441,7 +441,7 @@ const struct clk_ops mmp_clk_mix_ops = {
 
 struct clk *mmp_clk_register_mix(struct device *dev,
                                        const char *name,
-                                       const char **parent_names,
+                                       const char * const *parent_names,
                                        u8 num_parents,
                                        unsigned long flags,
                                        struct mmp_clk_mix_config *config,
index 6e71591e63a00d32269c24c02ccac62d6598fe2d..52dc8b43acd9ad5213297f4b1ee8eed286038f62 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2012 Marvell
  * Chao Xie <xiechao.mail@gmail.com>
+ * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
  *
  * This file is licensed under the terms of the GNU General Public
  * License version 2. This program is licensed "as is" without any
 #define APMU_SDH1      0x58
 #define APMU_SDH2      0xe8
 #define APMU_SDH3      0xec
+#define APMU_SDH4      0x15c
 #define APMU_USB       0x5c
 #define APMU_DISP0     0x4c
 #define APMU_DISP1     0x110
 #define APMU_CCIC0     0x50
 #define APMU_CCIC1     0xf4
+#define APBC_THERMAL0  0x90
+#define APBC_THERMAL1  0x98
+#define APBC_THERMAL2  0x9c
+#define APBC_THERMAL3  0xa0
 #define APMU_USBHSIC0  0xf8
 #define APMU_USBHSIC1  0xfc
-#define MPMU_UART_PLL  0x14
+#define APMU_GPU       0xcc
+
+#define MPMU_FCCR              0x8
+#define MPMU_POSR              0x10
+#define MPMU_UART_PLL          0x14
+#define MPMU_PLL2_CR           0x34
+/* MMP3 specific below */
+#define MPMU_PLL3_CR           0x50
+#define MPMU_PLL3_CTRL1                0x58
+#define MPMU_PLL1_CTRL         0x5c
+#define MPMU_PLL_DIFF_CTRL     0x68
+#define MPMU_PLL2_CTRL1                0x414
+
+enum mmp2_clk_model {
+       CLK_MODEL_MMP2,
+       CLK_MODEL_MMP3,
+};
 
 struct mmp2_clk_unit {
        struct mmp_clk_unit unit;
+       enum mmp2_clk_model model;
        void __iomem *mpmu_base;
        void __iomem *apmu_base;
        void __iomem *apbc_base;
@@ -67,11 +90,22 @@ struct mmp2_clk_unit {
 static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = {
        {MMP2_CLK_CLK32, "clk32", NULL, 0, 32768},
        {MMP2_CLK_VCTCXO, "vctcxo", NULL, 0, 26000000},
-       {MMP2_CLK_PLL1, "pll1", NULL, 0, 800000000},
-       {MMP2_CLK_PLL2, "pll2", NULL, 0, 960000000},
        {MMP2_CLK_USB_PLL, "usb_pll", NULL, 0, 480000000},
 };
 
+static struct mmp_param_pll_clk pll_clks[] = {
+       {MMP2_CLK_PLL1,   "pll1",   797330000, MPMU_FCCR,          0x4000, MPMU_POSR,     0},
+       {MMP2_CLK_PLL2,   "pll2",           0, MPMU_PLL2_CR,       0x0300, MPMU_PLL2_CR, 10},
+};
+
+static struct mmp_param_pll_clk mmp3_pll_clks[] = {
+       {MMP2_CLK_PLL2,   "pll1",   797330000, MPMU_FCCR,          0x4000, MPMU_POSR,     0,      26000000, MPMU_PLL1_CTRL,      25},
+       {MMP2_CLK_PLL2,   "pll2",           0, MPMU_PLL2_CR,       0x0300, MPMU_PLL2_CR, 10,      26000000, MPMU_PLL2_CTRL1,     25},
+       {MMP3_CLK_PLL1_P, "pll1_p",         0, MPMU_PLL_DIFF_CTRL, 0x0010, 0,             0,     797330000, MPMU_PLL_DIFF_CTRL,   0},
+       {MMP3_CLK_PLL2_P, "pll2_p",         0, MPMU_PLL_DIFF_CTRL, 0x0100, MPMU_PLL2_CR, 10,      26000000, MPMU_PLL_DIFF_CTRL,   5},
+       {MMP3_CLK_PLL3,   "pll3",           0, MPMU_PLL3_CR,       0x0300, MPMU_PLL3_CR, 10,      26000000, MPMU_PLL3_CTRL1,     25},
+};
+
 static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
        {MMP2_CLK_PLL1_2, "pll1_2", "pll1", 1, 2, 0},
        {MMP2_CLK_PLL1_4, "pll1_4", "pll1_2", 1, 2, 0},
@@ -113,6 +147,16 @@ static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit)
        mmp_register_fixed_rate_clks(unit, fixed_rate_clks,
                                        ARRAY_SIZE(fixed_rate_clks));
 
+       if (pxa_unit->model == CLK_MODEL_MMP3) {
+               mmp_register_pll_clks(unit, mmp3_pll_clks,
+                                       pxa_unit->mpmu_base,
+                                       ARRAY_SIZE(mmp3_pll_clks));
+       } else {
+               mmp_register_pll_clks(unit, pll_clks,
+                                       pxa_unit->mpmu_base,
+                                       ARRAY_SIZE(pll_clks));
+       }
+
        mmp_register_fixed_factor_clks(unit, fixed_factor_clks,
                                        ARRAY_SIZE(fixed_factor_clks));
 
@@ -127,16 +171,16 @@ static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit)
 static DEFINE_SPINLOCK(uart0_lock);
 static DEFINE_SPINLOCK(uart1_lock);
 static DEFINE_SPINLOCK(uart2_lock);
-static const char *uart_parent_names[] = {"uart_pll", "vctcxo"};
+static const char * const uart_parent_names[] = {"uart_pll", "vctcxo"};
 
 static DEFINE_SPINLOCK(ssp0_lock);
 static DEFINE_SPINLOCK(ssp1_lock);
 static DEFINE_SPINLOCK(ssp2_lock);
 static DEFINE_SPINLOCK(ssp3_lock);
-static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
+static const char * const ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
 
 static DEFINE_SPINLOCK(timer_lock);
-static const char *timer_parent_names[] = {"clk32", "vctcxo_4", "vctcxo_2", "vctcxo"};
+static const char * const timer_parent_names[] = {"clk32", "vctcxo_4", "vctcxo_2", "vctcxo"};
 
 static DEFINE_SPINLOCK(reset_lock);
 
@@ -176,6 +220,13 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = {
        {MMP2_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock},
        {MMP2_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x7, 0x3, 0x0, 0, &ssp3_lock},
        {MMP2_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x7, 0x3, 0x0, 0, &timer_lock},
+       {MMP2_CLK_THERMAL0, "thermal0_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL0, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
+};
+
+static struct mmp_param_gate_clk mmp3_apbc_gate_clks[] = {
+       {MMP3_CLK_THERMAL1, "thermal1_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL1, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
+       {MMP3_CLK_THERMAL2, "thermal2_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL2, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
+       {MMP3_CLK_THERMAL3, "thermal3_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_THERMAL3, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock},
 };
 
 static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
@@ -187,10 +238,15 @@ static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
 
        mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base,
                                ARRAY_SIZE(apbc_gate_clks));
+
+       if (pxa_unit->model == CLK_MODEL_MMP3) {
+               mmp_register_gate_clks(unit, mmp3_apbc_gate_clks, pxa_unit->apbc_base,
+                                       ARRAY_SIZE(mmp3_apbc_gate_clks));
+       }
 }
 
 static DEFINE_SPINLOCK(sdh_lock);
-static const char *sdh_parent_names[] = {"pll1_4", "pll2", "usb_pll", "pll1"};
+static const char * const sdh_parent_names[] = {"pll1_4", "pll2", "usb_pll", "pll1"};
 static struct mmp_clk_mix_config sdh_mix_config = {
        .reg_info = DEFINE_MIX_REG_INFO(4, 10, 2, 8, 32),
 };
@@ -201,11 +257,20 @@ static DEFINE_SPINLOCK(usbhsic1_lock);
 
 static DEFINE_SPINLOCK(disp0_lock);
 static DEFINE_SPINLOCK(disp1_lock);
-static const char *disp_parent_names[] = {"pll1", "pll1_16", "pll2", "vctcxo"};
+static const char * const disp_parent_names[] = {"pll1", "pll1_16", "pll2", "vctcxo"};
 
 static DEFINE_SPINLOCK(ccic0_lock);
 static DEFINE_SPINLOCK(ccic1_lock);
-static const char *ccic_parent_names[] = {"pll1_2", "pll1_16", "vctcxo"};
+static const char * const ccic_parent_names[] = {"pll1_2", "pll1_16", "vctcxo"};
+
+static DEFINE_SPINLOCK(gpu_lock);
+static const char * const mmp2_gpu_gc_parent_names[] =  {"pll1_2", "pll1_3", "pll2_2", "pll2_3", "pll2", "usb_pll"};
+static u32 mmp2_gpu_gc_parent_table[] =          { 0x0000,   0x0040,   0x0080,   0x00c0,   0x1000, 0x1040   };
+static const char * const mmp2_gpu_bus_parent_names[] = {"pll1_4", "pll2",   "pll2_2", "usb_pll"};
+static u32 mmp2_gpu_bus_parent_table[] =         { 0x0000,   0x0020,   0x0030,   0x4020   };
+static const char * const mmp3_gpu_bus_parent_names[] = {"pll1_4", "pll1_6", "pll1_2", "pll2_2"};
+static const char * const mmp3_gpu_gc_parent_names[] =  {"pll1",   "pll2",   "pll1_p", "pll2_p"};
+
 static struct mmp_clk_mix_config ccic0_mix_config = {
        .reg_info = DEFINE_MIX_REG_INFO(4, 17, 2, 6, 32),
 };
@@ -218,6 +283,15 @@ static struct mmp_param_mux_clk apmu_mux_clks[] = {
        {MMP2_CLK_DISP1_MUX, "disp1_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP1, 6, 2, 0, &disp1_lock},
 };
 
+static struct mmp_param_mux_clk mmp3_apmu_mux_clks[] = {
+       {0, "gpu_bus_mux", mmp3_gpu_bus_parent_names, ARRAY_SIZE(mmp3_gpu_bus_parent_names),
+                                                                       CLK_SET_RATE_PARENT, APMU_GPU, 4, 2, 0, &gpu_lock},
+       {0, "gpu_3d_mux", mmp3_gpu_gc_parent_names, ARRAY_SIZE(mmp3_gpu_gc_parent_names),
+                                                                       CLK_SET_RATE_PARENT, APMU_GPU, 6, 2, 0, &gpu_lock},
+       {0, "gpu_2d_mux", mmp3_gpu_gc_parent_names, ARRAY_SIZE(mmp3_gpu_gc_parent_names),
+                                                                       CLK_SET_RATE_PARENT, APMU_GPU, 12, 2, 0, &gpu_lock},
+};
+
 static struct mmp_param_div_clk apmu_div_clks[] = {
        {0, "disp0_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 8, 4, 0, &disp0_lock},
        {0, "disp0_sphy_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 15, 5, 0, &disp0_lock},
@@ -226,6 +300,11 @@ static struct mmp_param_div_clk apmu_div_clks[] = {
        {0, "ccic1_sphy_div", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 10, 5, 0, &ccic1_lock},
 };
 
+static struct mmp_param_div_clk mmp3_apmu_div_clks[] = {
+       {0, "gpu_3d_div", "gpu_3d_mux", CLK_SET_RATE_PARENT, APMU_GPU, 24, 4, 0, &gpu_lock},
+       {0, "gpu_2d_div", "gpu_2d_mux", CLK_SET_RATE_PARENT, APMU_GPU, 28, 4, 0, &gpu_lock},
+};
+
 static struct mmp_param_gate_clk apmu_gate_clks[] = {
        {MMP2_CLK_USB, "usb_clk", "usb_pll", 0, APMU_USB, 0x9, 0x9, 0x0, 0, &usb_lock},
        {MMP2_CLK_USBHSIC0, "usbhsic0_clk", "usb_pll", 0, APMU_USBHSIC0, 0x1b, 0x1b, 0x0, 0, &usbhsic0_lock},
@@ -235,8 +314,8 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = {
        {MMP2_CLK_SDH1, "sdh1_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
        {MMP2_CLK_SDH2, "sdh2_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH2, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
        {MMP2_CLK_SDH3, "sdh3_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH3, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
-       {MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x09, 0x09, 0x0, 0, &disp0_lock},
-       {MMP2_CLK_DISP0_LCDC, "disp0_lcdc_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x12, 0x12, 0x0, 0, &disp0_lock},
+       {MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x12, 0x12, 0x0, 0, &disp0_lock},
+       {MMP2_CLK_DISP0_LCDC, "disp0_lcdc_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x09, 0x09, 0x0, 0, &disp0_lock},
        {MMP2_CLK_DISP0_SPHY, "disp0_sphy_clk", "disp0_sphy_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1024, 0x1024, 0x0, 0, &disp0_lock},
        {MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x09, 0x09, 0x0, 0, &disp1_lock},
        {MMP2_CLK_CCIC_ARBITER, "ccic_arbiter", "vctcxo", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1800, 0x1800, 0x0, 0, &ccic0_lock},
@@ -246,6 +325,17 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = {
        {MMP2_CLK_CCIC1, "ccic1_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x1b, 0x1b, 0x0, 0, &ccic1_lock},
        {MMP2_CLK_CCIC1_PHY, "ccic1_phy_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x24, 0x24, 0x0, 0, &ccic1_lock},
        {MMP2_CLK_CCIC1_SPHY, "ccic1_sphy_clk", "ccic1_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x300, 0x300, 0x0, 0, &ccic1_lock},
+       {MMP2_CLK_GPU_BUS, "gpu_bus_clk", "gpu_bus_mux", CLK_SET_RATE_PARENT, APMU_GPU, 0xa, 0xa, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
+};
+
+static struct mmp_param_gate_clk mmp2_apmu_gate_clks[] = {
+       {MMP2_CLK_GPU_3D, "gpu_3d_clk", "gpu_3d_mux", CLK_SET_RATE_PARENT, APMU_GPU, 0x5, 0x5, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
+};
+
+static struct mmp_param_gate_clk mmp3_apmu_gate_clks[] = {
+       {MMP3_CLK_SDH4, "sdh4_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH4, 0x1b, 0x1b, 0x0, 0, &sdh_lock},
+       {MMP3_CLK_GPU_3D, "gpu_3d_clk", "gpu_3d_div", CLK_SET_RATE_PARENT, APMU_GPU, 0x5, 0x5, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
+       {MMP3_CLK_GPU_2D, "gpu_2d_clk", "gpu_2d_div", CLK_SET_RATE_PARENT, APMU_GPU, 0x1c0000, 0x1c0000, 0x0, MMP_CLK_GATE_NEED_DELAY, &gpu_lock},
 };
 
 static void mmp2_axi_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
@@ -281,6 +371,34 @@ static void mmp2_axi_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
 
        mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base,
                                ARRAY_SIZE(apmu_gate_clks));
+
+       if (pxa_unit->model == CLK_MODEL_MMP3) {
+               mmp_register_mux_clks(unit, mmp3_apmu_mux_clks, pxa_unit->apmu_base,
+                                       ARRAY_SIZE(mmp3_apmu_mux_clks));
+
+               mmp_register_div_clks(unit, mmp3_apmu_div_clks, pxa_unit->apmu_base,
+                                       ARRAY_SIZE(mmp3_apmu_div_clks));
+
+               mmp_register_gate_clks(unit, mmp3_apmu_gate_clks, pxa_unit->apmu_base,
+                                       ARRAY_SIZE(mmp3_apmu_gate_clks));
+       } else {
+               clk_register_mux_table(NULL, "gpu_3d_mux", mmp2_gpu_gc_parent_names,
+                                       ARRAY_SIZE(mmp2_gpu_gc_parent_names),
+                                       CLK_SET_RATE_PARENT,
+                                       pxa_unit->apmu_base + APMU_GPU,
+                                       0, 0x10c0, 0,
+                                       mmp2_gpu_gc_parent_table, &gpu_lock);
+
+               clk_register_mux_table(NULL, "gpu_bus_mux", mmp2_gpu_bus_parent_names,
+                                       ARRAY_SIZE(mmp2_gpu_bus_parent_names),
+                                       CLK_SET_RATE_PARENT,
+                                       pxa_unit->apmu_base + APMU_GPU,
+                                       0, 0x4030, 0,
+                                       mmp2_gpu_bus_parent_table, &gpu_lock);
+
+               mmp_register_gate_clks(unit, mmp2_apmu_gate_clks, pxa_unit->apmu_base,
+                                       ARRAY_SIZE(mmp2_apmu_gate_clks));
+       }
 }
 
 static void mmp2_clk_reset_init(struct device_node *np,
@@ -313,6 +431,11 @@ static void __init mmp2_clk_init(struct device_node *np)
        if (!pxa_unit)
                return;
 
+       if (of_device_is_compatible(np, "marvell,mmp3-clock"))
+               pxa_unit->model = CLK_MODEL_MMP3;
+       else
+               pxa_unit->model = CLK_MODEL_MMP2;
+
        pxa_unit->mpmu_base = of_iomap(np, 0);
        if (!pxa_unit->mpmu_base) {
                pr_err("failed to map mpmu registers\n");
@@ -352,3 +475,4 @@ free_memory:
 }
 
 CLK_OF_DECLARE(mmp2_clk, "marvell,mmp2-clock", mmp2_clk_init);
+CLK_OF_DECLARE(mmp3_clk, "marvell,mmp3-clock", mmp2_clk_init);
diff --git a/drivers/clk/mmp/clk-pll.c b/drivers/clk/mmp/clk-pll.c
new file mode 100644 (file)
index 0000000..7077be2
--- /dev/null
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MMP PLL clock rate calculation
+ *
+ * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include "clk.h"
+
+#define to_clk_mmp_pll(hw)     container_of(hw, struct mmp_clk_pll, hw)
+
+struct mmp_clk_pll {
+       struct clk_hw hw;
+       unsigned long default_rate;
+       void __iomem *enable_reg;
+       u32 enable;
+       void __iomem *reg;
+       u8 shift;
+
+       unsigned long input_rate;
+       void __iomem *postdiv_reg;
+       u8 postdiv_shift;
+};
+
+static int mmp_clk_pll_is_enabled(struct clk_hw *hw)
+{
+       struct mmp_clk_pll *pll = to_clk_mmp_pll(hw);
+       u32 val;
+
+       val = readl_relaxed(pll->enable_reg);
+       if ((val & pll->enable) == pll->enable)
+               return 1;
+
+       /* Some PLLs, if not software controlled, output default clock. */
+       if (pll->default_rate > 0)
+               return 1;
+
+       return 0;
+}
+
+static unsigned long mmp_clk_pll_recalc_rate(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct mmp_clk_pll *pll = to_clk_mmp_pll(hw);
+       u32 fbdiv, refdiv, postdiv;
+       u64 rate;
+       u32 val;
+
+       val = readl_relaxed(pll->enable_reg);
+       if ((val & pll->enable) != pll->enable)
+               return pll->default_rate;
+
+       if (pll->reg) {
+               val = readl_relaxed(pll->reg);
+               fbdiv = (val >> pll->shift) & 0x1ff;
+               refdiv = (val >> (pll->shift + 9)) & 0x1f;
+       } else {
+               fbdiv = 2;
+               refdiv = 1;
+       }
+
+       if (pll->postdiv_reg) {
+               /* MMP3 clock rate calculation */
+               static const u8 postdivs[] = {2, 3, 4, 5, 6, 8, 10, 12, 16};
+
+               val = readl_relaxed(pll->postdiv_reg);
+               postdiv = (val >> pll->postdiv_shift) & 0x7;
+
+               rate = pll->input_rate;
+               rate *= 2 * fbdiv;
+               do_div(rate, refdiv);
+               do_div(rate, postdivs[postdiv]);
+       } else {
+               /* MMP2 clock rate calculation */
+               if (refdiv == 3) {
+                       rate = 19200000;
+               } else if (refdiv == 4) {
+                       rate = 26000000;
+               } else {
+                       pr_err("bad refdiv: %d (0x%08x)\n", refdiv, val);
+                       return 0;
+               }
+
+               rate *= fbdiv + 2;
+               do_div(rate, refdiv + 2);
+       }
+
+       return (unsigned long)rate;
+}
+
+static const struct clk_ops mmp_clk_pll_ops = {
+       .is_enabled = mmp_clk_pll_is_enabled,
+       .recalc_rate = mmp_clk_pll_recalc_rate,
+};
+
+struct clk *mmp_clk_register_pll(char *name,
+                       unsigned long default_rate,
+                       void __iomem *enable_reg, u32 enable,
+                       void __iomem *reg, u8 shift,
+                       unsigned long input_rate,
+                       void __iomem *postdiv_reg, u8 postdiv_shift)
+{
+       struct mmp_clk_pll *pll;
+       struct clk *clk;
+       struct clk_init_data init;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (!pll)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &mmp_clk_pll_ops;
+       init.flags = 0;
+       init.parent_names = NULL;
+       init.num_parents = 0;
+
+       pll->default_rate = default_rate;
+       pll->enable_reg = enable_reg;
+       pll->enable = enable;
+       pll->reg = reg;
+       pll->shift = shift;
+
+       pll->input_rate = input_rate;
+       pll->postdiv_reg = postdiv_reg;
+       pll->postdiv_shift = postdiv_shift;
+
+       pll->hw.init = &init;
+
+       clk = clk_register(NULL, &pll->hw);
+
+       if (IS_ERR(clk))
+               kfree(pll);
+
+       return clk;
+}
index ca7d37e2c7be6bb6aba2a7d4e1459d8c4369fcca..317123641d1ed0be3ac73b41c9880d418c5119f5 100644 (file)
@@ -176,6 +176,37 @@ void mmp_register_div_clks(struct mmp_clk_unit *unit,
        }
 }
 
+void mmp_register_pll_clks(struct mmp_clk_unit *unit,
+                       struct mmp_param_pll_clk *clks,
+                       void __iomem *base, int size)
+{
+       struct clk *clk;
+       int i;
+
+       for (i = 0; i < size; i++) {
+               void __iomem *reg = NULL;
+
+               if (clks[i].offset)
+                       reg = base + clks[i].offset;
+
+               clk = mmp_clk_register_pll(clks[i].name,
+                                       clks[i].default_rate,
+                                       base + clks[i].enable_offset,
+                                       clks[i].enable,
+                                       reg, clks[i].shift,
+                                       clks[i].input_rate,
+                                       base + clks[i].postdiv_offset,
+                                       clks[i].postdiv_shift);
+               if (IS_ERR(clk)) {
+                       pr_err("%s: failed to register clock %s\n",
+                              __func__, clks[i].name);
+                       continue;
+               }
+               if (clks[i].id)
+                       unit->clk_table[clks[i].id] = clk;
+       }
+}
+
 void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id,
                        struct clk *clk)
 {
index 70bb73257647a2fe4ab280833175388d8d2bbdf7..971b4d6d992fb774e4730e343aca7d34739c997a 100644 (file)
@@ -97,7 +97,7 @@ struct mmp_clk_mix {
 extern const struct clk_ops mmp_clk_mix_ops;
 extern struct clk *mmp_clk_register_mix(struct device *dev,
                                        const char *name,
-                                       const char **parent_names,
+                                       const char * const *parent_names,
                                        u8 num_parents,
                                        unsigned long flags,
                                        struct mmp_clk_mix_config *config,
@@ -124,9 +124,6 @@ extern struct clk *mmp_clk_register_gate(struct device *dev, const char *name,
                        u32 val_disable, unsigned int gate_flags,
                        spinlock_t *lock);
 
-
-extern struct clk *mmp_clk_register_pll2(const char *name,
-               const char *parent_name, unsigned long flags);
 extern struct clk *mmp_clk_register_apbc(const char *name,
                const char *parent_name, void __iomem *base,
                unsigned int delay, unsigned int apbc_flags, spinlock_t *lock);
@@ -196,7 +193,7 @@ void mmp_register_gate_clks(struct mmp_clk_unit *unit,
 struct mmp_param_mux_clk {
        unsigned int id;
        char *name;
-       const char **parent_name;
+       const char * const *parent_name;
        u8 num_parents;
        unsigned long flags;
        unsigned long offset;
@@ -224,6 +221,30 @@ void mmp_register_div_clks(struct mmp_clk_unit *unit,
                        struct mmp_param_div_clk *clks,
                        void __iomem *base, int size);
 
+struct mmp_param_pll_clk {
+       unsigned int id;
+       char *name;
+       unsigned long default_rate;
+       unsigned long enable_offset;
+       u32 enable;
+       unsigned long offset;
+       u8 shift;
+       /* MMP3 specific: */
+       unsigned long input_rate;
+       unsigned long postdiv_offset;
+       unsigned long postdiv_shift;
+};
+void mmp_register_pll_clks(struct mmp_clk_unit *unit,
+                       struct mmp_param_pll_clk *clks,
+                       void __iomem *base, int size);
+
+extern struct clk *mmp_clk_register_pll(char *name,
+                       unsigned long default_rate,
+                       void __iomem *enable_reg, u32 enable,
+                       void __iomem *reg, u8 shift,
+                       unsigned long input_rate,
+                       void __iomem *postdiv_reg, u8 postdiv_shift);
+
 #define DEFINE_MIX_REG_INFO(w_d, s_d, w_m, s_m, fc)    \
 {                                                      \
        .width_div = (w_d),                             \
index fe686f77787f5197b25e8c80c97f48e0dbcfe53a..692be2fd9261a8ca9341cfca583161c52591b8b1 100644 (file)
 
 #define INTEGRATOR_AP_PCI_25_33_MHZ BIT(8)
 
-/**
- * enum icst_control_type - the type of ICST control register
- */
-enum icst_control_type {
-       ICST_VERSATILE, /* The standard type, all control bits available */
-       ICST_INTEGRATOR_AP_CM, /* Only 8 bits of VDW available */
-       ICST_INTEGRATOR_AP_SYS, /* Only 8 bits of VDW available */
-       ICST_INTEGRATOR_AP_PCI, /* Odd bit pattern storage */
-       ICST_INTEGRATOR_CP_CM_CORE, /* Only 8 bits of VDW and 3 bits of OD */
-       ICST_INTEGRATOR_CP_CM_MEM, /* Only 8 bits of VDW and 3 bits of OD */
-};
-
 /**
  * struct clk_icst - ICST VCO clock wrapper
  * @hw: corresponding clock hardware entry
@@ -344,12 +332,12 @@ static const struct clk_ops icst_ops = {
        .set_rate = icst_set_rate,
 };
 
-static struct clk *icst_clk_setup(struct device *dev,
-                                 const struct clk_icst_desc *desc,
-                                 const char *name,
-                                 const char *parent_name,
-                                 struct regmap *map,
-                                 enum icst_control_type ctype)
+struct clk *icst_clk_setup(struct device *dev,
+                          const struct clk_icst_desc *desc,
+                          const char *name,
+                          const char *parent_name,
+                          struct regmap *map,
+                          enum icst_control_type ctype)
 {
        struct clk *clk;
        struct clk_icst *icst;
@@ -386,6 +374,7 @@ static struct clk *icst_clk_setup(struct device *dev,
 
        return clk;
 }
+EXPORT_SYMBOL_GPL(icst_clk_setup);
 
 struct clk *icst_clk_register(struct device *dev,
                        const struct clk_icst_desc *desc,
index e36ca1a20e90862b1958e25c10d82a5aff1b4a13..1a119ef110660f2bcc5354c0f77ae6e61c011a17 100644 (file)
@@ -1,4 +1,19 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+struct regmap;
+
+/**
+ * enum icst_control_type - the type of ICST control register
+ */
+enum icst_control_type {
+       ICST_VERSATILE, /* The standard type, all control bits available */
+       ICST_INTEGRATOR_AP_CM, /* Only 8 bits of VDW available */
+       ICST_INTEGRATOR_AP_SYS, /* Only 8 bits of VDW available */
+       ICST_INTEGRATOR_AP_PCI, /* Odd bit pattern storage */
+       ICST_INTEGRATOR_CP_CM_CORE, /* Only 8 bits of VDW and 3 bits of OD */
+       ICST_INTEGRATOR_CP_CM_MEM, /* Only 8 bits of VDW and 3 bits of OD */
+       ICST_INTEGRATOR_IM_PD1, /* Like the Versatile, all control bits */
+};
+
 /**
  * struct clk_icst_desc - descriptor for the ICST VCO
  * @params: ICST parameters
@@ -17,3 +32,10 @@ struct clk *icst_clk_register(struct device *dev,
                              const char *name,
                              const char *parent_name,
                              void __iomem *base);
+
+struct clk *icst_clk_setup(struct device *dev,
+                          const struct clk_icst_desc *desc,
+                          const char *name,
+                          const char *parent_name,
+                          struct regmap *map,
+                          enum icst_control_type ctype);
index 1991f15a5db9694e3546495351a8a6d32fff5ad7..b05da8516d4c94d96d4d3fdc045721856f595072 100644 (file)
@@ -7,7 +7,11 @@
 #include <linux/clkdev.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
 #include <linux/platform_data/clk-integrator.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 #include "icst.h"
 #include "clk-icst.h"
@@ -175,3 +179,78 @@ void integrator_impd1_clk_exit(unsigned int id)
        kfree(imc->pclkname);
 }
 EXPORT_SYMBOL_GPL(integrator_impd1_clk_exit);
+
+static int integrator_impd1_clk_spawn(struct device *dev,
+                                     struct device_node *parent,
+                                     struct device_node *np)
+{
+       struct regmap *map;
+       struct clk *clk = ERR_PTR(-EINVAL);
+       const char *name = np->name;
+       const char *parent_name;
+       const struct clk_icst_desc *desc;
+       int ret;
+
+       map = syscon_node_to_regmap(parent);
+       if (IS_ERR(map)) {
+               pr_err("no regmap for syscon IM-PD1 ICST clock parent\n");
+               return PTR_ERR(map);
+       }
+
+       if (of_device_is_compatible(np, "arm,impd1-vco1")) {
+               desc = &impd1_icst1_desc;
+       } else if (of_device_is_compatible(np, "arm,impd1-vco2")) {
+               desc = &impd1_icst2_desc;
+       } else {
+               dev_err(dev, "not a clock node %s\n", name);
+               return -ENODEV;
+       }
+
+       parent_name = of_clk_get_parent_name(np, 0);
+       clk = icst_clk_setup(NULL, desc, name, parent_name, map,
+                            ICST_INTEGRATOR_IM_PD1);
+       if (!IS_ERR(clk)) {
+               of_clk_add_provider(np, of_clk_src_simple_get, clk);
+               ret = 0;
+       } else {
+               dev_err(dev, "error setting up IM-PD1 ICST clock\n");
+               ret = PTR_ERR(clk);
+       }
+
+       return ret;
+}
+
+static int integrator_impd1_clk_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *child;
+       int ret = 0;
+
+       for_each_available_child_of_node(np, child) {
+               ret = integrator_impd1_clk_spawn(dev, np, child);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static const struct of_device_id impd1_syscon_match[] = {
+       { .compatible = "arm,im-pd1-syscon", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, impd1_syscon_match);
+
+static struct platform_driver impd1_clk_driver = {
+       .driver = {
+               .name = "impd1-clk",
+               .of_match_table = impd1_syscon_match,
+       },
+       .probe  = integrator_impd1_clk_probe,
+};
+builtin_platform_driver(impd1_clk_driver);
+
+MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>");
+MODULE_DESCRIPTION("Arm IM-PD1 module clock driver");
+MODULE_LICENSE("GPL v2");
index 4b1a7724f20d7aeeba10504e3c826386f7b36192..06bb7fe4c62f432c71739a6610c97d22b824827f 100644 (file)
@@ -26,6 +26,9 @@
 #define MMP2_CLK_VCTCXO_4              25
 #define MMP2_CLK_UART_PLL              26
 #define MMP2_CLK_USB_PLL               27
+#define MMP3_CLK_PLL1_P                        28
+#define MMP3_CLK_PLL2_P                        29
+#define MMP3_CLK_PLL3                  30
 
 /* apb periphrals */
 #define MMP2_CLK_TWSI0                 60
 #define MMP2_CLK_SSP2                  79
 #define MMP2_CLK_SSP3                  80
 #define MMP2_CLK_TIMER                 81
+#define MMP2_CLK_THERMAL0              82
+#define MMP3_CLK_THERMAL1              83
+#define MMP3_CLK_THERMAL2              84
+#define MMP3_CLK_THERMAL3              85
 
 /* axi periphrals */
 #define MMP2_CLK_SDH0                  101
 #define MMP2_CLK_DISP0_LCDC            120
 #define MMP2_CLK_USBHSIC0              121
 #define MMP2_CLK_USBHSIC1              122
+#define MMP2_CLK_GPU_BUS               123
+#define MMP3_CLK_GPU_BUS               MMP2_CLK_GPU_BUS
+#define MMP2_CLK_GPU_3D                        124
+#define MMP3_CLK_GPU_3D                        MMP2_CLK_GPU_3D
+#define MMP3_CLK_GPU_2D                        125
+#define MMP3_CLK_SDH4                  126
 
 #define MMP2_NR_CLKS                   200
 #endif