[PATCH v5 06/15] clk: Add Ingenic jz4770 CGU driver

2018-01-02 Thread Paul Cercueil
Add support for the clocks provided by the CGU in the Ingenic JZ4770
SoC.

Signed-off-by: Paul Cercueil 
Signed-off-by: Maarten ter Huurne 
Acked-by: Stephen Boyd 
---
 drivers/clk/ingenic/Makefile |   1 +
 drivers/clk/ingenic/jz4770-cgu.c | 483 +++
 2 files changed, 484 insertions(+)
 create mode 100644 drivers/clk/ingenic/jz4770-cgu.c

 v2: Make structures static const
 v3:  is now added in a separate patch
 v4: No change
 v5: Use SPDX license identifier

diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile
index cd47b0664c2b..1456e4cdb562 100644
--- a/drivers/clk/ingenic/Makefile
+++ b/drivers/clk/ingenic/Makefile
@@ -1,3 +1,4 @@
 obj-y  += cgu.o
 obj-$(CONFIG_MACH_JZ4740)  += jz4740-cgu.o
+obj-$(CONFIG_MACH_JZ4770)  += jz4770-cgu.o
 obj-$(CONFIG_MACH_JZ4780)  += jz4780-cgu.o
diff --git a/drivers/clk/ingenic/jz4770-cgu.c b/drivers/clk/ingenic/jz4770-cgu.c
new file mode 100644
index ..c78d369b9403
--- /dev/null
+++ b/drivers/clk/ingenic/jz4770-cgu.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * JZ4770 SoC CGU driver
+ * Copyright 2018, Paul Cercueil 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cgu.h"
+
+/*
+ * CPM registers offset address definition
+ */
+#define CGU_REG_CPCCR  0x00
+#define CGU_REG_LCR0x04
+#define CGU_REG_CPPCR0 0x10
+#define CGU_REG_CLKGR0 0x20
+#define CGU_REG_OPCR   0x24
+#define CGU_REG_CLKGR1 0x28
+#define CGU_REG_CPPCR1 0x30
+#define CGU_REG_USBPCR10x48
+#define CGU_REG_USBCDR 0x50
+#define CGU_REG_I2SCDR 0x60
+#define CGU_REG_LPCDR  0x64
+#define CGU_REG_MSC0CDR0x68
+#define CGU_REG_UHCCDR 0x6c
+#define CGU_REG_SSICDR 0x74
+#define CGU_REG_CIMCDR 0x7c
+#define CGU_REG_GPSCDR 0x80
+#define CGU_REG_PCMCDR 0x84
+#define CGU_REG_GPUCDR 0x88
+#define CGU_REG_MSC1CDR0xA4
+#define CGU_REG_MSC2CDR0xA8
+#define CGU_REG_BCHCDR 0xAC
+
+/* bits within the LCR register */
+#define LCR_LPMBIT(0)  /* Low Power Mode */
+
+/* bits within the OPCR register */
+#define OPCR_SPENDHBIT(5)  /* UHC PHY suspend */
+#define OPCR_SPENDNBIT(7)  /* OTG PHY suspend */
+
+/* bits within the USBPCR1 register */
+#define USBPCR1_UHC_POWER  BIT(5)  /* UHC PHY power down */
+
+static struct ingenic_cgu *cgu;
+
+static int jz4770_uhc_phy_enable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+   void __iomem *reg_usbpcr1   = cgu->base + CGU_REG_USBPCR1;
+
+   writel(readl(reg_opcr) & ~OPCR_SPENDH, reg_opcr);
+   writel(readl(reg_usbpcr1) | USBPCR1_UHC_POWER, reg_usbpcr1);
+   return 0;
+}
+
+static void jz4770_uhc_phy_disable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+   void __iomem *reg_usbpcr1   = cgu->base + CGU_REG_USBPCR1;
+
+   writel(readl(reg_usbpcr1) & ~USBPCR1_UHC_POWER, reg_usbpcr1);
+   writel(readl(reg_opcr) | OPCR_SPENDH, reg_opcr);
+}
+
+static int jz4770_uhc_phy_is_enabled(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+   void __iomem *reg_usbpcr1   = cgu->base + CGU_REG_USBPCR1;
+
+   return !(readl(reg_opcr) & OPCR_SPENDH) &&
+   (readl(reg_usbpcr1) & USBPCR1_UHC_POWER);
+}
+
+static const struct clk_ops jz4770_uhc_phy_ops = {
+   .enable = jz4770_uhc_phy_enable,
+   .disable = jz4770_uhc_phy_disable,
+   .is_enabled = jz4770_uhc_phy_is_enabled,
+};
+
+static int jz4770_otg_phy_enable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+
+   writel(readl(reg_opcr) | OPCR_SPENDN, reg_opcr);
+
+   /* Wait for the clock to be stable */
+   udelay(50);
+   return 0;
+}
+
+static void jz4770_otg_phy_disable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+
+   writel(readl(reg_opcr) & ~OPCR_SPENDN, reg_opcr);
+}
+
+static int jz4770_otg_phy_is_enabled(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+
+   return !!(readl(reg_opcr) & OPCR_SPENDN);
+}
+
+static const struct clk_ops jz4770_otg_phy_ops = {
+   .enable = jz4770_otg_phy_enable,
+   .disable = jz4770_otg_phy_disable,
+   .is_enabled = jz4770_otg_phy_is_enabled,
+};
+
+static const s8 pll_od_encoding[8] = {
+   0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
+};
+
+static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
+
+   /* External clocks */
+
+   [JZ4770_CLK_EXT] = { "ext", CGU_CLK_EXT },
+   [JZ4770_CLK_OSC32K] = { "osc32k", 

[PATCH v5 06/15] clk: Add Ingenic jz4770 CGU driver

2018-01-02 Thread Paul Cercueil
Add support for the clocks provided by the CGU in the Ingenic JZ4770
SoC.

Signed-off-by: Paul Cercueil 
Signed-off-by: Maarten ter Huurne 
Acked-by: Stephen Boyd 
---
 drivers/clk/ingenic/Makefile |   1 +
 drivers/clk/ingenic/jz4770-cgu.c | 483 +++
 2 files changed, 484 insertions(+)
 create mode 100644 drivers/clk/ingenic/jz4770-cgu.c

 v2: Make structures static const
 v3:  is now added in a separate patch
 v4: No change
 v5: Use SPDX license identifier

diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile
index cd47b0664c2b..1456e4cdb562 100644
--- a/drivers/clk/ingenic/Makefile
+++ b/drivers/clk/ingenic/Makefile
@@ -1,3 +1,4 @@
 obj-y  += cgu.o
 obj-$(CONFIG_MACH_JZ4740)  += jz4740-cgu.o
+obj-$(CONFIG_MACH_JZ4770)  += jz4770-cgu.o
 obj-$(CONFIG_MACH_JZ4780)  += jz4780-cgu.o
diff --git a/drivers/clk/ingenic/jz4770-cgu.c b/drivers/clk/ingenic/jz4770-cgu.c
new file mode 100644
index ..c78d369b9403
--- /dev/null
+++ b/drivers/clk/ingenic/jz4770-cgu.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * JZ4770 SoC CGU driver
+ * Copyright 2018, Paul Cercueil 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "cgu.h"
+
+/*
+ * CPM registers offset address definition
+ */
+#define CGU_REG_CPCCR  0x00
+#define CGU_REG_LCR0x04
+#define CGU_REG_CPPCR0 0x10
+#define CGU_REG_CLKGR0 0x20
+#define CGU_REG_OPCR   0x24
+#define CGU_REG_CLKGR1 0x28
+#define CGU_REG_CPPCR1 0x30
+#define CGU_REG_USBPCR10x48
+#define CGU_REG_USBCDR 0x50
+#define CGU_REG_I2SCDR 0x60
+#define CGU_REG_LPCDR  0x64
+#define CGU_REG_MSC0CDR0x68
+#define CGU_REG_UHCCDR 0x6c
+#define CGU_REG_SSICDR 0x74
+#define CGU_REG_CIMCDR 0x7c
+#define CGU_REG_GPSCDR 0x80
+#define CGU_REG_PCMCDR 0x84
+#define CGU_REG_GPUCDR 0x88
+#define CGU_REG_MSC1CDR0xA4
+#define CGU_REG_MSC2CDR0xA8
+#define CGU_REG_BCHCDR 0xAC
+
+/* bits within the LCR register */
+#define LCR_LPMBIT(0)  /* Low Power Mode */
+
+/* bits within the OPCR register */
+#define OPCR_SPENDHBIT(5)  /* UHC PHY suspend */
+#define OPCR_SPENDNBIT(7)  /* OTG PHY suspend */
+
+/* bits within the USBPCR1 register */
+#define USBPCR1_UHC_POWER  BIT(5)  /* UHC PHY power down */
+
+static struct ingenic_cgu *cgu;
+
+static int jz4770_uhc_phy_enable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+   void __iomem *reg_usbpcr1   = cgu->base + CGU_REG_USBPCR1;
+
+   writel(readl(reg_opcr) & ~OPCR_SPENDH, reg_opcr);
+   writel(readl(reg_usbpcr1) | USBPCR1_UHC_POWER, reg_usbpcr1);
+   return 0;
+}
+
+static void jz4770_uhc_phy_disable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+   void __iomem *reg_usbpcr1   = cgu->base + CGU_REG_USBPCR1;
+
+   writel(readl(reg_usbpcr1) & ~USBPCR1_UHC_POWER, reg_usbpcr1);
+   writel(readl(reg_opcr) | OPCR_SPENDH, reg_opcr);
+}
+
+static int jz4770_uhc_phy_is_enabled(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+   void __iomem *reg_usbpcr1   = cgu->base + CGU_REG_USBPCR1;
+
+   return !(readl(reg_opcr) & OPCR_SPENDH) &&
+   (readl(reg_usbpcr1) & USBPCR1_UHC_POWER);
+}
+
+static const struct clk_ops jz4770_uhc_phy_ops = {
+   .enable = jz4770_uhc_phy_enable,
+   .disable = jz4770_uhc_phy_disable,
+   .is_enabled = jz4770_uhc_phy_is_enabled,
+};
+
+static int jz4770_otg_phy_enable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+
+   writel(readl(reg_opcr) | OPCR_SPENDN, reg_opcr);
+
+   /* Wait for the clock to be stable */
+   udelay(50);
+   return 0;
+}
+
+static void jz4770_otg_phy_disable(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+
+   writel(readl(reg_opcr) & ~OPCR_SPENDN, reg_opcr);
+}
+
+static int jz4770_otg_phy_is_enabled(struct clk_hw *hw)
+{
+   void __iomem *reg_opcr  = cgu->base + CGU_REG_OPCR;
+
+   return !!(readl(reg_opcr) & OPCR_SPENDN);
+}
+
+static const struct clk_ops jz4770_otg_phy_ops = {
+   .enable = jz4770_otg_phy_enable,
+   .disable = jz4770_otg_phy_disable,
+   .is_enabled = jz4770_otg_phy_is_enabled,
+};
+
+static const s8 pll_od_encoding[8] = {
+   0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
+};
+
+static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
+
+   /* External clocks */
+
+   [JZ4770_CLK_EXT] = { "ext", CGU_CLK_EXT },
+   [JZ4770_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },
+
+   /* PLLs */
+
+   [JZ4770_CLK_PLL0] = {
+   "pll0",