All of lore.kernel.org
 help / color / mirror / Atom feed
From: zoss@devai.org (Zoltan Devai)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 3/9] ARM: SPMP8000: Add clk support
Date: Sun,  9 Oct 2011 18:36:06 +0200	[thread overview]
Message-ID: <1318178172-7965-4-git-send-email-zoss@devai.org> (raw)
In-Reply-To: <1318178172-7965-1-git-send-email-zoss@devai.org>

Signed-off-by: Zoltan Devai <zoss@devai.org>
---
 arch/arm/mach-spmp8000/clkdev.c             |  586 +++++++++++++++++++++++++++
 arch/arm/mach-spmp8000/clock.c              |  155 +++++++
 arch/arm/mach-spmp8000/include/mach/clock.h |   37 ++
 3 files changed, 778 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-spmp8000/clkdev.c
 create mode 100644 arch/arm/mach-spmp8000/clock.c
 create mode 100644 arch/arm/mach-spmp8000/include/mach/clock.h

diff --git a/arch/arm/mach-spmp8000/clkdev.c b/arch/arm/mach-spmp8000/clkdev.c
new file mode 100644
index 0000000..c492d20
--- /dev/null
+++ b/arch/arm/mach-spmp8000/clkdev.c
@@ -0,0 +1,586 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <mach/clock.h>
+#include <mach/scu.h>
+
+/* The SoC doesn't support anything else, so no need to make these machine
+ * configurable.
+ */
+#define XTAL_FREQ		27000000UL
+#define XTAL_32K_FREQ		32768UL
+#define APLL_48K_FREQ		73728000UL
+#define APLL_44K_FREQ		67737600UL
+
+static unsigned long spll_get_rate(struct clk *clk)
+{
+	unsigned int f, r, od;
+	unsigned long osc;
+	u32 cfg;
+
+	cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	osc = XTAL_FREQ;
+
+	/* SPLL off ? */
+	if ((cfg & (SPLL_CFG_XR | SPLL_CFG_PD | SPLL_CFG_BP)) ||
+		(~cfg & (SPLL_CFG_PL | SPLL_CFG_XE | SPLL_CFG_SE))) {
+		return 0;
+	}
+
+	f = (cfg & SPLL_CFG_F_MASK) >> SPLL_CFG_F_SHIFT;
+	r = (cfg & SPLL_CFG_R_MASK) >> SPLL_CFG_R_SHIFT;
+	od = !!(cfg & SPLL_CFG_OD);
+	clk->rate = (osc * (f + 1) * 2) / ((r + 1) * (od + 1));
+
+	return clk->rate;
+}
+
+static unsigned long pll_mux_get_rate(int ratesel, struct clk *clk)
+{
+	int pll_rate;
+
+	switch (ratesel) {
+	case 0:
+		clk->rate = XTAL_FREQ;
+		break;
+	case 1:
+		clk->rate = XTAL_32K_FREQ;
+		break;
+	case 2:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 2;
+		break;
+	case 3:
+		pll_rate = clk_get_rate(clk->parent);
+		clk->rate = pll_rate / 3;
+		break;
+	}
+
+	return clk->rate;
+}
+
+static unsigned long ref_arm_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	ratesel = (spll_cfg & SPLL_CFG_ASEL_MASK) >> SPLL_CFG_ASEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long ref_ceva_get_rate(struct clk *clk)
+{
+	int ratesel;
+	u32 spll_cfg;
+
+	spll_cfg = readl(REG_SCU_B(SCU_B_SPLL_CFG));
+	ratesel = (spll_cfg & SPLL_CFG_CSEL_MASK) >> SPLL_CFG_CSEL_SHIFT;
+	return pll_mux_get_rate(ratesel, clk);
+}
+
+static unsigned long apll_get_rate(void)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	if ((cfg & APLL_CFG_R) || (~cfg & (APLL_CFG_P | APLL_CFG_E)) ||
+		(((cfg & APLL_CFG_AS_MASK) >> APLL_CFG_AS_SHIFT)
+			!= APLL_CFG_AS_MAGIC)) {
+		return 0;
+	}
+
+	/* Not dealing with the input oscillator frequency as the settings
+	 * for non-27Mhz are unknown, and all platforms use that anyway */
+	if (cfg & APLL_CFG_S)
+		return APLL_44K_FREQ;
+	else
+		return APLL_48K_FREQ;
+}
+
+static void apll_enable(struct clk *clk)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	/* Store new config with reset, then disable reset */
+	cfg |= APLL_CFG_R;
+	cfg |= APLL_CFG_E | APLL_CFG_P | APLL_CFG_F;
+	cfg |= APLL_CFG_AS_MAGIC << APLL_CFG_AS_SHIFT;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	cfg &= ~APLL_CFG_R;
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static void apll_disable(struct clk *clk)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	cfg &= ~APLL_CFG_P;
+	cfg |= APLL_CFG_E;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static int apll_set_rate(unsigned long rate)
+{
+	u32 cfg;
+
+	cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	switch (rate) {
+	case 67737600:
+		cfg |= APLL_CFG_S;
+		break;
+	case 73728000:
+		cfg &= ~APLL_CFG_S;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	return 0;
+}
+
+static void i2s_mck_switch(u32 mask, int enable)
+{
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+	if (enable)
+		cfg &= ~mask;
+	else
+		cfg |= mask;
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+}
+
+static void i2stx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 1);
+}
+
+static void i2stx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_DS, 0);
+}
+
+static void i2srx_mck_enable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 1);
+}
+
+static void i2srx_mck_disable(struct clk *clk)
+{
+	i2s_mck_switch(APLL_CFG_AS, 0);
+}
+
+static const int apll_dividers[7] = {
+	3, 6, 9, 12, 18, 24, 32,
+};
+
+static unsigned long i2s_mck_get_rate(struct clk *clk, u32 msk, u32 sh)
+{
+	unsigned long apll_rate = apll_get_rate();
+	u32 cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+	int divider;
+
+	divider = (cfg & msk) >> sh;
+
+	if (divider == ARRAY_SIZE(apll_dividers))
+		return 0;
+
+	divider = apll_dividers[divider];
+
+	return apll_rate / divider;
+}
+
+static unsigned long i2stx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static unsigned long i2srx_mck_get_rate(struct clk *clk)
+{
+	return i2s_mck_get_rate(clk, APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static int i2s_mck_set_rate(struct clk *clk, unsigned long rate,
+					u32 msk, u32 sh)
+{
+	int i = ARRAY_SIZE(apll_dividers) - 1;
+	unsigned long apll_rate;
+	int divider = -1;
+	u32 cfg;
+
+	/* Set up APLL */
+	if (rate % 8000)
+		apll_rate = APLL_44K_FREQ;
+	else
+		apll_rate = APLL_48K_FREQ;
+
+	apll_set_rate(apll_rate);
+
+	cfg = readl(REG_SCU_A(SCU_A_APLL_CFG));
+
+	/* Get the biggest possible divider for MCK */
+	while (i >= 0) {
+		if (apll_rate / apll_dividers[i] == rate) {
+			divider = apll_dividers[i];
+			break;
+		}
+
+		i--;
+	}
+
+	if (divider < 0)
+		return -EINVAL;
+
+	cfg &= ~msk;
+	cfg |= (i << sh);
+
+	writel(cfg, REG_SCU_A(SCU_A_APLL_CFG));
+
+	return 0;
+}
+
+static int i2stx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_DAR_MASK, APLL_CFG_DAR_SHIFT);
+}
+
+static int i2srx_mck_set_rate(struct clk *clk, unsigned long rate)
+{
+	return i2s_mck_set_rate(clk, rate,
+					APLL_CFG_ADR_MASK, APLL_CFG_ADR_SHIFT);
+}
+
+static int divider_set_clock(struct clk *clk, int on)
+{
+	u32 divider;
+
+	if (!(clk->flags & CLK_DIVIDER_WITH_ENABLE))
+		return -EINVAL;
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if (on)
+		divider |= DIVIDER_ENABLE_BIT;
+	else
+		divider = 0;
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	return 0;
+}
+
+static void divider_enable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 1);
+}
+
+static void divider_disable_clock(struct clk *clk)
+{
+	divider_set_clock(clk, 0);
+}
+
+static unsigned long divider_get_rate(struct clk *clk)
+{
+	u32 divider;
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+
+	if (!parent_rate) {
+		clk->rate = 0;
+		return clk->rate;
+	}
+
+	divider = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	if ((clk->flags & CLK_DIVIDER_WITH_ENABLE) &&
+		!(divider & DIVIDER_ENABLE_BIT)) {
+		clk->rate = 0UL;
+		return clk->rate;
+	}
+
+	clk->rate = parent_rate / ((divider & clk->divmask) + 1);
+
+	return clk->rate;
+}
+
+static int divider_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long parent_rate = clk_get_rate(clk->parent);
+	u32 divider, divider_old;
+
+	if (unlikely(!parent_rate || rate > parent_rate)) {
+		clk->rate = 0;
+		pr_debug("parent rate not sufficient\n");
+		return -EINVAL;
+	}
+
+	divider = (parent_rate / rate) - 1;
+
+	if (divider > clk->divmask) {
+		pr_debug("input clock too high\n");
+		return -EINVAL;
+	};
+
+	divider_old = readl(REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(0, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+	writel(divider, REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	/* Re-enable clock if it was enabled before */
+	if (divider_old & DIVIDER_ENABLE_BIT)
+		writel(divider | DIVIDER_ENABLE_BIT,
+			REG_SCU_BASE(clk->scu) + clk->dividerreg);
+
+	clk->rate = parent_rate / (divider + 1);
+
+	return 0;
+}
+static void periph_enable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(REG_SCU_CLKEN(clk->scu));
+
+	scu_reg |= (1 << clk->scu_periph_id);
+
+	writel(scu_reg, REG_SCU_CLKEN(clk->scu));
+}
+
+static void periph_disable_clock(struct clk *clk)
+{
+	u32 scu_reg = readl(REG_SCU_CLKEN(clk->scu));
+
+	scu_reg &= ~(1 << clk->scu_periph_id);
+
+	writel(scu_reg, REG_SCU_CLKEN(clk->scu));
+}
+
+void spmp8000_update_arm_freqs(void)
+{
+	writel(7, REG_SCU_B(SCU_B_UPDATE_ARM_RATIO));
+}
+EXPORT_SYMBOL(spmp8000_update_arm_freqs);
+
+static struct clk clk_spll = {
+	.get_rate = spll_get_rate,
+	.flags = CLK_FIXED_RATE,
+};
+
+static struct clk clk_ref_arm = {
+	.parent = &clk_spll,
+	.get_rate = ref_arm_get_rate,
+};
+
+static struct clk clk_ref_ceva = {
+	.parent = &clk_spll,
+	.get_rate = ref_ceva_get_rate,
+};
+
+static struct clk clk_apll = {
+	.name = "clk_apll",
+	.enable = apll_enable,
+	.disable = apll_disable,
+};
+
+static struct clk clk_i2stx_mck = {
+	.name = "clk_i2stx_mck",
+	.parent = &clk_apll,
+	.enable = i2stx_mck_enable,
+	.disable = i2stx_mck_disable,
+	.get_rate = i2stx_mck_get_rate,
+	.set_rate = i2stx_mck_set_rate,
+};
+
+static struct clk clk_i2srx_mck = {
+	.name = "clk_i2srx_mck",
+	.parent = &clk_apll,
+	.enable = i2srx_mck_enable,
+	.disable = i2srx_mck_disable,
+	.get_rate = i2srx_mck_get_rate,
+	.set_rate = i2srx_mck_set_rate,
+};
+
+#define SYSTEM_CLK(__name, __parent, __scu, __divider, __divmask, __flags) \
+static struct clk clk_##__name = { \
+	.name = "clk_" #__name, \
+	.parent = &clk_##__parent, \
+	.scu = REG_SCU_##__scu##_ID, \
+	.dividerreg = __divider, \
+	.divmask = __divmask, \
+	.flags = __flags, \
+	.enable = &divider_enable_clock, \
+	.disable = &divider_disable_clock, \
+	.get_rate = &divider_get_rate, \
+	.set_rate = &divider_set_rate, \
+}
+
+SYSTEM_CLK(arm, ref_arm, B, SCU_B_ARM_RATIO, 0x3F, 0);
+SYSTEM_CLK(arm_ahb, arm, B, SCU_B_ARM_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(arm_apb, arm_ahb, B, SCU_B_ARM_APB_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva, ref_ceva, C, SCU_C_CEVA_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva_ahb, ceva, C, SCU_C_CEVA_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(ceva_apb, ceva_ahb, C, SCU_C_CEVA_APB_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys, ref_ceva, C, SCU_C_SYS_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys_ahb, sys, C, SCU_C_SYS_AHB_RATIO, 0x3F, 0);
+SYSTEM_CLK(sys_apb, sys_ahb, C, SCU_C_SYS_APB_RATIO, 0x3F, 0);
+
+SYSTEM_CLK(uart, ref_ceva, A, SCU_A_UART_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(lcd, ref_ceva, A, SCU_A_LCD_CLK_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(csi, ref_ceva, A, SCU_A_CSI_CLK_CFG, 0xFF, CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(i2srx_bck, i2srx_mck, A, SCU_A_I2S_BCK_CFG, 0xFF,
+		CLK_DIVIDER_WITH_ENABLE);
+SYSTEM_CLK(i2stx_bck, i2stx_mck, A, SCU_A_I2S_BCK_CFG, 0xFF,
+		CLK_DIVIDER_WITH_ENABLE);
+
+
+/* Peripherals */
+#define PERIPH_CLK(__name, __parent, __scu, __id) \
+static struct clk clk_##__name = { \
+	.name = "clk_" #__name, \
+	.parent = &__parent, \
+	.scu = REG_SCU_##__scu##_ID, \
+	.scu_periph_id = __id, \
+	.enable = periph_enable_clock, \
+	.disable = periph_disable_clock, \
+}
+
+/* Not needed yet for any driver */
+#if 0
+PERIPH_CLK(drm, clk_sys_ahb, A, 2);
+PERIPH_CLK(usb_host, clk_sys_ahb, A, 3);
+PERIPH_CLK(usb_device, clk_sys_ahb, A, 4);
+PERIPH_CLK(scu_a, clk_sys_ahb, A, 6);
+PERIPH_CLK(tvout, clk_sys, A, 7);
+PERIPH_CLK(csi_ctrl, clk_sys, A, 10);
+PERIPH_CLK(nand0, clk_sys_ahb, A, 11);
+PERIPH_CLK(nand1, clk_sys_ahb, A, 12);
+PERIPH_CLK(ecc, clk_sys_ahb, A, 13);
+PERIPH_CLK(uart_con, clk_sys_ahb, A, 15);
+PERIPH_CLK(aahbm212, clk_sys, A, 16);
+PERIPH_CLK(nand_abt, clk_sys_ahb, A, 20);
+PERIPH_CLK(rt_abt212, clk_sys, A, 22);
+PERIPH_CLK(cahbm212, clk_sys, A, 23);
+
+PERIPH_CLK(tcm_bist, clk_arm, B, 0);
+PERIPH_CLK(tcm_ctrl, clk_arm, B, 1);
+PERIPH_CLK(ahb2ahb, clk_arm, B, 2);
+PERIPH_CLK(ahbsw, clk_arm, B, 3);
+PERIPH_CLK(vic0, clk_arm_ahb, B, 4);
+PERIPH_CLK(vic1, clk_arm_ahb, B, 5);
+PERIPH_CLK(dpm, clk_arm_ahb, B, 6);
+PERIPH_CLK(apbb, clk_arm_ahb, B, 7);
+PERIPH_CLK(arm926, clk_arm, B, 8);
+PERIPH_CLK(wdt, clk_arm_apb, B, 10);
+PERIPH_CLK(uartapb, clk_arm_apb, B, 11);
+PERIPH_CLK(i2c, clk_arm_apb, B, 12);
+PERIPH_CLK(rand, clk_arm_apb, B, 13);
+PERIPH_CLK(gpio0, clk_arm_apb, B, 14);
+PERIPH_CLK(rtc, clk_arm_apb, B, 15);
+
+PERIPH_CLK(fabricc, clk_sys, C, 0);
+PERIPH_CLK(dmac0, clk_sys, C, 1);
+PERIPH_CLK(dmac1, clk_sys, C, 2);
+PERIPH_CLK(dram_ctrl, clk_sys, C, 4);
+PERIPH_CLK(scu_c, clk_sys, C, 5);
+PERIPH_CLK(i2c_ctrl, clk_sys, C, 6);
+PERIPH_CLK(2d, clk_sys, C, 8);
+PERIPH_CLK(extmem, clk_sys, C, 9);
+PERIPH_CLK(cf, clk_sys_apb, C, 10);
+PERIPH_CLK(ms, clk_sys_apb, C, 11);
+PERIPH_CLK(intmem, clk_sys, C, 12);
+PERIPH_CLK(uartc0, clk_sys_apb, C, 13);
+PERIPH_CLK(uartc1, clk_sys_apb, C, 14);
+PERIPH_CLK(uartc2, clk_sys_apb, C, 15);
+PERIPH_CLK(ssp0, clk_sys_apb, C, 16);
+PERIPH_CLK(ssp1, clk_sys_apb, C, 17);
+PERIPH_CLK(sd1, clk_sys_apb, C, 19);
+PERIPH_CLK(i2c_sys, clk_sys_apb, C, 20);
+PERIPH_CLK(scale, clk_sys, C, 21);
+PERIPH_CLK(scaleabt, clk_sys, C, 22);
+PERIPH_CLK(ti2c, clk_sys_apb, C, 23);
+PERIPH_CLK(fabric_a, clk_sys, C, 24);
+PERIPH_CLK(cxmp_sl, clk_sys, C, 25);
+PERIPH_CLK(cxmd_sl, clk_sys, C, 26);
+#endif
+
+PERIPH_CLK(rt_abt, clk_sys, A, 21);
+/* Make the parent rt_abt to auto-enable it when enabling the lcdc clock */
+PERIPH_CLK(lcd_ctrl, clk_rt_abt, A, 1);
+PERIPH_CLK(apbdma_a, clk_rt_abt, A, 9);
+PERIPH_CLK(apll_ctrl, clk_sys_ahb, A, 14);
+PERIPH_CLK(i2stx_ctrl, clk_apbdma_a, A, 17);
+PERIPH_CLK(i2srx_ctrl, clk_apbdma_a, A, 18);
+PERIPH_CLK(saacc, clk_sys_apb, A, 19);
+
+PERIPH_CLK(tmrb, clk_arm_apb, B, 9);
+PERIPH_CLK(apbdma_c, clk_sys_apb, C, 7);
+PERIPH_CLK(sd0, clk_sys_apb, C, 18);
+
+/* TODO use common macro once available */
+#define _DEFINE_CLKDEV_CON(n) \
+	{ \
+		.dev_id = NULL, \
+		.con_id = #n, \
+		.clk = &clk_##n, \
+	}
+
+#define _DEFINE_CLKDEV_DEV(d, c) \
+	{ \
+		.dev_id = d, \
+		.con_id = NULL, \
+		.clk = &clk_##c, \
+	}
+
+static struct clk_lookup lookups[] = {
+	_DEFINE_CLKDEV_CON(arm),
+	_DEFINE_CLKDEV_CON(arm_ahb),
+	_DEFINE_CLKDEV_CON(arm_apb),
+	_DEFINE_CLKDEV_CON(ceva),
+	_DEFINE_CLKDEV_CON(ceva_ahb),
+	_DEFINE_CLKDEV_CON(ceva_apb),
+	_DEFINE_CLKDEV_CON(sys),
+	_DEFINE_CLKDEV_CON(sys_ahb),
+	_DEFINE_CLKDEV_CON(sys_apb),
+	_DEFINE_CLKDEV_CON(lcd),
+	_DEFINE_CLKDEV_CON(apbdma_a),
+	_DEFINE_CLKDEV_CON(saacc),
+	_DEFINE_CLKDEV_CON(i2stx_mck),
+	_DEFINE_CLKDEV_CON(i2srx_mck),
+	_DEFINE_CLKDEV_CON(i2stx_bck),
+	_DEFINE_CLKDEV_CON(i2srx_bck),
+	_DEFINE_CLKDEV_DEV("uart.0", uart),
+	_DEFINE_CLKDEV_DEV("uart.1", uart),
+	_DEFINE_CLKDEV_DEV("uart.2", uart),
+	_DEFINE_CLKDEV_DEV("93000000.fb", lcd_ctrl),
+	_DEFINE_CLKDEV_DEV("90000000.pwm", tmrb),
+	_DEFINE_CLKDEV_DEV("92b00000.dma", apbdma_c),
+	_DEFINE_CLKDEV_DEV("92b0b000.mmc", sd0),
+	_DEFINE_CLKDEV_DEV("93010000.dma", apbdma_a),
+	_DEFINE_CLKDEV_DEV("93012000.i2s", i2stx_ctrl),
+	_DEFINE_CLKDEV_DEV("9301d000.i2s", i2srx_ctrl),
+};
+
+void __init spmp8000_init_clkdev(void)
+{
+	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	/* Enable the apll control registers here, as the according clock
+	 * isn't exported, but used by the mck/bck clocks. If we wouldn't
+	 * enable it here, the freq of the pll couldn't be set up before
+	 * enabling one of its child clocks.
+	 * The PLL clock itself will be auto-enabled on demand by them.
+	 */
+	clk_enable(&clk_apll_ctrl);
+}
diff --git a/arch/arm/mach-spmp8000/clock.c b/arch/arm/mach-spmp8000/clock.c
new file mode 100644
index 0000000..5652aff
--- /dev/null
+++ b/arch/arm/mach-spmp8000/clock.c
@@ -0,0 +1,155 @@
+/*
+ * Usual clk API boilerplate
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+
+#include <mach/clock.h>
+
+static DEFINE_MUTEX(clocks_mutex);
+
+static void __clk_disable(struct clk *clk)
+{
+	BUG_ON(clk->refcount == 0);
+
+	if (!(--clk->refcount) && clk->disable) {
+		clk->disable(clk);
+		if (clk->parent)
+			__clk_disable(clk->parent);
+	}
+}
+
+static int __clk_enable(struct clk *clk)
+{
+	int ret = 0;
+
+	if (clk->refcount++ == 0 && clk->enable) {
+		if (clk->parent)
+			ret = __clk_enable(clk->parent);
+		if (ret)
+			return ret;
+		else
+			clk->enable(clk);
+	}
+
+	return 0;
+}
+
+int clk_enable(struct clk *clk)
+{
+	int ret = 0;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return -EINVAL;
+
+	mutex_lock(&clocks_mutex);
+	ret = __clk_enable(clk);
+	mutex_unlock(&clocks_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return;
+
+	mutex_lock(&clocks_mutex);
+	__clk_disable(clk);
+	mutex_unlock(&clocks_mutex);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return 0UL;
+
+	/* Is fixed and has been calculated already */
+	if ((clk->flags & CLK_FIXED_RATE) && clk->rate)
+		return clk->rate;
+
+	if (clk->get_rate)
+		return clk->get_rate(clk);
+
+	return clk_get_rate(clk->parent);
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return 0;
+	if (unlikely(!clk->round_rate))
+		return 0;
+
+	return clk->round_rate(clk, rate);
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	int ret = -EINVAL;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return ret;
+
+	if (unlikely(clk->flags & CLK_FIXED_RATE))
+		return ret;
+
+	if (unlikely(!clk->set_rate || !rate))
+		return ret;
+
+	mutex_lock(&clocks_mutex);
+	ret = clk->set_rate(clk, rate);
+	mutex_unlock(&clocks_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk *old;
+	int ret = -EINVAL;
+
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return ret;
+	if (unlikely(!clk->set_parent || !parent))
+		return ret;
+
+	mutex_lock(&clocks_mutex);
+	old = clk->parent;
+	if (clk->refcount)
+		__clk_enable(parent);
+	ret = clk->set_parent(clk, parent);
+	if (ret)
+		old = parent;
+	if (clk->refcount)
+		__clk_disable(old);
+	mutex_unlock(&clocks_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+	if (unlikely(IS_ERR_OR_NULL(clk)))
+		return NULL;
+
+	return clk->parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
diff --git a/arch/arm/mach-spmp8000/include/mach/clock.h b/arch/arm/mach-spmp8000/include/mach/clock.h
new file mode 100644
index 0000000..e5cdbd2
--- /dev/null
+++ b/arch/arm/mach-spmp8000/include/mach/clock.h
@@ -0,0 +1,37 @@
+/*
+ * SPMP8000 machines clock support
+ *
+ * Copyright (C) 2011 Zoltan Devai <zoss@devai.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __MACH_SPMP8000_CLOCK_H__
+#define __MACH_SPMP8000_CLOCK_H__
+
+struct clk {
+	char		*name;
+	unsigned long	rate;
+	unsigned int	id;
+	unsigned int	refcount;
+	int		scu;
+	int		scu_periph_id;
+	int		dividerreg;
+	int		divmask;
+	unsigned long	flags;
+/* Either really fixed rate (crystal) or which we don't change for sure */
+#define CLK_FIXED_RATE			BIT(0)
+/* SPMP8000 has two types of dividers, one of them needs enabling */
+#define CLK_DIVIDER_WITH_ENABLE		BIT(1)
+
+	struct clk	*parent;
+	unsigned long	(*get_rate)(struct clk *clk);
+	unsigned long	(*round_rate) (struct clk *, u32);
+	int		(*set_rate) (struct clk *, unsigned long);
+	int		(*set_parent) (struct clk *clk, struct clk *parent);
+	void		(*enable) (struct clk *);
+	void		(*disable) (struct clk *);
+};
+
+#endif /* __MACH_SPMP8000_CLOCK_H__ */
-- 
1.7.4.1

  parent reply	other threads:[~2011-10-09 16:36 UTC|newest]

Thread overview: 103+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-10-09 16:36 Add support for the SPMP8000 SoC and Letcool board Zoltan Devai
2011-10-09 16:36 ` [PATCH 1/9] ARM: vic: Don't write to the read-only register VIC_IRQ_STATUS Zoltan Devai
2011-10-10  1:35   ` Linus Walleij
2011-10-10 13:59     ` Zoltan Devai
2011-10-09 16:36 ` [PATCH 2/9] ARM: SPMP8000: Add machine base files Zoltan Devai
2011-10-09 17:22   ` Jamie Iles
2011-10-10 11:36     ` Zoltan Devai
2011-10-10 11:52       ` Jamie Iles
2011-10-11 14:44   ` Arnd Bergmann
2011-10-16 14:10     ` Zoltan Devai
2011-10-16 15:57       ` Russell King - ARM Linux
2011-10-16 20:59       ` Arnd Bergmann
2011-10-16 20:52         ` Jean-Christophe PLAGNIOL-VILLARD
2011-10-17 11:44           ` Arnd Bergmann
2011-10-17 11:44             ` Arnd Bergmann
2011-10-09 16:36 ` Zoltan Devai [this message]
2011-10-13  9:38   ` [PATCH 3/9] ARM: SPMP8000: Add clk support Russell King - ARM Linux
2011-10-16 14:16     ` Zoltan Devai
2011-10-17 12:15       ` Mark Brown
2011-10-18 10:18         ` Russell King - ARM Linux
2011-10-09 16:36 ` [PATCH 4/9] ARM: SPMP8000: Add ADC driver Zoltan Devai
2011-10-10  1:29   ` Linus Walleij
2011-10-10  1:29     ` Linus Walleij
2011-10-10  9:42     ` Jonathan Cameron
2011-10-10  9:42       ` Jonathan Cameron
2011-10-10  9:46       ` Jonathan Cameron
2011-10-10  9:46         ` Jonathan Cameron
2011-10-10 10:00       ` Mark Brown
2011-10-10 10:00         ` Mark Brown
2011-10-10 11:42         ` Zoltan Devai
2011-10-10 11:42           ` Zoltan Devai
2011-10-10 11:44           ` Mark Brown
2011-10-10 11:44             ` Mark Brown
2011-10-11 14:17             ` Arnd Bergmann
2011-10-11 14:17               ` Arnd Bergmann
2011-10-11 14:40               ` Mark Brown
2011-10-11 14:40                 ` Mark Brown
2011-10-11 15:24                 ` Arnd Bergmann
2011-10-11 15:24                   ` Arnd Bergmann
2011-10-11 15:39                   ` Jonathan Cameron
2011-10-11 15:39                     ` Jonathan Cameron
2011-10-12 14:42                   ` Mark Brown
2011-10-12 14:42                     ` Mark Brown
2011-10-12 15:41                     ` Jonathan Cameron
2011-10-12 15:41                       ` Jonathan Cameron
2011-10-13  9:47             ` Russell King - ARM Linux
2011-10-13  9:47               ` Russell King - ARM Linux
2011-10-13 11:09               ` Linus Walleij
2011-10-13 11:09                 ` Linus Walleij
2011-10-13 11:35                 ` Jonathan Cameron
2011-10-13 11:35                   ` Jonathan Cameron
2011-10-13 11:35               ` Mark Brown
2011-10-13 11:35                 ` Mark Brown
2011-10-13 12:17                 ` Russell King - ARM Linux
2011-10-13 12:17                   ` Russell King - ARM Linux
2011-10-13 14:19                   ` Arnd Bergmann
2011-10-13 14:19                     ` Arnd Bergmann
2011-10-13 14:27                     ` Mark Brown
2011-10-13 14:27                       ` Mark Brown
2011-10-13 14:38                   ` Mark Brown
2011-10-13 14:38                     ` Mark Brown
2011-10-13 14:56                     ` Arnd Bergmann
2011-10-13 14:56                       ` Arnd Bergmann
2011-10-13 16:25                       ` Mark Brown
2011-10-13 16:25                         ` Mark Brown
2011-10-09 16:36 ` [PATCH 5/9] ARM: SPMP8000: Add pinmux driver Zoltan Devai
2011-10-10  1:32   ` Linus Walleij
2011-10-10  8:01     ` Barry Song
2011-10-10  8:34       ` Linus Walleij
2011-10-09 16:36 ` [PATCH 6/9] ARM: SPMP8000: Add pwm driver Zoltan Devai
2011-10-10  1:50   ` Linus Walleij
2011-10-10  9:30     ` Sascha Hauer
2011-10-09 16:36 ` [PATCH 7/9] ARM: SPMP8000: Add dts file of SPMP8000 SoC and Letcool board Zoltan Devai
2011-10-10  8:54   ` Jamie Iles
2011-10-09 16:36 ` [PATCH 8/9] ARM: SPMP8000: Add support for the " Zoltan Devai
2011-10-11 14:09   ` Arnd Bergmann
2011-10-11 14:43     ` Zoltan Devai
2011-10-11 15:18       ` Arnd Bergmann
2011-10-13  9:54   ` Russell King - ARM Linux
2011-10-09 16:36 ` [PATCH 9/9] ARM: SPMP8000: Add Kconfig and Makefile entries to build the machine Zoltan Devai
2011-10-09 17:25   ` Jamie Iles
2011-10-10  1:43   ` Linus Walleij
2011-10-13  9:53   ` Russell King - ARM Linux
2011-10-10  8:55 ` Add support for the SPMP8000 SoC and Letcool board Jamie Iles
2011-10-10 12:00   ` Zoltan Devai
2011-10-10 12:03     ` Jamie Iles
2011-10-11 14:57 ` Arnd Bergmann
     [not found] ` <1319040118-29773-1-git-send-email-zoss@devai.org>
2011-10-19 16:01   ` [PATCH v2 1/5] ARM: SPMP8000: Add machine base files Zoltan Devai
2011-10-19 19:15     ` Arnd Bergmann
2011-10-21 22:54       ` Russell King - ARM Linux
2011-10-23 21:47         ` Zoltan Devai
2011-10-23 21:37       ` Zoltan Devai
2011-10-24  9:13         ` Arnd Bergmann
2011-10-24 11:00           ` Jamie Iles
2011-11-02 13:29             ` Zoltan Devai
2011-11-03 15:08               ` Arnd Bergmann
2011-10-19 16:01   ` [PATCH v2 2/5] ARM: SPMP8000: Add clk support Zoltan Devai
2011-10-19 16:01   ` [PATCH v2 3/5] ARM: SPMP8000: Add clocksource and clockevent drivers Zoltan Devai
2011-10-19 16:01   ` [PATCH v2 4/5] ARM: SPMP8000: Add SPMP8000 SoC and Letcool board dts descriptions Zoltan Devai
2011-10-19 16:01     ` Zoltan Devai
2011-10-24 12:47     ` Rob Herring
2011-10-24 12:47       ` Rob Herring
2011-10-19 16:01   ` [PATCH v2 5/5] ARM: SPMP8000: Add Kconfig and Makefile entries Zoltan Devai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1318178172-7965-4-git-send-email-zoss@devai.org \
    --to=zoss@devai.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.