Forgot to cc linux-clk, so doing that now...

On 12/31/2017 05:39 PM, David Lechner wrote:
This introduces new drivers for arch/arm/mach-davinci. The code is based
on the clock drivers from there and adapted to use the common clock
framework.

Signed-off-by: David Lechner <da...@lechnology.com>
---
  drivers/clk/Makefile                      |   1 +
  drivers/clk/davinci/Makefile              |   3 +
  drivers/clk/davinci/da8xx-cfgchip-clk.c   | 380 ++++++++++++++++++++++++++++++
  drivers/clk/davinci/pll.c                 | 333 ++++++++++++++++++++++++++
  drivers/clk/davinci/psc.c                 | 217 +++++++++++++++++
  include/linux/clk/davinci.h               |  46 ++++
  include/linux/platform_data/davinci_clk.h |  25 ++
  7 files changed, 1005 insertions(+)
  create mode 100644 drivers/clk/davinci/Makefile
  create mode 100644 drivers/clk/davinci/da8xx-cfgchip-clk.c
  create mode 100644 drivers/clk/davinci/pll.c
  create mode 100644 drivers/clk/davinci/psc.c
  create mode 100644 include/linux/clk/davinci.h
  create mode 100644 include/linux/platform_data/davinci_clk.h

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f7f761b..c865fd0 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_ARCH_ARTPEC)             += axis/
  obj-$(CONFIG_ARC_PLAT_AXS10X)         += axs10x/
  obj-y                                 += bcm/
  obj-$(CONFIG_ARCH_BERLIN)             += berlin/
+obj-$(CONFIG_ARCH_DAVINCI)             += davinci/
  obj-$(CONFIG_H8300)                   += h8300/
  obj-$(CONFIG_ARCH_HISI)                       += hisilicon/
  obj-y                                 += imgtec/
diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile
new file mode 100644
index 0000000..5efbdcd
--- /dev/null
+++ b/drivers/clk/davinci/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PHY_DA8XX_USB) += da8xx-cfgchip-clk.o
+obj-y += pll.o
+obj-y += psc.o
\ No newline at end of file
diff --git a/drivers/clk/davinci/da8xx-cfgchip-clk.c 
b/drivers/clk/davinci/da8xx-cfgchip-clk.c
new file mode 100644
index 0000000..780bb25
--- /dev/null
+++ b/drivers/clk/davinci/da8xx-cfgchip-clk.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * da8xx-cfgchip-clk - TI DaVinci DA8xx CFGCHIP clocks driver
+ *
+ * Copyright (C) 2017 David Lechner <da...@lechnology.com>
+ *
+ * This driver exposes the USB PHY clocks on DA8xx/AM18xx/OMAP-L13x SoCs.
+ * The clocks consist of two muxes and a PLL. The USB 2.0 PHY mux and PLL are
+ * combined into a single clock in Linux. The USB 1.0 PHY clock just consists
+ * of a mux. These clocks are controlled through CFGCHIP2, which is accessed
+ * as a syscon regmap since it is shared with other devices.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/mfd/da8xx-cfgchip.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_data/davinci_clk.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/**
+ * da8xx_cfgchip_clk
+ * @usb0_hw: The USB 2.0 PHY clock (mux + PLL)
+ * @usb1_hw: The USB 1.1 PHY clock (mux)
+ * @usb0_clk: The USB 2.0 subsystem PSC clock
+ * @regmap: The CFGCHIP syscon regmap
+ */
+struct da8xx_cfgchip_clk {
+       struct clk_hw usb0_hw;
+       struct clk_hw usb1_hw;
+       struct clk *usb0_clk;
+       struct regmap *regmap;
+};
+
+/* The USB 2.0 PHY can use either USB_REFCLKIN or AUXCLK */
+enum usb0_phy_clk_parent {
+       USB20_PHY_CLK_PARENT_USB_REFCLKIN,
+       USB20_PHY_CLK_PARENT_PLL0_AUX,
+};
+
+/* The USB 1.1 PHY can use either the PLL output from the USB 2.0 PHY or
+ * USB_REFCLKIN
+ */
+enum usb1_phy_clk_parent {
+       USB1_PHY_CLK_PARENT_USB_REFCLKIN,
+       USB1_PHY_CLK_PARENT_USB0_PHY_PLL,
+};
+
+/* --- USB 2.0 PHY clock --- */
+
+static int usb0_phy_clk_prepare(struct clk_hw *hw)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
+
+       /* The USB 2.0 PSC clock is only needed temporarily during the USB 2.0
+        * PHY clock enable, but since clk_prepare() can't be called in an
+        * atomic context (i.e. in clk_enable()), we have to prepare it here.
+        */
+       return clk_prepare(clk->usb0_clk);
+}
+
+static void usb0_phy_clk_unprepare(struct clk_hw *hw)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
+
+       clk_unprepare(clk->usb0_clk);
+}
+
+static int usb0_phy_clk_enable(struct clk_hw *hw)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
+       unsigned int mask, val;
+       int ret;
+
+       /* Locking the USB 2.O PLL requires that the USB 2.O PSC is enabled
+        * temporaily. It can be turned back off once the PLL is locked.
+        */
+       clk_enable(clk->usb0_clk);
+
+       /* Turn on the USB 2.0 PHY, but just the PLL, and not OTG. The USB 1.1
+        * PHY may use the USB 2.0 PLL clock without USB 2.0 OTG being used.
+        */
+       mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_PHY_PLLON;
+       val = CFGCHIP2_PHY_PLLON;
+
+       regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
+       ret = regmap_read_poll_timeout(clk->regmap, CFGCHIP(2), val,
+                                      val & CFGCHIP2_PHYCLKGD, 0, 500000);
+
+       clk_disable(clk->usb0_clk);
+
+       return ret;
+}
+
+static void usb0_phy_clk_disable(struct clk_hw *hw)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
+       unsigned int val;
+
+       val = CFGCHIP2_PHYPWRDN;
+       regmap_write_bits(clk->regmap, CFGCHIP(2), val, val);
+}
+
+static unsigned long usb0_phy_clk_recalc_rate(struct clk_hw *hw,
+                                             unsigned long parent_rate)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
+       unsigned int mask, val;
+
+       /* The parent clock rate must be one of the following */
+       mask = CFGCHIP2_REFFREQ_MASK;
+       switch (parent_rate) {
+       case 12000000:
+               val = CFGCHIP2_REFFREQ_12MHZ;
+               break;
+       case 13000000:
+               val = CFGCHIP2_REFFREQ_13MHZ;
+               break;
+       case 19200000:
+               val = CFGCHIP2_REFFREQ_19_2MHZ;
+               break;
+       case 20000000:
+               val = CFGCHIP2_REFFREQ_20MHZ;
+               break;
+       case 24000000:
+               val = CFGCHIP2_REFFREQ_24MHZ;
+               break;
+       case 26000000:
+               val = CFGCHIP2_REFFREQ_26MHZ;
+               break;
+       case 38400000:
+               val = CFGCHIP2_REFFREQ_38_4MHZ;
+               break;
+       case 40000000:
+               val = CFGCHIP2_REFFREQ_40MHZ;
+               break;
+       case 48000000:
+               val = CFGCHIP2_REFFREQ_48MHZ;
+               break;
+       default:
+               return 0;
+       }
+
+       regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
+
+       /* USB 2.0 PLL always supplies 48MHz */
+       return 48000000;
+}
+
+static long usb0_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                   unsigned long *parent_rate)
+{
+       return 48000000;
+}
+
+static int usb0_phy_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
+       unsigned int mask, val;
+
+       /* Set the mux depending on the parent clock. */
+       mask = CFGCHIP2_USB2PHYCLKMUX;
+       switch (index) {
+       case USB20_PHY_CLK_PARENT_USB_REFCLKIN:
+               val = 0;
+               break;
+       case USB20_PHY_CLK_PARENT_PLL0_AUX:
+               val = CFGCHIP2_USB2PHYCLKMUX;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
+
+       return 0;
+}
+
+static u8 usb0_phy_clk_get_parent(struct clk_hw *hw)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
+       unsigned int val;
+
+       regmap_read(clk->regmap, CFGCHIP(2), &val);
+
+       if (val & CFGCHIP2_USB2PHYCLKMUX)
+               return USB20_PHY_CLK_PARENT_PLL0_AUX;
+
+       return USB20_PHY_CLK_PARENT_USB_REFCLKIN;
+}
+
+static const struct clk_ops usb0_phy_clk_ops = {
+       .prepare        = usb0_phy_clk_prepare,
+       .unprepare      = usb0_phy_clk_unprepare,
+       .enable         = usb0_phy_clk_enable,
+       .disable        = usb0_phy_clk_disable,
+       .recalc_rate    = usb0_phy_clk_recalc_rate,
+       .round_rate     = usb0_phy_clk_round_rate,
+       .set_parent     = usb0_phy_clk_set_parent,
+       .get_parent     = usb0_phy_clk_get_parent,
+};
+
+static const char * const usb0_phy_clk_parent_names[] = {
+       [USB20_PHY_CLK_PARENT_USB_REFCLKIN]     = "usb_refclkin",
+       [USB20_PHY_CLK_PARENT_PLL0_AUX]         = "pll0_aux_clk",
+};
+
+static const struct clk_init_data usb0_phy_clk_init_data = {
+       .name           = "usb0_phy_clk",
+       .ops            = &usb0_phy_clk_ops,
+       .parent_names   = usb0_phy_clk_parent_names,
+       .num_parents    = ARRAY_SIZE(usb0_phy_clk_parent_names),
+};
+
+/* --- USB 1.1 PHY clock --- */
+
+static int usb1_phy_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb1_hw);
+       unsigned int mask, val;
+
+       /* Set the USB 1.1 PHY clock mux based on the parent clock. */
+       mask = CFGCHIP2_USB1PHYCLKMUX;
+       switch (index) {
+       case USB1_PHY_CLK_PARENT_USB_REFCLKIN:
+               val = CFGCHIP2_USB1PHYCLKMUX;
+               break;
+       case USB1_PHY_CLK_PARENT_USB0_PHY_PLL:
+               val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
+
+       return 0;
+}
+
+static u8 usb1_phy_clk_get_parent(struct clk_hw *hw)
+{
+       struct da8xx_cfgchip_clk *clk =
+                       container_of(hw, struct da8xx_cfgchip_clk, usb1_hw);
+       unsigned int val;
+
+       regmap_read(clk->regmap, CFGCHIP(2), &val);
+
+       if (val & CFGCHIP2_USB1PHYCLKMUX)
+               return USB1_PHY_CLK_PARENT_USB_REFCLKIN;
+
+       return USB1_PHY_CLK_PARENT_USB0_PHY_PLL;
+}
+
+static const struct clk_ops usb1_phy_clk_ops = {
+       .set_parent     = usb1_phy_clk_set_parent,
+       .get_parent     = usb1_phy_clk_get_parent,
+};
+
+static const char * const usb1_phy_clk_parent_names[] = {
+       [USB1_PHY_CLK_PARENT_USB_REFCLKIN]      = "usb_refclkin",
+       [USB1_PHY_CLK_PARENT_USB0_PHY_PLL]      = "usb0_phy_clk",
+};
+
+static struct clk_init_data usb1_phy_clk_init_data = {
+       .name           = "usb1_phy_clk",
+       .ops            = &usb1_phy_clk_ops,
+       .parent_names   = usb1_phy_clk_parent_names,
+       .num_parents    = ARRAY_SIZE(usb1_phy_clk_parent_names),
+};
+
+/* --- platform driver --- */
+
+static int da8xx_cfgchip_clk_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct da8xx_cfgchip_clk_data *pdata = dev->platform_data;
+       struct da8xx_cfgchip_clk *phy_clk;
+       const char *parent_name;
+       struct clk *parent;
+       int ret;
+
+       if (!pdata)
+               return -EINVAL;
+
+       phy_clk = devm_kzalloc(dev, sizeof(*phy_clk), GFP_KERNEL);
+       if (!phy_clk)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, phy_clk);
+
+       phy_clk->regmap = syscon_regmap_lookup_by_pdevname("syscon");
+       if (IS_ERR(phy_clk->regmap)) {
+               dev_err(dev, "Failed to get syscon\n");
+               return PTR_ERR(phy_clk->regmap);
+       }
+
+       /* USB 2.0 subsystem PSC clock - needed to lock PLL */
+       phy_clk->usb0_clk = clk_get(dev, "usb20");
+       if (IS_ERR(phy_clk->usb0_clk)) {
+               dev_err(dev, "Failed to get usb20 clock\n");
+               return PTR_ERR(phy_clk->usb0_clk);
+       }
+
+       phy_clk->usb0_hw.init = &usb0_phy_clk_init_data;
+       ret = devm_clk_hw_register(dev, &phy_clk->usb0_hw);
+       if (ret) {
+               dev_err(dev, "Failed to register usb0_phy_clk\n");
+               return ret;
+       }
+
+       phy_clk->usb1_hw.init = &usb1_phy_clk_init_data;
+       ret = devm_clk_hw_register(dev, &phy_clk->usb1_hw);
+       if (ret) {
+               dev_err(dev, "Failed to register usb1_phy_clk\n");
+               return ret;
+       }
+
+       parent_name = pdata->usb0_use_refclkin ? "usb_refclkin" : "pll0_aux";
+       parent = devm_clk_get(dev, parent_name);
+       if (IS_ERR(parent)) {
+               dev_err(dev, "Failed to get usb0 parent clock %s\n",
+                       parent_name);
+               return PTR_ERR(parent);
+       }
+
+       ret = clk_set_parent(phy_clk->usb0_hw.clk, parent);
+       if (ret) {
+               dev_err(dev, "Failed to set usb0 parent clock to %s\n",
+                       parent_name);
+               return ret;
+       }
+
+       clk_hw_register_clkdev(&phy_clk->usb0_hw, NULL, "da8xx-cfgchip-clk");
+
+       parent_name = pdata->usb1_use_refclkin ? "usb_refclkin" : 
"usb0_phy_clk";
+       parent = devm_clk_get(dev, parent_name);
+       if (IS_ERR(parent)) {
+               dev_err(dev, "Failed to get usb1 parent clock %s\n",
+                       parent_name);
+               return PTR_ERR(parent);
+       }
+
+       ret = clk_set_parent(phy_clk->usb1_hw.clk, parent);
+       if (ret) {
+               dev_err(dev, "Failed to set usb1 parent clock to %s\n",
+                       parent_name);
+               return ret;
+       }
+
+       clk_hw_register_clkdev(&phy_clk->usb0_hw, "usb20_phy", "da8xx-usb-phy");
+       clk_hw_register_clkdev(&phy_clk->usb1_hw, "usb11_phy", "da8xx-usb-phy");
+
+       return 0;
+}
+
+static struct platform_driver da8xx_cfgchip_clk_driver = {
+       .probe = da8xx_cfgchip_clk_probe,
+       .driver = {
+               .name = "da8xx-cfgchip-clk",
+       },
+};
+module_platform_driver(da8xx_cfgchip_clk_driver);
+
+MODULE_ALIAS("platform:da8xx-cfgchip-clk");
+MODULE_AUTHOR("David Lechner <da...@lechnology.com>");
+MODULE_DESCRIPTION("TI DA8xx CFGCHIP clock driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c
new file mode 100644
index 0000000..035cd91
--- /dev/null
+++ b/drivers/clk/davinci/pll.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock driver for Davinci devices
+ *
+ * Copyright (C) 2017 David Lechner <da...@lechnology.com>
+ *
+ * Based on drivers/clk/keystone/pll.c
+ * Copyright (C) 2013 Texas Instruments Inc.
+ *     Murali Karicheri <m-kariche...@ti.com>
+ *     Santosh Shilimkar <santosh.shilim...@ti.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define REVID          0x000
+#define PLLCTL         0x100
+#define OCSEL          0x104
+#define PLLSECCTL      0x108
+#define PLLM           0x110
+#define PREDIV         0x114
+#define PLLDIV1                0x118
+#define PLLDIV2                0x11c
+#define PLLDIV3                0x120
+#define OSCDIV         0x124
+#define POSTDIV                0x128
+#define BPDIV          0x12c
+#define PLLCMD         0x138
+#define PLLSTAT                0x13c
+#define ALNCTL         0x140
+#define DCHANGE                0x144
+#define CKEN           0x148
+#define CKSTAT         0x14c
+#define SYSTAT         0x150
+#define PLLDIV4                0x160
+#define PLLDIV5                0x164
+#define PLLDIV6                0x168
+#define PLLDIV7                0x16c
+#define PLLDIV8                0x170
+#define PLLDIV9                0x174
+
+#define PLLM_MASK              0x1f
+#define PREDIV_RATIO_MASK      0x1f
+#define PLLDIV_RATIO_WIDTH     5
+#define PLLDIV_ENABLE_SHIFT    15
+#define OSCDIV_RATIO_WIDTH     5
+#define POSTDIV_RATIO_MASK     0x1f
+#define BPDIV_RATIO_SHIFT      0
+#define BPDIV_RATIO_WIDTH      5
+#define CKEN_OBSCLK_SHIFT      1
+#define CKEN_AUXEN_SHIFT       0
+
+/**
+ * struct davinci_pll_clk - Main PLL clock
+ * @hw: clk_hw for the pll
+ * @base: Base memory address
+ * @parent_rate: Saved parent rate used by some child clocks
+ */
+struct davinci_pll_clk {
+       struct clk_hw hw;
+       void __iomem *base;
+};
+
+#define to_davinci_pll_clk(_hw) container_of((_hw), struct davinci_pll_clk, hw)
+
+static unsigned long davinci_pll_clk_recalc(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
+       unsigned long rate = parent_rate;
+       u32 prediv, mult, postdiv;
+
+       prediv = readl(pll->base + PREDIV) & PREDIV_RATIO_MASK;
+       mult = readl(pll->base + PLLM) & PLLM_MASK;
+       postdiv = readl(pll->base + POSTDIV) & POSTDIV_RATIO_MASK;
+
+       rate /= prediv + 1;
+       rate *= mult + 1;
+       rate /= postdiv + 1;
+
+       return rate;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+#define DEBUG_REG(n)   \
+{                      \
+       .name   = #n,   \
+       .offset = n,    \
+}
+
+static const struct debugfs_reg32 davinci_pll_regs[] = {
+       DEBUG_REG(REVID),
+       DEBUG_REG(PLLCTL),
+       DEBUG_REG(OCSEL),
+       DEBUG_REG(PLLSECCTL),
+       DEBUG_REG(PLLM),
+       DEBUG_REG(PREDIV),
+       DEBUG_REG(PLLDIV1),
+       DEBUG_REG(PLLDIV2),
+       DEBUG_REG(PLLDIV3),
+       DEBUG_REG(OSCDIV),
+       DEBUG_REG(POSTDIV),
+       DEBUG_REG(BPDIV),
+       DEBUG_REG(PLLCMD),
+       DEBUG_REG(PLLSTAT),
+       DEBUG_REG(ALNCTL),
+       DEBUG_REG(DCHANGE),
+       DEBUG_REG(CKEN),
+       DEBUG_REG(CKSTAT),
+       DEBUG_REG(SYSTAT),
+       DEBUG_REG(PLLDIV4),
+       DEBUG_REG(PLLDIV5),
+       DEBUG_REG(PLLDIV6),
+       DEBUG_REG(PLLDIV7),
+       DEBUG_REG(PLLDIV8),
+       DEBUG_REG(PLLDIV9),
+};
+
+static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
+{
+       struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
+       struct debugfs_regset32 *regset;
+       struct dentry *d;
+
+       regset = kzalloc(sizeof(regset), GFP_KERNEL);
+       if (!regset)
+               return -ENOMEM;
+
+       regset->regs = davinci_pll_regs;
+       regset->nregs = ARRAY_SIZE(davinci_pll_regs);
+       regset->base = pll->base;
+
+       d = debugfs_create_regset32("registers", 0400, dentry, regset);
+       if (IS_ERR(d)) {
+               kfree(regset);
+               return PTR_ERR(d);
+       }
+
+       return 0;
+}
+#else
+#define davinci_pll_debug_init NULL
+#endif
+
+static const struct clk_ops davinci_pll_clk_ops = {
+       .recalc_rate    = davinci_pll_clk_recalc,
+       .debug_init     = davinci_pll_debug_init,
+};
+
+/**
+ * davinci_pll_clk_register - Register a PLL clock
+ * @name: The clock name
+ * @parent_name: The parent clock name
+ * @base: The PLL's memory region
+ */
+struct clk *davinci_pll_clk_register(const char *name,
+                                    const char *parent_name,
+                                    void __iomem *base)
+{
+       struct clk_init_data init;
+       struct davinci_pll_clk *pll;
+       struct clk *clk;
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (!pll)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &davinci_pll_clk_ops;
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+
+       pll->base = base;
+       pll->hw.init = &init;
+
+       clk = clk_register(NULL, &pll->hw);
+       if (IS_ERR(clk))
+               kfree(pll);
+
+       return clk;
+}
+
+struct davinci_pll_aux_clk {
+       struct clk_hw hw;
+       struct davinci_pll_clk *pll;
+};
+
+/**
+ * davinci_pll_aux_clk_register - Register bypass clock (AUXCLK)
+ * @name: The clock name
+ * @parent_name: The parent clock name (usually "ref_clk" since this bypasses
+ *               the PLL)
+ * @base: The PLL memory region
+ */
+struct clk *davinci_pll_aux_clk_register(const char *name,
+                                        const char *parent_name,
+                                        void __iomem *base)
+{
+       return clk_register_gate(NULL, name, parent_name, 0, base + CKEN,
+                                CKEN_AUXEN_SHIFT, 0, NULL);
+}
+
+/**
+ * davinci_pll_bpdiv_clk_register - Register bypass divider clock (SYSCLKBP)
+ * @name: The clock name
+ * @parent_name: The parent clock name (usually "ref_clk" since this bypasses
+ *               the PLL)
+ * @base: The PLL memory region
+ */
+struct clk *davinci_pll_bpdiv_clk_register(const char *name,
+                                          const char *parent_name,
+                                          void __iomem *base)
+{
+       return clk_register_divider(NULL, name, parent_name, 0, base + BPDIV,
+                                   BPDIV_RATIO_SHIFT, BPDIV_RATIO_WIDTH,
+                                   CLK_DIVIDER_READ_ONLY, NULL);
+}
+
+/**
+ * davinci_pll_obs_clk_register - Register oscillator divider clock (OBSCLK)
+ * @name: The clock name
+ * @parent_names: The parent clock names
+ * @num_parents: The number of paren clocks
+ * @base: The PLL memory region
+ * @table: A table of values cooresponding to the parent clocks (see OCSEL
+ *         register in SRM for values)
+ */
+struct clk *davinci_pll_obs_clk_register(const char *name,
+                                        const char * const *parent_names,
+                                        u8 num_parents,
+                                        void __iomem *base,
+                                        u32 *table)
+{
+       struct clk_mux *mux;
+       struct clk_gate *gate;
+       struct clk_divider *divider;
+       struct clk *clk;
+
+       mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+       if (!mux)
+               return ERR_PTR(-ENOMEM);
+
+       mux->reg = base + OCSEL;
+       mux->table = table;
+
+       gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+       if (!gate) {
+               kfree(mux);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       gate->reg = base + CKEN;
+       gate->bit_idx = CKEN_OBSCLK_SHIFT;
+
+       divider = kzalloc(sizeof(*divider), GFP_KERNEL);
+       if (!divider) {
+               kfree(gate);
+               kfree(mux);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       divider->reg = base + OSCDIV;
+       divider->width = OSCDIV_RATIO_WIDTH;
+
+       clk = clk_register_composite(NULL, name, parent_names, num_parents,
+                                    &mux->hw, &clk_mux_ops,
+                                    &divider->hw, &clk_divider_ops,
+                                    &gate->hw, &clk_gate_ops, 0);
+       if (IS_ERR(clk)) {
+               kfree(divider);
+               kfree(gate);
+               kfree(mux);
+       }
+
+       return clk;
+}
+
+/**
+ * davinci_pll_div_clk_register - Register a PLLDIV (SYSCLK) clock
+ * @name: The clock name
+ * @parent_name: The parent clock name
+ * @base: The PLL memory region
+ * @id: The id of the divider (n in PLLDIVn)
+ */
+struct clk *davinci_pll_div_clk_register(const char *name,
+                                        const char *parent_name,
+                                        void __iomem *base,
+                                        u32 id)
+{
+       const char * const *parent_names = (parent_name ? &parent_name : NULL);
+       int num_parents = (parent_name ? 1 : 0);
+       struct clk_gate *gate;
+       struct clk_divider *divider;
+       struct clk *clk;
+       u32 reg;
+
+       /* PLLDIVn registers are not entirely consecutive */
+       if (id < 4)
+               reg = PLLDIV1 + 4 * (id - 1);
+       else
+               reg = PLLDIV4 + 4 * (id - 4);
+
+       gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+       if (!gate)
+               return ERR_PTR(-ENOMEM);
+
+       gate->reg = base + reg;
+       gate->bit_idx = PLLDIV_ENABLE_SHIFT;
+
+       divider = kzalloc(sizeof(*divider), GFP_KERNEL);
+       if (!divider) {
+               kfree(gate);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       divider->reg = base + reg;
+       divider->width = PLLDIV_RATIO_WIDTH;
+       divider->flags = CLK_DIVIDER_READ_ONLY;
+
+       clk = clk_register_composite(NULL, name, parent_names, num_parents,
+                                    NULL, NULL, &divider->hw, &clk_divider_ops,
+                                    &gate->hw, &clk_gate_ops, 0);
+       if (IS_ERR(clk)) {
+               kfree(divider);
+               kfree(gate);
+       }
+
+       return clk;
+}
diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c
new file mode 100644
index 0000000..8ae85ee
--- /dev/null
+++ b/drivers/clk/davinci/psc.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x PSC controllers
+ *
+ * Copyright (C) 2017 David Lechner <da...@lechnology.com>
+ *
+ * Based on: drivers/clk/keystone/gate.c
+ * Copyright (C) 2013 Texas Instruments.
+ *     Murali Karicheri <m-kariche...@ti.com>
+ *     Santosh Shilimkar <santosh.shilim...@ti.com>
+ *
+ * And: arch/arm/mach-davinci/psc.c
+ * Copyright (C) 2006 Texas Instruments.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/* PSC register offsets */
+#define EPCPR                  0x070
+#define PTCMD                  0x120
+#define PTSTAT                 0x128
+#define PDSTAT                 0x200
+#define PDCTL                  0x300
+#define MDSTAT                 0x800
+#define MDCTL                  0xa00
+
+/* PSC module states */
+enum davinci_psc_state {
+       PSC_STATE_SWRSTDISABLE  = 0,
+       PSC_STATE_SYNCRST       = 1,
+       PSC_STATE_DISABLE       = 2,
+       PSC_STATE_ENABLE        = 3,
+};
+
+#define MDSTAT_STATE_MASK      0x3f
+#define MDSTAT_MCKOUT          BIT(12)
+#define PDSTAT_STATE_MASK      0x1f
+#define MDCTL_FORCE            BIT(31)
+#define MDCTL_LRESET           BIT(8)
+#define PDCTL_EPCGOOD          BIT(8)
+#define PDCTL_NEXT             BIT(0)
+
+/**
+ * struct davinci_psc_clk - PSC clock structure
+ * @hw: clk_hw for the psc
+ * @psc_data: PSC driver specific data
+ * @lpsc: Local PSC number (module id)
+ * @pd: Power domain
+ */
+struct davinci_psc_clk {
+       struct clk_hw hw;
+       void __iomem *base;
+       u32 lpsc;
+       u32 pd;
+};
+
+#define to_davinci_psc_clk(_hw) container_of(_hw, struct davinci_psc_clk, hw)
+
+static void psc_config(struct davinci_psc_clk *psc,
+                      enum davinci_psc_state next_state)
+{
+       u32 epcpr, ptcmd, pdstat, pdctl, mdstat, mdctl, ptstat;
+
+       mdctl = readl(psc->base + MDCTL + 4 * psc->lpsc);
+       mdctl &= ~MDSTAT_STATE_MASK;
+       mdctl |= next_state;
+       /* TODO: old davinci clocks for da850 set MDCTL_FORCE bit for sata and
+        * dsp here. Is this really needed?
+        */
+       writel(mdctl, psc->base + MDCTL + 4 * psc->lpsc);
+
+       pdstat = readl(psc->base + PDSTAT + 4 * psc->pd);
+       if ((pdstat & PDSTAT_STATE_MASK) == 0) {
+               pdctl = readl(psc->base + PDSTAT + 4 * psc->pd);
+               pdctl |= PDCTL_NEXT;
+               writel(pdctl, psc->base + PDSTAT + 4 * psc->pd);
+
+               ptcmd = BIT(psc->pd);
+               writel(ptcmd, psc->base + PTCMD);
+
+               do {
+                       epcpr = __raw_readl(psc->base + EPCPR);
+               } while (!(epcpr & BIT(psc->pd)));
+
+               pdctl = __raw_readl(psc->base + PDCTL + 4 * psc->pd);
+               pdctl |= PDCTL_EPCGOOD;
+               __raw_writel(pdctl, psc->base + PDCTL + 4 * psc->pd);
+       } else {
+               ptcmd = BIT(psc->pd);
+               writel(ptcmd, psc->base + PTCMD);
+       }
+
+       do {
+               ptstat = readl(psc->base + PTSTAT);
+       } while (ptstat & BIT(psc->pd));
+
+       do {
+               mdstat = readl(psc->base + MDSTAT + 4 * psc->lpsc);
+       } while (!((mdstat & MDSTAT_STATE_MASK) == next_state));
+}
+
+static int davinci_psc_clk_enable(struct clk_hw *hw)
+{
+       struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
+
+       psc_config(psc, PSC_STATE_ENABLE);
+
+       return 0;
+}
+
+static void davinci_psc_clk_disable(struct clk_hw *hw)
+{
+       struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
+
+       psc_config(psc, PSC_STATE_DISABLE);
+}
+
+static int davinci_psc_clk_is_enabled(struct clk_hw *hw)
+{
+       struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
+       u32 mdstat;
+
+       mdstat = readl(psc->base + MDSTAT + 4 * psc->lpsc);
+
+       return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
+}
+
+static const struct clk_ops davinci_psc_clk_ops = {
+       .enable         = davinci_psc_clk_enable,
+       .disable        = davinci_psc_clk_disable,
+       .is_enabled     = davinci_psc_clk_is_enabled,
+};
+
+/**
+ * davinci_psc_clk_register - register psc clock
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @base: memory mapped register for the PSC
+ * @lpsc: local PSC number
+ * @pd: power domain
+ */
+struct clk *davinci_psc_clk_register(const char *name,
+                                    const char *parent_name,
+                                    void __iomem *base,
+                                    u32 lpsc, u32 pd)
+{
+       struct clk_init_data init;
+       struct davinci_psc_clk *psc;
+       struct clk *clk;
+
+       psc = kzalloc(sizeof(*psc), GFP_KERNEL);
+       if (!psc)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &davinci_psc_clk_ops;
+       init.flags = 0;
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+
+       psc->base = base;
+       psc->hw.init = &init;
+       psc->lpsc = lpsc;
+       psc->pd = pd;
+
+       clk = clk_register(NULL, &psc->hw);
+       if (IS_ERR(clk))
+               kfree(psc);
+
+       return clk;
+}
+
+/* FIXME: This needs to be converted to a reset controller. But, the reset
+ * framework is currently device tree only.
+ */
+
+DEFINE_SPINLOCK(davinci_psc_reset_lock);
+
+static int davinci_psc_clk_reset(struct davinci_psc_clk *psc, bool reset)
+{
+       unsigned long flags;
+       u32 mdctl;
+
+       if (IS_ERR_OR_NULL(psc))
+               return -EINVAL;
+
+       spin_lock_irqsave(&davinci_psc_reset_lock, flags);
+       mdctl = readl(psc->base + MDCTL + 4 * psc->lpsc);
+       if (reset)
+               mdctl &= ~MDCTL_LRESET;
+       else
+               mdctl |= MDCTL_LRESET;
+       writel(mdctl, psc->base + MDCTL + 4 * psc->lpsc);
+       spin_unlock_irqrestore(&davinci_psc_reset_lock, flags);
+
+       return 0;
+}
+
+int davinci_clk_reset_assert(struct clk *clk)
+{
+       struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
+
+       return davinci_psc_clk_reset(psc, true);
+}
+EXPORT_SYMBOL(davinci_clk_reset_assert);
+
+int davinci_clk_reset_deassert(struct clk *clk)
+{
+       struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
+
+       return davinci_psc_clk_reset(psc, false);
+}
+EXPORT_SYMBOL(davinci_clk_reset_deassert);
diff --git a/include/linux/clk/davinci.h b/include/linux/clk/davinci.h
new file mode 100644
index 0000000..c5d2181
--- /dev/null
+++ b/include/linux/clk/davinci.h
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI Davinci clocks
+ *
+ * Copyright (C) 2017 David Lechner <da...@lechnology.com>
+ */
+#ifndef __LINUX_CLK_DAVINCI_H__
+#define __LINUX_CLK_DAVINCI_H__
+
+#include <linux/clk-provider.h>
+#include <linux/types.h>
+
+struct clk *davinci_pll_clk_register(const char *name,
+                                    const char *parent_name,
+                                    void __iomem *base);
+struct clk *davinci_pll_aux_clk_register(const char *name,
+                                        const char *parent_name,
+                                        void __iomem *base);
+struct clk *davinci_pll_bpdiv_clk_register(const char *name,
+                                          const char *parent_name,
+                                          void __iomem *base);
+struct clk *davinci_pll_obs_clk_register(const char *name,
+                                        const char * const *parent_names,
+                                        u8 num_parents,
+                                        void __iomem *base,
+                                        u32 *table);
+struct clk *davinci_pll_div_clk_register(const char *name,
+                                        const char *parent_name,
+                                        void __iomem *base,
+                                        u32 id);
+struct clk *davinci_psc_clk_register(const char *name,
+                                    const char *parent_name,
+                                    void __iomem *base,
+                                    u32 lpsc, u32 pd);
+
+/* convience macros for board declaration files */
+#define EXT_CLK(n, r) clk_register_fixed_rate(NULL, (n), NULL, 0, (r))
+#define FIX_CLK(n, p) clk_register_fixed_factor(NULL, (n), (p), 0, 1, 1)
+#define PLL_CLK davinci_pll_clk_register
+#define PLL_DIV_CLK davinci_pll_div_clk_register
+#define PLL_AUX_CLK davinci_pll_aux_clk_register
+#define PLL_BP_CLK davinci_pll_bpdiv_clk_register
+#define PLL_OBS_CLK davinci_pll_obs_clk_register
+#define PSC_CLK davinci_psc_clk_register
+
+#endif /* __LINUX_CLK_DAVINCI_H__ */
diff --git a/include/linux/platform_data/davinci_clk.h 
b/include/linux/platform_data/davinci_clk.h
new file mode 100644
index 0000000..7576ace
--- /dev/null
+++ b/include/linux/platform_data/davinci_clk.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI DaVinci Clock support
+ *
+ * Copyright (C) 2017 David Lechner <da...@lechnology.com>
+ */
+
+#ifndef __PLATFORM_DATA_DAVINCI_CLK_H
+#define __PLATFORM_DATA_DAVINCI_CLK_H
+
+#include <linux/types.h>
+
+/**
+ * da8xx_cfgchip_clk_data - DA8xx CFGCHIP clock platform data
+ * @usb0_use_refclkin: when true, use USB_REFCLKIN, otherwise use AUXCLK for
+ *                     USB 2.0 PHY clock
+ * @usb1_use_refclkin: when true, use USB_REFCLKIN, otherwise use USB 2.0 PHY
+ *                     PLL for USB 1.1 PHY clock
+ */
+struct da8xx_cfgchip_clk_data {
+       bool usb0_use_refclkin;
+       bool usb1_use_refclkin;
+};
+
+#endif /* __PLATFORM_DATA_DAVINCI_CLK_H */


Reply via email to