[RFC] MIPS: BCM63XX: switch to common clock and Device Tree

From: Jonas Gorski
Date: Sun Nov 11 2012 - 07:50:41 EST


Switch BCM63XX to the common clock framework and use clkdev for
providing clock name lookups for non-DT devices.

Clocks can have a frequency and gate-bit, or none, in case they
are just provided for drivers expecting them to be present.

Signed-off-by: Jonas Gorski <jonas.gorski@xxxxxxxxx>
---
.../devicetree/bindings/clock/bcm63xx-clock.txt | 32 ++
arch/mips/Kconfig | 3 +-
arch/mips/bcm63xx/Makefile | 7 +-
arch/mips/bcm63xx/clk.c | 331 --------------------
arch/mips/include/asm/mach-bcm63xx/bcm63xx_clk.h | 11 -
drivers/clk/Makefile | 1 +
drivers/clk/clk-bcm63xx.c | 241 ++++++++++++++
7 files changed, 279 insertions(+), 347 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/bcm63xx-clock.txt
delete mode 100644 arch/mips/bcm63xx/clk.c
delete mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_clk.h
create mode 100644 drivers/clk/clk-bcm63xx.c

diff --git a/Documentation/devicetree/bindings/clock/bcm63xx-clock.txt b/Documentation/devicetree/bindings/clock/bcm63xx-clock.txt
new file mode 100644
index 0000000..467c0c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/bcm63xx-clock.txt
@@ -0,0 +1,32 @@
+* Broadcom BCM63XX Clock bindings
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible: one of
+ a) "brcm,bcm63xx-clock"
+ Standard BCM63XX clock.
+ b) "brcm,bcm63xx-sar-clock"
+ SAR/ATM clock, which requires a reset of the SAR/ATM block.
+ c) "brcm,bcm63xx-enetsw-clock"
+ Generic ethernet switch clock, which requires a reset of the block.
+ d) "brcm,bcm6368-enetsw-clock"
+ BCM6368 ethernet switch clock, which requires additional clocks to be
+ enabled during reset.
+
+Optional properties:
+- brcm,gate-bit: gate bit in the clock control register.
+
+- clock-frequency: frequency of this clock.
+
+Example:
+
+ hsspi: clock@9 {
+ compatible = "brcm,bcm63xx-clock";
+ #clock-cells = <0>;
+ clock-output-names = "hsspi";
+ brcm,gate-bit = <9>;
+ clock-frequency = <133333333>;
+ };
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 168b0fc..1203113 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -131,7 +131,8 @@ config BCM63XX
select SYS_HAS_EARLY_PRINTK
select SWAP_IO_SPACE
select ARCH_REQUIRE_GPIOLIB
- select HAVE_CLK
+ select COMMON_CLK
+ select CLKDEV
select USE_OF
help
Support for BCM63XX based boards
diff --git a/arch/mips/bcm63xx/Makefile b/arch/mips/bcm63xx/Makefile
index 30971a7..994893c 100644
--- a/arch/mips/bcm63xx/Makefile
+++ b/arch/mips/bcm63xx/Makefile
@@ -1,7 +1,6 @@
-obj-y += clk.o cpu.o cs.o gpio.o irq.o nvram.o prom.o reset.o \
- setup.o timer.o dev-dsp.o dev-enet.o dev-flash.o \
- dev-pcmcia.o dev-rng.o dev-spi.o dev-uart.o dev-wdt.o \
- dev-usb-usbd.o
+obj-y += cpu.o cs.o gpio.o irq.o nvram.o prom.o reset.o setup.o \
+ timer.o dev-dsp.o dev-enet.o dev-flash.o dev-pcmcia.o \
+ dev-rng.o dev-spi.o dev-uart.o dev-wdt.o dev-usb-usbd.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o

