From 6af39fb5f6a56ebe67ed827992711fddc75450f9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Sat, 24 Apr 2010 03:48:44 +0000 Subject: [PATCH] add clock support --- arch/arm/mach-rk2818/Makefile | 2 +- arch/arm/mach-rk2818/clock-rk2818.c | 111 --- arch/arm/mach-rk2818/clock.c | 1075 ++++++++++++++++++++++++--- arch/arm/mach-rk2818/clock.h | 47 -- 4 files changed, 966 insertions(+), 269 deletions(-) delete mode 100644 arch/arm/mach-rk2818/clock-rk2818.c delete mode 100644 arch/arm/mach-rk2818/clock.h diff --git a/arch/arm/mach-rk2818/Makefile b/arch/arm/mach-rk2818/Makefile index f1000ca5146d..78ffd575c806 100644 --- a/arch/arm/mach-rk2818/Makefile +++ b/arch/arm/mach-rk2818/Makefile @@ -2,7 +2,7 @@ obj-y += io.o idle.o irq.o timer.o obj-y += devices.o obj-y += proc_comm.o obj-y += vreg.o -obj-y += clock.o clock-rk2818.o +obj-y += clock.o obj-$(CONFIG_MACH_RK2818MID) += board-midsdk.o diff --git a/arch/arm/mach-rk2818/clock-rk2818.c b/arch/arm/mach-rk2818/clock-rk2818.c deleted file mode 100644 index c4a66260ad2a..000000000000 --- a/arch/arm/mach-rk2818/clock-rk2818.c +++ /dev/null @@ -1,111 +0,0 @@ -/* arch/arm/mach-rk2818/clock-rk2818.c - * - * - * Copyright (C) 2010 Rockchip, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include - -#include "clock.h" -#include "devices.h" - -/* clock IDs used by the modem processor */ - -#define ACPU_CLK 0 /* Applications processor clock */ -#define ADM_CLK 1 /* Applications data mover clock */ -#define ADSP_CLK 2 /* ADSP clock */ -#define EBI1_CLK 3 /* External bus interface 1 clock */ -#define EBI2_CLK 4 /* External bus interface 2 clock */ -#define ECODEC_CLK 5 /* External CODEC clock */ -#define EMDH_CLK 6 /* External MDDI host clock */ -#define GP_CLK 7 /* General purpose clock */ -#define GRP_CLK 8 /* Graphics clock */ -#define I2C_CLK 9 /* I2C clock */ -#define ICODEC_RX_CLK 10 /* Internal CODEX RX clock */ -#define ICODEC_TX_CLK 11 /* Internal CODEX TX clock */ -#define IMEM_CLK 12 /* Internal graphics memory clock */ -#define MDC_CLK 13 /* MDDI client clock */ -#define MDP_CLK 14 /* Mobile display processor clock */ -#define PBUS_CLK 15 /* Peripheral bus clock */ -#define PCM_CLK 16 /* PCM clock */ -#define PMDH_CLK 17 /* Primary MDDI host clock */ -#define SDAC_CLK 18 /* Stereo DAC clock */ -#define SDC1_CLK 19 /* Secure Digital Card clocks */ -#define SDC1_PCLK 20 -#define SDC2_CLK 21 -#define SDC2_PCLK 22 -#define SDC3_CLK 23 -#define SDC3_PCLK 24 -#define SDC4_CLK 25 -#define SDC4_PCLK 26 -#define TSIF_CLK 27 /* Transport Stream Interface clocks */ -#define TSIF_REF_CLK 28 -#define TV_DAC_CLK 29 /* TV clocks */ -#define TV_ENC_CLK 30 -#define UART1_CLK 31 /* UART clocks */ -#define UART2_CLK 32 -#define UART3_CLK 33 -#define UART1DM_CLK 34 -#define UART2DM_CLK 35 -#define USB_HS_CLK 36 /* High speed USB core clock */ -#define USB_HS_PCLK 37 /* High speed USB pbus clock */ -#define USB_OTG_CLK 38 /* Full speed USB clock */ -#define VDC_CLK 39 /* Video controller clock */ -#define VFE_CLK 40 /* Camera / Video Front End clock */ -#define VFE_MDC_CLK 41 /* VFE MDDI client clock */ - -#define NR_CLKS 42 - -#define CLOCK(clk_name, clk_id, clk_dev, clk_flags) { \ - .name = clk_name, \ - .id = clk_id, \ - .flags = clk_flags, \ - .dev = clk_dev, \ - } - -#define OFF CLKFLAG_AUTO_OFF -#define MINMAX CLKFLAG_USE_MIN_MAX_TO_SET - -struct clk rk2818_clocks[] = { - CLOCK("adm_clk", ADM_CLK, NULL, 0), - CLOCK("adsp_clk", ADSP_CLK, NULL, 0), - CLOCK("ebi1_clk", EBI1_CLK, NULL, 0), - CLOCK("ebi2_clk", EBI2_CLK, NULL, 0), - CLOCK("ecodec_clk", ECODEC_CLK, NULL, 0), - CLOCK("emdh_clk", EMDH_CLK, NULL, OFF), - CLOCK("gp_clk", GP_CLK, NULL, 0), - CLOCK("grp_clk", GRP_CLK, NULL, OFF), - CLOCK("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), - CLOCK("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), - CLOCK("imem_clk", IMEM_CLK, NULL, OFF), - CLOCK("mdc_clk", MDC_CLK, NULL, 0), - CLOCK("mdp_clk", MDP_CLK, NULL, OFF), - CLOCK("pbus_clk", PBUS_CLK, NULL, 0), - CLOCK("pcm_clk", PCM_CLK, NULL, 0), - CLOCK("pmdh_clk", PMDH_CLK, NULL, OFF | MINMAX), - CLOCK("sdac_clk", SDAC_CLK, NULL, OFF), - CLOCK("tsif_clk", TSIF_CLK, NULL, 0), - CLOCK("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), - CLOCK("tv_dac_clk", TV_DAC_CLK, NULL, 0), - CLOCK("tv_enc_clk", TV_ENC_CLK, NULL, 0), - CLOCK("uart_clk", UART1_CLK, &rk2818_device_uart1.dev, OFF), - CLOCK("uart1dm_clk", UART1DM_CLK, NULL, OFF), - CLOCK("uart2dm_clk", UART2DM_CLK, NULL, 0), - CLOCK("usb_otg_clk", USB_OTG_CLK, NULL, 0), - CLOCK("vdc_clk", VDC_CLK, NULL, OFF | MINMAX), - CLOCK("vfe_clk", VFE_CLK, NULL, OFF), - CLOCK("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), -}; - -unsigned rk2818_num_clocks = ARRAY_SIZE(rk2818_clocks); diff --git a/arch/arm/mach-rk2818/clock.c b/arch/arm/mach-rk2818/clock.c index 01c1cedf945c..28085a389071 100644 --- a/arch/arm/mach-rk2818/clock.c +++ b/arch/arm/mach-rk2818/clock.c @@ -8,210 +8,1065 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ -#include -#include +#include +#include +#include +#include #include -#include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include -#include "clock.h" -#include "proc_comm.h" +enum +{ + /* SCU CLK GATE 0 CON */ + SCU_IPID_ARM = 0, + SCU_IPID_DSP, + SCU_IPID_DMA, + SCU_IPID_SRAMARM, + SCU_IPID_SRAMDSP, + SCU_IPID_HIF, + SCU_IPID_OTGBUS, + SCU_IPID_OTGPHY, + SCU_IPID_NANDC, + SCU_IPID_INTC, + SCU_IPID_DEBLK, /* 10 */ + SCU_IPID_LCDC, + SCU_IPID_VIP, /* as sensor */ + SCU_IPID_I2S, + SCU_IPID_SDMMC0, /* 14 */ + SCU_IPID_EBROM, + SCU_IPID_GPIO0, + SCU_IPID_GPIO1, + SCU_IPID_UART0, + SCU_IPID_UART1, + SCU_IPID_I2C0, /* 20 */ + SCU_IPID_I2C1, + SCU_IPID_SPI0, + SCU_IPID_SPI1, + SCU_IPID_PWM, + SCU_IPID_TIMER, + SCU_IPID_WDT, + SCU_IPID_RTC, + SCU_IPID_LSADC, + SCU_IPID_UART2, + SCU_IPID_UART3, /* 30 */ + SCU_IPID_SDMMC1, -static DEFINE_MUTEX(clocks_mutex); -static DEFINE_SPINLOCK(clocks_lock); -static LIST_HEAD(clocks); + /* SCU CLK GATE 1 CON */ + SCU_IPID_HSADC = 32, + SCU_IPID_MOBILE_SDARM_COMMON = 47, + SCU_IPID_SDRAM_CONTROLLER, + SCU_IPID_MOBILE_SDRAM_CONTROLLER, + SCU_IPID_LCDC_SHARE_MEMORY, /* 50 */ + SCU_IPID_LCDC_HCLK, + SCU_IPID_DEBLK_H264, + SCU_IPID_GPU, + SCU_IPID_DDR_HCLK, + SCU_IPID_DDR, + SCU_IPID_CUSTOMIZED_SDRAM_CONTROLLER, + SCU_IPID_MCDMA, + SCU_IPID_SDRAM, + SCU_IPID_DDR_AXI, + SCU_IPID_DSP_TIMER, /* 60 */ + SCU_IPID_DSP_SLAVE, + SCU_IPID_DSP_MASTER, + SCU_IPID_USB_HOST, -/* - * glue for the proc_comm interface - */ -static inline int pc_clk_enable(unsigned id) + /* SCU CLK GATE 2 CON */ + SCU_IPID_ARMIBUS = 64, + SCU_IPID_ARMDBUS, + SCU_IPID_DSPBUS, + SCU_IPID_EXPBUS, + SCU_IPID_APBBUS, + SCU_IPID_EFUSE, + SCU_IPID_DTCM1, /* 70 */ + SCU_IPID_DTCM0, + SCU_IPID_ITCM, + SCU_IPID_VIDEOBUS, + + SCU_IPID_GATE_MAX, +}; + +static struct rockchip_scu_reg_hw { - return rk2818_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); -} + u32 scu_pll_config[3]; /* 0:arm 1:dsp 2:codec */ + u32 scu_mode_config; + u32 scu_pmu_config; + u32 scu_clksel0_config; + u32 scu_clksel1_config; + u32 scu_clkgate0_config; + u32 scu_clkgate1_config; + u32 scu_clkgate2_config; + u32 scu_softreset_config; + u32 scu_chipcfg_config; + u32 scu_cuppd; /* arm power down */ + u32 scu_clksel2_config; +} *scu_register_base = (struct rockchip_scu_reg_hw *)(RK2818_SCU_BASE); + +#define CLKSEL0_REG (u32 __iomem *)(RK2818_SCU_BASE + 0x14) +#define CLKSEL1_REG (u32 __iomem *)(RK2818_SCU_BASE + 0x18) +#define CLKSEL2_REG (u32 __iomem *)(RK2818_SCU_BASE + 0x34) -static inline void pc_clk_disable(unsigned id) +/* Clock flags */ +/* bit 0 is free */ +#define RATE_FIXED (1 << 1) /* Fixed clock rate */ +#define CONFIG_PARTICIPANT (1 << 10) /* Fundamental clock */ +#define ENABLE_ON_INIT (1 << 11) /* Enable upon framework init */ + +struct clk { + struct list_head node; + const char *name; + struct clk *parent; + struct list_head children; + struct list_head sibling; /* node for children */ + unsigned long rate; + u32 flags; + int (*mode)(struct clk *clk, int on); + unsigned long (*recalc)(struct clk *); + int (*set_rate)(struct clk *, unsigned long); + long (*round_rate)(struct clk *, unsigned long); + void (*init)(struct clk *); /* set clk's parent field from the hardware */ + s16 usecount; + u8 gate_idx; + u8 pll_idx; + u32 __iomem *clksel_reg; + u32 clksel_mask; + u8 clksel_shift; + u8 clksel_maxdiv; +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) + struct dentry *dent; /* For visible tree hierarchy */ +#endif +}; + +static void __clk_disable(struct clk *clk); +static void clk_reparent(struct clk *child, struct clk *parent); +static void propagate_rate(struct clk *tclk); + +/* Used for clocks that always have same value as the parent clock */ +static unsigned long followparent_recalc(struct clk *clk) { - rk2818_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); + return clk->parent->rate; } -static inline int pc_clk_set_rate(unsigned id, unsigned rate) +static unsigned long clksel_recalc(struct clk *clk) { - return rk2818_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); + u32 div = ((readl(clk->clksel_reg) & clk->clksel_mask) >> clk->clksel_shift) + 1; + unsigned long rate = clk->parent->rate / div; + pr_debug("clock: %s new clock rate is %ld (div %d) reg %p mask %x shift %d\n", clk->name, rate, div, clk->clksel_reg, clk->clksel_mask, clk->clksel_shift); + return rate; } -static inline int pc_clk_set_min_rate(unsigned id, unsigned rate) +static int clksel_set_rate(struct clk *clk, unsigned long rate) { - return rk2818_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); + u32 div; + + for (div = 1; div <= clk->clksel_maxdiv; div++) { + u32 new_rate = clk->parent->rate / div; + if (new_rate <= rate) { + u32 *reg = clk->clksel_reg; + u32 v = readl(reg); + v &= ~clk->clksel_mask; + v |= (div - 1) << clk->clksel_shift; + writel(v, reg); + clk->rate = new_rate; + pr_debug("clock: clksel_set_rate for clock %s to rate %ld (div %d)\n", clk->name, rate, div); + return 0; + } + } + + return -ENOENT; } -static inline int pc_clk_set_max_rate(unsigned id, unsigned rate) +static struct clk xin24m = { + .name = "xin24m", + .rate = 24000000, + .flags = RATE_FIXED, +}; + +static struct clk clk12m = { + .name = "clk12m", + .rate = 12000000, + .parent = &xin24m, + .flags = RATE_FIXED, +}; + +static struct clk extclk = { + .name = "extclk", + .rate = 27000000, + .flags = RATE_FIXED, +}; + +static unsigned long pll_clk_recalc(struct clk *clk) { - return rk2818_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate); + u32 v = readl(&scu_register_base->scu_pll_config[clk->pll_idx]); + u32 OD = ((v >> 1) & 0x7) + 1; + u32 NF = ((v >> 4) & 0xfff) + 1; + u32 NR = ((v >> 16) & 0x3f) + 1; + unsigned long rate = clk->parent->rate / NR * NF / OD; + pr_debug("clock: %s new clock rate is %ld NR %d NF %d OD %d\n", clk->name, rate, NR, NF, OD); + return rate; +} + +#define PLL_CLK(NAME,IDX) \ +static struct clk NAME##_pll_clk = { \ + .name = #NAME"_pll", \ + .parent = &xin24m, \ + .pll_idx = IDX, \ + .recalc = pll_clk_recalc, \ } -static inline int pc_clk_set_flags(unsigned id, unsigned flags) +PLL_CLK(arm, 0); +PLL_CLK(dsp, 1); +PLL_CLK(codec, 2); + +static struct clk arm_clk = { + .name = "arm", + .parent = &arm_pll_clk, + .recalc = clksel_recalc, +// .set_rate = arm_clk_set_rate, + .clksel_reg = CLKSEL2_REG, + .clksel_mask = 0xF, +}; + +static struct clk arm_hclk = { + .name = "arm_hclk", + .parent = &arm_clk, + .recalc = clksel_recalc, + .clksel_reg = CLKSEL0_REG, + .clksel_mask = 0x3, +}; + +static struct clk clk48m = { + .name = "clk48m", + .parent = &arm_clk, + .recalc = clksel_recalc, + .flags = RATE_FIXED, + .clksel_reg = CLKSEL2_REG, + .clksel_mask = 0xF << 4, + .clksel_shift = 4, +}; + +static unsigned long arm_pclk_recalc(struct clk *clk) { - return rk2818_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags); + u32 shift = (readl(&scu_register_base->scu_clksel0_config) >> 2) & 0x3; + unsigned long rate = clk->parent->rate >> shift; + pr_debug("clock: %s new clock rate is %ld (shift %d)\n", clk->name, rate, shift); + return rate; } -static inline unsigned pc_clk_get_rate(unsigned id) +static struct clk arm_pclk = { + .name = "arm_pclk", + .parent = &arm_hclk, + .recalc = arm_pclk_recalc, +}; + +static void demod_clk_init(struct clk *clk) { - if (rk2818_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) - return 0; - else - return id; + struct clk *real_parent = clk->parent; + u32 r = readl(CLKSEL1_REG); + if (r & (1 << 26)) { + real_parent = &xin24m; + } else { + r >>= 24; + if (r == 0) { + real_parent = &codec_pll_clk; + } else if (r == 1) { + real_parent = &arm_pll_clk; + } else if (r == 2) { + real_parent = &dsp_pll_clk; + } + } + if (real_parent != clk->parent) { + pr_debug("clock: inited %s parent to %s (was %s)\n", clk->name, real_parent->name, ((clk->parent) ? clk->parent->name : "NULL")); + clk_reparent(clk, real_parent); + } } -static inline unsigned pc_clk_is_enabled(unsigned id) +static struct clk demod_clk = { + .name = "demod", + .parent = &codec_pll_clk, + .recalc = clksel_recalc, + .set_rate = clksel_set_rate, + .init = demod_clk_init, +// .set_parent = demod_clk_set_parent, + .clksel_reg = CLKSEL1_REG, + .clksel_mask = 0xFF << 16, + .clksel_shift = 16, + .clksel_maxdiv = 128, +}; + +static struct clk codec_clk = { + .name = "codec", + .parent = &codec_pll_clk, + .recalc = clksel_recalc, + .set_rate = clksel_set_rate, + .clksel_reg = CLKSEL1_REG, + .clksel_mask = 0x1F << 3, + .clksel_shift = 3, + .clksel_maxdiv = 32, +}; + +static struct clk lcdc_divider_clk = { + .name = "lcdc_divider", + .parent = &arm_pll_clk, + .recalc = clksel_recalc, + .set_rate = clksel_set_rate, + .clksel_reg = CLKSEL0_REG, + .clksel_mask = 0xFF << 8, + .clksel_shift = 8, + .clksel_maxdiv = 128, +}; + +static void lcdc_clk_init(struct clk *clk) { - if (rk2818_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) - return 0; - else - return id; + u32 r = readl(&scu_register_base->scu_clksel0_config) & (1 << 7); + struct clk *real_parent = r ? &extclk : &lcdc_divider_clk; + if (real_parent != clk->parent) { + pr_debug("clock: inited %s parent to %s (was %s)\n", clk->name, real_parent->name, ((clk->parent) ? clk->parent->name : "NULL")); + clk_reparent(clk, real_parent); + } } -static inline int pc_pll_request(unsigned id, unsigned on) +static int gate_mode(struct clk *clk, int on) { - on = !!on; - return rk2818_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on); + u32 *reg; + int idx = clk->gate_idx; + u32 v; + + if (idx >= SCU_IPID_GATE_MAX) + return -EINVAL; + + reg = &scu_register_base->scu_clkgate0_config; + reg += (idx >> 5); + idx &= 0x1F; + + v = readl(reg); + if (on) { + v &= ~(1 << idx); // clear bit + } else { + v |= (1 << idx); // set bit + } + writel(v, reg); + + return 0; } -/* - * Standard clock functions defined in include/linux/clk.h +/** + * uart_clk_init_ - set a uart clk's parent field from the hardware + * @clk: clock struct ptr to use + * + * Given a pointer to a source-selectable struct clk, read the hardware + * register and determine what its parent is currently set to. Update the + * clk->parent field with the appropriate clk ptr. */ -struct clk *clk_get(struct device *dev, const char *id) +static void uart_clk_init(struct clk *clk) { - struct clk *clk; + u32 r = readl(&scu_register_base->scu_clksel1_config) >> 31; + struct clk *real_parent = r ? &clk48m : &xin24m; + if (real_parent != clk->parent) { + pr_debug("clock: inited %s parent to %s (was %s)\n", clk->name, real_parent->name, ((clk->parent) ? clk->parent->name : "NULL")); + clk_reparent(clk, real_parent); + } +} - mutex_lock(&clocks_mutex); +#define UART_CLK(n) \ +static struct clk uart##n##_clk = { \ + .name = "uart"#n, \ + .parent = &xin24m, \ + .gate_idx = SCU_IPID_UART##n, \ + .mode = gate_mode, \ + .recalc = followparent_recalc, \ + .init = uart_clk_init, \ +} - list_for_each_entry(clk, &clocks, list) - if (!strcmp(id, clk->name) && clk->dev == dev) - goto found_it; +#define GATE_CLK(NAME,PARENT,ID) \ +static struct clk NAME##_clk = { \ + .name = #NAME, \ + .parent = &PARENT, \ + .gate_idx = SCU_IPID_##ID, \ + .mode = gate_mode, \ + .recalc = followparent_recalc, \ +} - list_for_each_entry(clk, &clocks, list) - if (!strcmp(id, clk->name) && clk->dev == NULL) - goto found_it; +GATE_CLK(arm_core, arm_clk, ARM); +GATE_CLK(dsp, dsp_pll_clk, DSP); +GATE_CLK(dma, arm_hclk, DMA); +GATE_CLK(sramarm, arm_hclk, SRAMARM); +GATE_CLK(sramdsp, arm_hclk, SRAMDSP); +GATE_CLK(hif, arm_hclk, HIF); +GATE_CLK(otgbus, arm_hclk, OTGBUS); +//GATE_CLK(otgphy, arm_hclk, OTGPHY); //FIXME +GATE_CLK(nandc, arm_hclk, NANDC); +GATE_CLK(intc, arm_hclk, INTC); +GATE_CLK(deblocking_rv, arm_hclk, DEBLK); +static struct clk lcdc_clk = { + .name = "lcdc", + .parent = &lcdc_divider_clk, + .gate_idx = SCU_IPID_LCDC, + .mode = gate_mode, + .recalc = followparent_recalc, + .init = lcdc_clk_init, +// .set_parent = lcdc_clk_set_parent, +}; +GATE_CLK(vip, arm_hclk, VIP); //FIXME +GATE_CLK(i2s, arm_pclk, I2S); //FIXME +static struct clk sdmmc0_clk = { + .name = "sdmmc0", + .parent = &arm_hclk, + .gate_idx = SCU_IPID_SDMMC0, + .mode = gate_mode, + .recalc = clksel_recalc, + .set_rate = clksel_set_rate, + .clksel_reg = CLKSEL0_REG, + .clksel_mask = 7 << 4, + .clksel_shift = 4, + .clksel_maxdiv = 8, +}; - clk = ERR_PTR(-ENOENT); -found_it: - mutex_unlock(&clocks_mutex); - return clk; +GATE_CLK(ebrom, arm_hclk, EBROM); +GATE_CLK(gpio0, arm_pclk, GPIO0); +GATE_CLK(gpio1, arm_pclk, GPIO1); +UART_CLK(0); +UART_CLK(1); +GATE_CLK(i2c0, arm_pclk, I2C0); +GATE_CLK(i2c1, arm_pclk, I2C1); +GATE_CLK(spi0, arm_pclk, SPI0); +GATE_CLK(spi1, arm_pclk, SPI1); +GATE_CLK(pwm, arm_pclk, PWM); +GATE_CLK(timer, arm_pclk, TIMER); +GATE_CLK(wdt, arm_pclk, WDT); +GATE_CLK(rtc, arm_pclk, RTC); +static struct clk lsadc_clk = { + .name = "lsadc", + .parent = &arm_pclk, + .gate_idx = SCU_IPID_LSADC, + .mode = gate_mode, + .recalc = clksel_recalc, + .set_rate = clksel_set_rate, + .clksel_reg = CLKSEL1_REG, + .clksel_mask = 0xFF << 8, + .clksel_shift = 8, + .clksel_maxdiv = 128, +}; +UART_CLK(2); +UART_CLK(3); +static struct clk sdmmc1_clk = { + .name = "sdmmc1", + .parent = &arm_hclk, + .gate_idx = SCU_IPID_SDMMC1, + .mode = gate_mode, + .recalc = clksel_recalc, + .set_rate = clksel_set_rate, + .clksel_reg = CLKSEL2_REG, + .clksel_mask = 7 << 8, + .clksel_shift = 8, + .clksel_maxdiv = 8, +}; + +static unsigned long hsadc_clk_recalc(struct clk *clk) +{ + return clk->parent->rate >> 1; } -EXPORT_SYMBOL(clk_get); -void clk_put(struct clk *clk) +static struct clk hsadc_clk = { + .name = "hsadc", + .parent = &demod_clk, + .gate_idx = SCU_IPID_HSADC, + .mode = gate_mode, + .recalc = hsadc_clk_recalc, +}; +//GATE_CLK(mobile_sdram_common, arm_hclk, MOBILE_SDARM_COMMON); +//GATE_CLK(sdram_controller, arm_hclk, SDRAM_CONTROLLER); +//GATE_CLK(mobile_sdram_controller, arm_hclk, MOBILE_SDRAM_CONTROLLER); +//GATE_CLK(lcdc_share_memory, arm_hclk, LCDC_SHARE_MEMORY); //FIXME +//GATE_CLK(lcdc_hclk, arm_hclk, LCDC_HCLK); //FIXME +//GATE_CLK(deblocking_h264, arm_hclk, DEBLK_H264); +//GATE_CLK(gpu, arm_hclk, GPU); +//GATE_CLK(ddr_hclk, arm_hclk, DDR_HCLK); //FIXME +//GATE_CLK(ddr, arm_hclk, DDR); //FIXME +//GATE_CLK(customized_sdram_controller, ); //FIXME +//GATE_CLK(mcdma, arm_hclk, MCDMA); //FIXME +//GATE_CLK(sdram, arm_hclk, SDRAM); //FIXME +//GATE_CLK(ddr_axi, arm_hclk, DDR_AXI); //FIXME +//GATE_CLK(dsp_timer, ); //FIXME +//GATE_CLK(dsp_slave, ); //FIXME +//GATE_CLK(dsp_master, ); //FIXME +//GATE_CLK(usb_host, ); //FIXME + +GATE_CLK(armibus, arm_hclk, ARMIBUS); +GATE_CLK(armdbus, arm_hclk, ARMDBUS); +GATE_CLK(dspbus, arm_hclk, DSPBUS); +GATE_CLK(expbus, arm_hclk, EXPBUS); +GATE_CLK(apbbus, arm_hclk, APBBUS); +GATE_CLK(efuse, arm_pclk, EFUSE); +GATE_CLK(dtcm1, arm_clk, DTCM1); +GATE_CLK(dtcm0, arm_clk, DTCM0); +GATE_CLK(itcm, arm_clk, ITCM); +GATE_CLK(videobus, arm_hclk, VIDEOBUS); + +#define CLK(dev, con, ck) \ + { \ + .dev_id = dev, \ + .con_id = con, \ + .clk = ck, \ + } + +#define CLK1(name) \ + { \ + .dev_id = NULL, \ + .con_id = #name, \ + .clk = &name##_clk, \ + } + +static struct clk_lookup clks[] = { + CLK(NULL, "xin24m", &xin24m), + CLK(NULL, "extclk", &extclk), + + CLK(NULL, "clk12m", &clk12m), + CLK1(arm_pll), + CLK1(dsp_pll), + CLK1(codec_pll), + CLK1(arm), + CLK(NULL, "arm_hclk", &arm_hclk), + CLK(NULL, "clk48m", &clk48m), + CLK(NULL, "arm_pclk", &arm_pclk), + CLK1(demod), + CLK1(codec), + CLK1(lcdc_divider), + + CLK1(arm_core), + CLK1(dsp), + CLK1(dma), + CLK1(sramarm), + CLK1(sramdsp), + CLK1(hif), + CLK1(otgbus), + //CLK1(otgphy), //FIXME + CLK1(nandc), + CLK1(intc), + CLK1(deblocking_rv), + CLK1(lcdc), + CLK1(vip), //FIXME + CLK1(i2s), //FIXME + CLK1(sdmmc0), + CLK1(ebrom), + CLK1(gpio0), + CLK1(gpio1), + CLK("rk2818_serial.0", "uart", &uart0_clk), + CLK("rk2818_serial.1", "uart", &uart1_clk), + CLK1(i2c0), + CLK1(i2c1), + CLK1(spi0), + CLK1(spi1), + CLK1(pwm), + CLK1(timer), + CLK1(wdt), + CLK1(rtc), + CLK1(lsadc), + CLK("rk2818_serial.2", "uart", &uart2_clk), + CLK("rk2818_serial.3", "uart", &uart3_clk), + CLK1(sdmmc1), + + CLK1(hsadc), + //CLK1(mobile_sdram_common), + //CLK1(sdram_controller), + //CLK1(mobile_sdram_controller), + //CLK1(lcdc_share_memory), //FIXME + //CLK1(lcdc_hclk), //FIXME + //CLK1(deblocking_h264), + //CLK1(gpu), + //CLK1(ddr_hclk), //FIXME + //CLK1(ddr), //FIXME + //CLK1(customized_sdram_controller), //FIXME + //CLK1(mcdma), //FIXME + //CLK1(sdram), //FIXME + //CLK1(ddr_axi), //FIXME + //CLK1(dsp_timer), //FIXME + //CLK1(dsp_slave), //FIXME + //CLK1(dsp_master), //FIXME + //CLK1(usb_host), //FIXME + + CLK1(armibus), + CLK1(armdbus), + CLK1(dspbus), + CLK1(expbus), + CLK1(apbbus), + CLK1(efuse), + CLK1(dtcm1), + CLK1(dtcm0), + CLK1(itcm), + CLK1(videobus), +}; + +static LIST_HEAD(clocks); +static DEFINE_MUTEX(clocks_mutex); +static DEFINE_SPINLOCK(clockfw_lock); + +static int __clk_enable(struct clk *clk) { + int ret = 0; + + if (clk->usecount == 0) { + if (clk->parent) { + ret = __clk_enable(clk->parent); + if (ret) + return ret; + } + + if (clk->mode) { + ret = clk->mode(clk, 1); + if (ret) { + if (clk->parent) + __clk_disable(clk->parent); + return ret; + } + } + pr_debug("clock: %s enabled\n", clk->name); + } + clk->usecount++; + + return ret; } -EXPORT_SYMBOL(clk_put); int clk_enable(struct clk *clk) { + int ret = 0; unsigned long flags; - spin_lock_irqsave(&clocks_lock, flags); - clk->count++; - if (clk->count == 1) - pc_clk_enable(clk->id); - spin_unlock_irqrestore(&clocks_lock, flags); - return 0; + + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clockfw_lock, flags); + ret = __clk_enable(clk); + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; } EXPORT_SYMBOL(clk_enable); +static void __clk_disable(struct clk *clk) +{ + if (--clk->usecount == 0) { + if (clk->mode) + clk->mode(clk, 0); + pr_debug("clock: %s disabled\n", clk->name); + } + if (clk->parent) + __clk_disable(clk->parent); +} + void clk_disable(struct clk *clk) { unsigned long flags; - spin_lock_irqsave(&clocks_lock, flags); - BUG_ON(clk->count == 0); - clk->count--; - if (clk->count == 0) - pc_clk_disable(clk->id); - spin_unlock_irqrestore(&clocks_lock, flags); + + if (clk == NULL || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clockfw_lock, flags); + if (clk->usecount == 0) { + printk(KERN_ERR "Trying disable clock %s with 0 usecount\n", clk->name); + WARN_ON(1); + goto out; + } + + __clk_disable(clk); + +out: + spin_unlock_irqrestore(&clockfw_lock, flags); } EXPORT_SYMBOL(clk_disable); unsigned long clk_get_rate(struct clk *clk) { - return pc_clk_get_rate(clk->id); + unsigned long flags; + unsigned long ret; + + if (clk == NULL || IS_ERR(clk)) + return 0; + + spin_lock_irqsave(&clockfw_lock, flags); + ret = clk->rate; + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; } EXPORT_SYMBOL(clk_get_rate); +/*------------------------------------------------------------------------- + * Optional clock functions defined in include/linux/clk.h + *-------------------------------------------------------------------------*/ + +/* Given a clock and a rate apply a clock specific rounding function */ +static long __clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (clk->round_rate) + return clk->round_rate(clk, rate); + + if (clk->flags & RATE_FIXED) + printk(KERN_ERR "clock: clk_round_rate called on fixed-rate clock %s\n", clk->name); + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + unsigned long flags; + long ret = 0; + + if (clk == NULL || IS_ERR(clk)) + return ret; + + spin_lock_irqsave(&clockfw_lock, flags); + ret = __clk_round_rate(clk, rate); + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; +} +EXPORT_SYMBOL(clk_round_rate); + +/* Set the clock rate for a clock source */ +static int __clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EINVAL; + + pr_debug("clock: set_rate for clock %s to rate %ld\n", clk->name, rate); + + if (clk->flags & CONFIG_PARTICIPANT) + return -EINVAL; + + if (clk->set_rate) + ret = clk->set_rate(clk, rate); + + return ret; +} + int clk_set_rate(struct clk *clk, unsigned long rate) { - int ret; - if (clk->flags & CLKFLAG_USE_MIN_MAX_TO_SET) { - ret = pc_clk_set_max_rate(clk->id, rate); - if (ret) - return ret; - return pc_clk_set_min_rate(clk->id, rate); + unsigned long flags; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk)) + return ret; + + spin_lock_irqsave(&clockfw_lock, flags); + ret = __clk_set_rate(clk, rate); + if (ret == 0) { + if (clk->recalc) + clk->rate = clk->recalc(clk); + propagate_rate(clk); } - return pc_clk_set_rate(clk->id, rate); + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; } EXPORT_SYMBOL(clk_set_rate); +static int __clk_set_parent(struct clk *clk, struct clk *new_parent) +{ + return -EINVAL; // FIXME +} + int clk_set_parent(struct clk *clk, struct clk *parent) { - return -ENOSYS; + unsigned long flags; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) + return ret; + + spin_lock_irqsave(&clockfw_lock, flags); + if (clk->usecount == 0) { + ret = __clk_set_parent(clk, parent); + if (ret == 0) { + if (clk->recalc) + clk->rate = clk->recalc(clk); + propagate_rate(clk); + } + } else + ret = -EBUSY; + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; } EXPORT_SYMBOL(clk_set_parent); struct clk *clk_get_parent(struct clk *clk) { - return ERR_PTR(-ENOSYS); + return clk->parent; } EXPORT_SYMBOL(clk_get_parent); -int clk_set_flags(struct clk *clk, unsigned long flags) +static void clk_reparent(struct clk *child, struct clk *parent) +{ + list_del_init(&child->sibling); + if (parent) + list_add(&child->sibling, &parent->children); + child->parent = parent; + + /* now do the debugfs renaming to reattach the child + to the proper parent */ +} + +/* Propagate rate to children */ +static void propagate_rate(struct clk *tclk) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &tclk->children, sibling) { + if (clkp->recalc) + clkp->rate = clkp->recalc(clkp); + propagate_rate(clkp); + } +} + +static LIST_HEAD(root_clks); + +/** + * recalculate_root_clocks - recalculate and propagate all root clocks + * + * Recalculates all root clocks (clocks with no parent), which if the + * clock's .recalc is set correctly, should also propagate their rates. + * Called at init. + */ +static void recalculate_root_clocks(void) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &root_clks, sibling) { + if (clkp->recalc) + clkp->rate = clkp->recalc(clkp); + propagate_rate(clkp); + } +} + +/** + * clk_preinit - initialize any fields in the struct clk before clk init + * @clk: struct clk * to initialize + * + * Initialize any struct clk fields needed before normal clk initialization + * can run. No return value. + */ +static void clk_preinit(struct clk *clk) +{ + INIT_LIST_HEAD(&clk->children); +} + +static int clk_register(struct clk *clk) { if (clk == NULL || IS_ERR(clk)) return -EINVAL; - return pc_clk_set_flags(clk->id, flags); + + /* + * trap out already registered clocks + */ + if (clk->node.next || clk->node.prev) + return 0; + + mutex_lock(&clocks_mutex); + if (clk->parent) + list_add(&clk->sibling, &clk->parent->children); + else + list_add(&clk->sibling, &root_clks); + + list_add(&clk->node, &clocks); + if (clk->init) + clk->init(clk); + mutex_unlock(&clocks_mutex); + + return 0; +} + +static void clk_enable_init_clocks(void) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &clocks, node) { + if (clkp->flags & ENABLE_ON_INIT) + clk_enable(clkp); + } } -EXPORT_SYMBOL(clk_set_flags); +#ifdef CONFIG_CPU_FREQ +void clk_init_cpufreq_table(struct cpufreq_frequency_table **table) +{ + unsigned long flags; + + spin_lock_irqsave(&clockfw_lock, flags); + __clk_init_cpufreq_table(table); + spin_unlock_irqrestore(&clockfw_lock, flags); +} +EXPORT_SYMBOL(clk_init_cpufreq_table); +#endif + +static unsigned int __initdata armclk; + +/* + * By default we use the rate set by the bootloader. + * You can override this with armclk= cmdline option. + */ +static int __init clk_setup(char *str) +{ + get_option(&str, &armclk); + + if (!armclk) + return 1; + + if (armclk < 1000) + armclk *= 1000000; + + return 1; +} +__setup("armclk=", clk_setup); + +/* + * Switch the arm_clk rate if specified on cmdline. + * We cannot do this early until cmdline is parsed. + */ +static int __init rk28_clk_arch_init(void) +{ + if (!armclk) + return -EINVAL; + + if (clk_set_rate(&arm_clk, armclk)) + printk(KERN_ERR "*** Unable to set arm_clk rate\n"); + + recalculate_root_clocks(); + + printk(KERN_INFO "Switched to new clocking rate (pll/arm/hclk/pclk): " + "%ld/%ld/%ld/%ld MHz\n", + arm_pll_clk.rate / 1000000, arm_clk.rate / 1000000, + arm_hclk.rate / 1000000, arm_pclk.rate / 1000000); + + calibrate_delay(); + + return 0; +} +arch_initcall(rk28_clk_arch_init); void __init rk2818_clock_init(void) { - unsigned n; + struct clk_lookup *lk; - spin_lock_init(&clocks_lock); - mutex_lock(&clocks_mutex); - for (n = 0; n < rk2818_num_clocks; n++) - list_add_tail(&rk2818_clocks[n].list, &clocks); - mutex_unlock(&clocks_mutex); + for (lk = clks; lk < clks + ARRAY_SIZE(clks); lk++) + clk_preinit(lk->clk); + + for (lk = clks; lk < clks + ARRAY_SIZE(clks); lk++) { + clkdev_add(lk); + clk_register(lk->clk); + } + + recalculate_root_clocks(); + + printk(KERN_INFO "Clocking rate (pll/arm/hclk/pclk): " + "%ld/%ld/%ld/%ld MHz\n", + arm_pll_clk.rate / 1000000, arm_clk.rate / 1000000, + arm_hclk.rate / 1000000, arm_pclk.rate / 1000000); + + /* + * Only enable those clocks we will need, let the drivers + * enable other clocks as necessary + */ + clk_enable_init_clocks(); } -/* The bootloader and/or AMSS may have left various clocks enabled. - * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have - * not been explicitly enabled by a clk_enable() call. +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) +/* + * debugfs support to trace clock tree hierarchy and attributes */ -static int __init clock_late_init(void) +static struct dentry *clk_debugfs_root; + +static int clk_debugfs_register_one(struct clk *c) { - unsigned long flags; - struct clk *clk; - unsigned count = 0; + int err; + struct dentry *d, *child; + struct clk *pa = c->parent; + char s[255]; + char *p = s; - mutex_lock(&clocks_mutex); - list_for_each_entry(clk, &clocks, list) { - if (clk->flags & CLKFLAG_AUTO_OFF) { - spin_lock_irqsave(&clocks_lock, flags); - if (!clk->count) { - count++; - pc_clk_disable(clk->id); - } - spin_unlock_irqrestore(&clocks_lock, flags); - } + p += sprintf(p, "%s", c->name); + d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root); + if (!d) + return -ENOMEM; + c->dent = d; + + d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount); + if (!d) { + err = -ENOMEM; + goto err_out; + } + d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate); + if (!d) { + err = -ENOMEM; + goto err_out; + } + d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags); + if (!d) { + err = -ENOMEM; + goto err_out; } - mutex_unlock(&clocks_mutex); - pr_info("clock_late_init() disabled %d unused clocks\n", count); return 0; + +err_out: + d = c->dent; + list_for_each_entry(child, &d->d_subdirs, d_u.d_child) + debugfs_remove(child); + debugfs_remove(c->dent); + return err; } -late_initcall(clock_late_init); +static int clk_debugfs_register(struct clk *c) +{ + int err; + struct clk *pa = c->parent; + + if (pa && !pa->dent) { + err = clk_debugfs_register(pa); + if (err) + return err; + } + + if (!c->dent) { + err = clk_debugfs_register_one(c); + if (err) + return err; + } + return 0; +} + +static int __init clk_debugfs_init(void) +{ + struct clk *c; + struct dentry *d; + int err; + + d = debugfs_create_dir("clock", NULL); + if (!d) + return -ENOMEM; + clk_debugfs_root = d; + + list_for_each_entry(c, &clocks, node) { + err = clk_debugfs_register(c); + if (err) + goto err_out; + } + return 0; +err_out: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) + debugfs_remove_recursive(clk_debugfs_root); +#endif + return err; +} +late_initcall(clk_debugfs_init); + +#endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */ + diff --git a/arch/arm/mach-rk2818/clock.h b/arch/arm/mach-rk2818/clock.h deleted file mode 100644 index 4059b10090ba..000000000000 --- a/arch/arm/mach-rk2818/clock.h +++ /dev/null @@ -1,47 +0,0 @@ -/* arch/arm/mach-rk2818/clock.h - * - * Copyright (C) 2010 ROCKCHIP, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef __ARCH_ARM_MACH_RK2818_CLOCK_H -#define __ARCH_ARM_MACH_RK2818_CLOCK_H - -#include - -#define CLKFLAG_INVERT 0x00000001 -#define CLKFLAG_NOINVERT 0x00000002 -#define CLKFLAG_NONEST 0x00000004 -#define CLKFLAG_NORESET 0x00000008 - -#define CLK_FIRST_AVAILABLE_FLAG 0x00000100 -#define CLKFLAG_USE_MIN_MAX_TO_SET 0x00000200 -#define CLKFLAG_AUTO_OFF 0x00000400 - -struct clk { - uint32_t id; - uint32_t count; - uint32_t flags; - const char *name; - struct list_head list; - struct device *dev; -}; - -//#define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) -//#define A11S_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) -//#define A11S_VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124) - -extern struct clk rk2818_clocks[]; -extern unsigned rk2818_num_clocks; - -#endif - -- 2.34.1