obj-y += boards/
diff --git a/arch/mips/bcm63xx/clk.c b/arch/mips/bcm63xx/clk.c
deleted file mode 100644
index b9e948d..0000000
--- a/arch/mips/bcm63xx/clk.c
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2008 Maxime Bizon <mbizon@xxxxxxxxxx>
- */
-
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <bcm63xx_cpu.h>
-#include <bcm63xx_io.h>
-#include <bcm63xx_regs.h>
-#include <bcm63xx_reset.h>
-#include <bcm63xx_clk.h>
-
-static DEFINE_MUTEX(clocks_mutex);
-
-
-static void clk_enable_unlocked(struct clk *clk)
-{
- if (clk->set && (clk->usage++) == 0)
- clk->set(clk, 1);
-}
-
-static void clk_disable_unlocked(struct clk *clk)
-{
- if (clk->set && (--clk->usage) == 0)
- clk->set(clk, 0);
-}
-
-static void bcm_hwclock_set(u32 mask, int enable)
-{
- u32 reg;
-
- reg = bcm_perf_readl(PERF_CKCTL_REG);
- if (enable)
- reg |= mask;
- else
- reg &= ~mask;
- bcm_perf_writel(reg, PERF_CKCTL_REG);
-}
-
-/*
- * Ethernet MAC "misc" clock: dma clocks and main clock on 6348
- */
-static void enet_misc_set(struct clk *clk, int enable)
-{
- u32 mask;
-
- if (BCMCPU_IS_6338())
- mask = CKCTL_6338_ENET_EN;
- else if (BCMCPU_IS_6345())
- mask = CKCTL_6345_ENET_EN;
- else if (BCMCPU_IS_6348())
- mask = CKCTL_6348_ENET_EN;
- else
- /* BCMCPU_IS_6358 */
- mask = CKCTL_6358_EMUSB_EN;
- bcm_hwclock_set(mask, enable);
-}
-
-static struct clk clk_enet_misc = {
- .set = enet_misc_set,
-};
-
-/*
- * Ethernet MAC clocks: only revelant on 6358, silently enable misc
- * clocks
- */
-static void enetx_set(struct clk *clk, int enable)
-{
- if (enable)
- clk_enable_unlocked(&clk_enet_misc);
- else
- clk_disable_unlocked(&clk_enet_misc);
-
- if (BCMCPU_IS_6358()) {
- u32 mask;
-
- if (clk->id == 0)
- mask = CKCTL_6358_ENET0_EN;
- else
- mask = CKCTL_6358_ENET1_EN;
- bcm_hwclock_set(mask, enable);
- }
-}
-
-static struct clk clk_enet0 = {
- .id = 0,
- .set = enetx_set,
-};
-
-static struct clk clk_enet1 = {
- .id = 1,
- .set = enetx_set,
-};
-
-/*
- * Ethernet PHY clock
- */
-static void ephy_set(struct clk *clk, int enable)
-{
- if (!BCMCPU_IS_6358())
- return;
- bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable);
-}
-
-
-static struct clk clk_ephy = {
- .set = ephy_set,
-};
-
-/*
- * Ethernet switch clock
- */
-static void enetsw_set(struct clk *clk, int enable)
-{
- if (!BCMCPU_IS_6368())
- return;
- bcm_hwclock_set(CKCTL_6368_ROBOSW_EN |
- CKCTL_6368_SWPKT_USB_EN |
- CKCTL_6368_SWPKT_SAR_EN, enable);
- if (enable) {
- /* reset switch core afer clock change */
- bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 1);
- msleep(10);
- bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 0);
- msleep(10);
- }
-}
-
-static struct clk clk_enetsw = {
- .set = enetsw_set,
-};
-
-/*
- * PCM clock
- */
-static void pcm_set(struct clk *clk, int enable)
-{
- if (!BCMCPU_IS_6358())
- return;
- bcm_hwclock_set(CKCTL_6358_PCM_EN, enable);
-}
-
-static struct clk clk_pcm = {
- .set = pcm_set,
-};
-
-/*
- * USB host clock
- */
-static void usbh_set(struct clk *clk, int enable)
-{
- if (BCMCPU_IS_6328())
- bcm_hwclock_set(CKCTL_6328_USBH_EN, enable);
- else if (BCMCPU_IS_6348())
- bcm_hwclock_set(CKCTL_6348_USBH_EN, enable);
- else if (BCMCPU_IS_6368())
- bcm_hwclock_set(CKCTL_6368_USBH_EN, enable);
-}
-
-static struct clk clk_usbh = {
- .set = usbh_set,
-};
-
-/*
- * USB device clock
- */
-static void usbd_set(struct clk *clk, int enable)
-{
- if (BCMCPU_IS_6328())
- bcm_hwclock_set(CKCTL_6328_USBD_EN, enable);
- else if (BCMCPU_IS_6368())
- bcm_hwclock_set(CKCTL_6368_USBD_EN, enable);
-}
-
-static struct clk clk_usbd = {
- .set = usbd_set,
-};
-
-/*
- * SPI clock
- */
-static void spi_set(struct clk *clk, int enable)
-{
- u32 mask;
-
- if (BCMCPU_IS_6338())
- mask = CKCTL_6338_SPI_EN;
- else if (BCMCPU_IS_6348())
- mask = CKCTL_6348_SPI_EN;
- else if (BCMCPU_IS_6358())
- mask = CKCTL_6358_SPI_EN;
- else
- /* BCMCPU_IS_6368 */
- mask = CKCTL_6368_SPI_EN;
- bcm_hwclock_set(mask, enable);
-}
-
-static struct clk clk_spi = {
- .set = spi_set,
-};
-
-/*
- * XTM clock
- */
-static void xtm_set(struct clk *clk, int enable)
-{
- if (!BCMCPU_IS_6368())
- return;
-
- bcm_hwclock_set(CKCTL_6368_SAR_EN |
- CKCTL_6368_SWPKT_SAR_EN, enable);
-
- if (enable) {
- /* reset sar core afer clock change */
- bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 1);
- mdelay(1);
- bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 0);
- mdelay(1);
- }
-}
-
-
-static struct clk clk_xtm = {
- .set = xtm_set,
-};
-
-/*
- * IPsec clock
- */
-static void ipsec_set(struct clk *clk, int enable)
-{
- bcm_hwclock_set(CKCTL_6368_IPSEC_EN, enable);
-}
-
-static struct clk clk_ipsec = {
- .set = ipsec_set,
-};
-
-/*
- * PCIe clock
- */
-
-static void pcie_set(struct clk *clk, int enable)
-{
- bcm_hwclock_set(CKCTL_6328_PCIE_EN, enable);
-}
-
-static struct clk clk_pcie = {
- .set = pcie_set,
-};
-
-/*
- * Internal peripheral clock
- */
-static struct clk clk_periph = {
- .rate = (50 * 1000 * 1000),
-};
-
-
-/*
- * Linux clock API implementation
- */
-int clk_enable(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- clk_enable_unlocked(clk);
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-
-EXPORT_SYMBOL(clk_enable);
-
-void clk_disable(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- clk_disable_unlocked(clk);
- mutex_unlock(&clocks_mutex);
-}
-
-EXPORT_SYMBOL(clk_disable);
-
-unsigned long clk_get_rate(struct clk *clk)
-{
- return clk->rate;
-}
-
-EXPORT_SYMBOL(clk_get_rate);
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- if (!strcmp(id, "enet0"))
- return &clk_enet0;
- if (!strcmp(id, "enet1"))
- return &clk_enet1;
- if (!strcmp(id, "enetsw"))
- return &clk_enetsw;
- if (!strcmp(id, "ephy"))
- return &clk_ephy;
- if (!strcmp(id, "usbh"))
- return &clk_usbh;
- if (!strcmp(id, "usbd"))
- return &clk_usbd;
- if (!strcmp(id, "spi"))
- return &clk_spi;
- if (!strcmp(id, "xtm"))
- return &clk_xtm;
- if (!strcmp(id, "periph"))
- return &clk_periph;
- if (BCMCPU_IS_6358() && !strcmp(id, "pcm"))
- return &clk_pcm;
- if (BCMCPU_IS_6368() && !strcmp(id, "ipsec"))
- return &clk_ipsec;
- if (BCMCPU_IS_6328() && !strcmp(id, "pcie"))
- return &clk_pcie;
- return ERR_PTR(-ENOENT);
-}
-
-EXPORT_SYMBOL(clk_get);
-
-void clk_put(struct clk *clk)
-{
-}
-
-EXPORT_SYMBOL(clk_put);
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_clk.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_clk.h
deleted file mode 100644
index 8fcf8df..0000000
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_clk.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef BCM63XX_CLK_H_
-#define BCM63XX_CLK_H_
-
-struct clk {
- void (*set)(struct clk *, int);
- unsigned int rate;
- unsigned int usage;
- int id;
-};
-
-#endif /* ! BCM63XX_CLK_H_ */
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 71a25b9..c991c8b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -19,6 +19,7 @@ endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
+obj-$(CONFIG_BCM63XX) += clk-bcm63xx.o

# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/clk-bcm63xx.c b/drivers/clk/clk-bcm63xx.c
new file mode 100644
index 0000000..571bb71
--- /dev/null
+++ b/drivers/clk/clk-bcm63xx.c
@@ -0,0 +1,241 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Jonas Gorski <jonas.gorski@xxxxxxxxx>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_reset.h>
+
+DEFINE_SPINLOCK(bcm63xx_clk_lock);
+
+struct bcm63xx_clk {
+ struct clk_hw hw;
+ u32 rate;
+ s8 gate_bit;
+ void (*reset)(void);
+};
+
+#define to_bcm63xx_clk(p) container_of(p, struct bcm63xx_clk, hw)
+
+static void bcm63xx_clk_set(u32 bit, int enable)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&bcm63xx_clk_lock, flags);
+
+ val = bcm_perf_readl(PERF_CKCTL_REG);
+ if (enable)
+ val |= BIT(bit);
+ else
+ val &= ~BIT(bit);
+
+ bcm_perf_writel(val, PERF_CKCTL_REG);
+
+ spin_unlock_irqrestore(&bcm63xx_clk_lock, flags);
+
+}
+
+static int bcm63xx_clk_enable(struct clk_hw *hw)
+{
+ struct bcm63xx_clk *clk = to_bcm63xx_clk(hw);
+
+ if (clk->gate_bit >= 0)
+ bcm63xx_clk_set(clk->gate_bit, 1);
+
+ if (clk->reset)
+ clk->reset();
+
+ return 0;
+}
+
+static void bcm63xx_clk_disable(struct clk_hw *hw)
+{
+ struct bcm63xx_clk *clk = to_bcm63xx_clk(hw);
+
+ if (clk->gate_bit >= 0)
+ bcm63xx_clk_set(clk->gate_bit, 0);
+}
+
+static int bcm63xx_clk_is_enabled(struct clk_hw *hw)
+{
+ struct bcm63xx_clk *clk = to_bcm63xx_clk(hw);
+
+ if (clk->gate_bit >= 0)
+ return bcm_perf_readl(PERF_CKCTL_REG) & BIT(clk->gate_bit);
+
+ return 1;
+}
+
+static unsigned long bcm63xx_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_state)
+{
+ return to_bcm63xx_clk(hw)->rate;
+}
+
+static const struct clk_ops bcm63xx_clk_ops = {
+ .enable = bcm63xx_clk_enable,
+ .disable = bcm63xx_clk_disable,
+ .is_enabled = bcm63xx_clk_is_enabled,
+ .recalc_rate = bcm63xx_clk_recalc_rate,
+};
+
+static void bcm63xx_enetsw_reset(void)
+{
+ bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 1);
+ mdelay(100);
+ bcm63xx_core_set_reset(BCM63XX_RESET_ENETSW, 0);
+ mdelay(100);
+}
+
+static void bcm6368_enetsw_reset(void)
+{
+ struct clk *enetsw_sar = clk_get(NULL, "enetsw-sar");
+ struct clk *enetsw_usb = clk_get(NULL, "enetsw-usb");
+
+ /* secondary clocks need to be enabled while resetting the core */
+ clk_prepare_enable(enetsw_sar);
+ clk_prepare_enable(enetsw_usb);
+
+ bcm63xx_enetsw_reset();
+
+ clk_disable_unprepare(enetsw_usb);
+ clk_disable_unprepare(enetsw_sar);
+
+ clk_put(enetsw_sar);
+ clk_put(enetsw_usb);
+}
+
+static void bcm63xx_sar_reset(void)
+{
+ bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 1);
+ mdelay(1);
+ bcm63xx_core_set_reset(BCM63XX_RESET_SAR, 0);
+ mdelay(1);
+}
+
+static void __init bcm63xx_clock_init(struct device_node *node,
+ void (*reset)(void))
+{
+ u32 gate_bit_dt, rate = 0;
+ s8 gate_bit = -1;
+ struct clk *clk;
+ struct bcm63xx_clk *bcm63xx_clk;
+ const char *clk_name = node->name;
+ const char *parent = NULL;
+ int num_names, i;
+ struct clk_init_data init;
+
+ if (!of_property_read_u32(node, "brcm,gate-bit", &gate_bit_dt) &&
+ !WARN_ON(gate_bit_dt > 32))
+ gate_bit = gate_bit_dt;
+
+ of_property_read_u32(node, "clock-frequency", &rate);
+
+ num_names = of_property_count_strings(node, "clock-output-names");
+
+ if (!WARN_ON(num_names == 0))
+ of_property_read_string_index(node, "clock-output-names", 0,
+ &clk_name);
+
+ parent = of_clk_get_parent_name(node, 0);
+
+ bcm63xx_clk = kzalloc(sizeof(*bcm63xx_clk), GFP_KERNEL);
+ if (!bcm63xx_clk)
+ return;
+
+ bcm63xx_clk->rate = rate;
+ bcm63xx_clk->gate_bit = gate_bit;
+ bcm63xx_clk->reset = reset;
+
+ init.name = clk_name;
+ init.ops = &bcm63xx_clk_ops;
+
+ if (parent) {
+ init.flags = 0;
+ init.num_parents = 1;
+ init.parent_names = &parent;
+ } else {
+ init.flags = CLK_IS_ROOT;
+ init.num_parents = 0;
+ init.parent_names = NULL;
+ }
+
+ bcm63xx_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &bcm63xx_clk->hw);
+ if (IS_ERR(clk)) {
+ kfree(bcm63xx_clk);
+ return;
+ }
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+
+ /* register aliases */
+ for (i = 1; i < num_names; i++) {
+ of_property_read_string_index(node, "clock-output-names", i,
+ &clk_name);
+ clk_register_clkdev(clk, clk_name, NULL);
+ }
+}
+
+static void __init bcm63xx_generic_clock_init(struct device_node *node)
+{
+ bcm63xx_clock_init(node, NULL);
+}
+
+static void __init bcm63xx_enetsw_clock_init(struct device_node *node)
+{
+ bcm63xx_clock_init(node, bcm63xx_enetsw_reset);
+}
+
+static void __init bcm6368_enetsw_clock_init(struct device_node *node)
+{
+ bcm63xx_clock_init(node, bcm6368_enetsw_reset);
+}
+
+static void __init bcm63xx_sar_clock_init(struct device_node *node)
+{
+ bcm63xx_clock_init(node, bcm63xx_sar_reset);
+}
+
+static const __initconst struct of_device_id clk_match[] = {
+ {
+ .compatible = "brcm,bcm63xx-clock",
+ .data = bcm63xx_generic_clock_init,
+ },
+ {
+ .compatible = "brcm,bcm63xx-enetsw-clock",
+ .data = bcm63xx_enetsw_clock_init,
+ },
+ {
+ .compatible = "brcm,bcm6368-enetsw-clock",
+ .data = bcm63xx_enetsw_clock_init,
+ },
+ {
+ .compatible = "brcm,bcm63xx-sar-clock",
+ .data = bcm63xx_sar_clock_init,
+ },
+};
+
+int __init bcm63xx_clocks_init(void)
+{
+ of_clk_init(clk_match);
+
+ return 0;
+}
+arch_initcall(bcm63xx_clocks_init);
--
1.7.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/