diff options
author | Stefan Bosch <stefan_b@posteo.net> | 2020-07-10 19:07:26 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2020-07-29 08:43:40 -0400 |
commit | 95e9a8e2cb409632e4e83be0cb9e1ed4629114c1 (patch) | |
tree | 1a9e11e5e83e28761c31e2950f5fdcd748168ba3 /arch/arm/mach-nexell/clock.c | |
parent | a1ce9ed0637fd329c7c0d5ee7c92ceb332866c65 (diff) |
arm: add mach-nexell (all files except header files)
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- SPL not supported yet --> no spl-directory in arch/arm/mach-nexell.
Appropriate line in Makefile removed.
- clock.c: 'section(".data")' added to declaration of clk_periphs[] and
core_hz.
- Kconfig: Changes to have a structure like in mach-bcm283x/Kconfig,
e.g. "config ..." entries moved from other Kconfig.
- timer.c: 'section(".data")' added to declaration of timestamp and
lastdec.
- arch/arm/mach-nexell/serial.c removed because this is for the UARTs
of the S5P6818 SoC which is not supported yet. S5P4418 UARTs are
different, here the (existing) PL011-code is used.
- '#ifdef CONFIG...' changed to 'if (IS_ENABLED(CONFIG...))' where
possible (and similar).
Signed-off-by: Stefan Bosch <stefan_b@posteo.net>
Diffstat (limited to 'arch/arm/mach-nexell/clock.c')
-rw-r--r-- | arch/arm/mach-nexell/clock.c | 869 |
1 files changed, 869 insertions, 0 deletions
diff --git a/arch/arm/mach-nexell/clock.c b/arch/arm/mach-nexell/clock.c new file mode 100644 index 0000000000..a0ba2d8e0c --- /dev/null +++ b/arch/arm/mach-nexell/clock.c @@ -0,0 +1,869 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Hyunseok, Jung <hsjung@nexell.co.kr> + */ + +#include <common.h> +#include <command.h> +#include <linux/err.h> +#include <asm/io.h> +#include <asm/arch/nexell.h> +#include <asm/arch/clk.h> + +/* + * clock generator macros + */ +#define I_PLL0_BIT (0) +#define I_PLL1_BIT (1) +#define I_PLL2_BIT (2) +#define I_PLL3_BIT (3) +#define I_EXT1_BIT (4) +#define I_EXT2_BIT (5) +#define I_CLKn_BIT (7) +#define I_EXT1_BIT_FORCE (8) +#define I_EXT2_BIT_FORCE (9) + +#define I_CLOCK_NUM 6 /* PLL0, PLL1, PLL2, PLL3, EXT1, EXT2 */ + +#define I_EXECEPT_CLK (0) +#define I_CLOCK_MASK (((1 << I_CLOCK_NUM) - 1) & ~I_EXECEPT_CLK) + +#define I_PLL0 (1 << I_PLL0_BIT) +#define I_PLL1 (1 << I_PLL1_BIT) +#define I_PLL2 (1 << I_PLL2_BIT) +#define I_PLL3 (1 << I_PLL3_BIT) +#define I_EXTCLK1 (1 << I_EXT1_BIT) +#define I_EXTCLK2 (1 << I_EXT2_BIT) +#define I_EXTCLK1_FORCE (1 << I_EXT1_BIT_FORCE) +#define I_EXTCLK2_FORCE (1 << I_EXT2_BIT_FORCE) + +#define I_PLL_0_1 (I_PLL0 | I_PLL1) +#define I_PLL_0_2 (I_PLL_0_1 | I_PLL2) +#define I_PLL_0_3 (I_PLL_0_2 | I_PLL3) +#define I_CLKnOUT (0) + +#define I_PCLK (1 << 16) +#define I_BCLK (1 << 17) +#define I_GATE_PCLK (1 << 20) +#define I_GATE_BCLK (1 << 21) +#define I_PCLK_MASK (I_GATE_PCLK | I_PCLK) +#define I_BCLK_MASK (I_GATE_BCLK | I_BCLK) + +struct clk_dev_peri { + const char *dev_name; + void __iomem *base; + int dev_id; + int periph_id; + int clk_step; + u32 in_mask; + u32 in_mask1; + int div_src_0; + int div_val_0; + int invert_0; + int div_src_1; + int div_val_1; + int invert_1; + int in_extclk_1; + int in_extclk_2; +}; + +struct clk_dev { + struct clk clk; + struct clk *link; + const char *name; + struct clk_dev_peri *peri; +}; + +struct clk_dev_map { + unsigned int con_enb; + unsigned int con_gen[4]; +}; + +#define CLK_PERI_1S(name, devid, id, addr, mk)[id] = \ + { .dev_name = name, .dev_id = devid, .periph_id = id, .clk_step = 1, \ + .base = (void *)addr, .in_mask = mk, } + +#define CLK_PERI_2S(name, devid, id, addr, mk, mk2)[id] = \ + { .dev_name = name, .dev_id = devid, .periph_id = id, .clk_step = 2, \ + .base = (void *)addr, .in_mask = mk, .in_mask1 = mk2, } + +static const char * const clk_core[] = { + CORECLK_NAME_PLL0, CORECLK_NAME_PLL1, CORECLK_NAME_PLL2, + CORECLK_NAME_PLL3, CORECLK_NAME_FCLK, CORECLK_NAME_MCLK, + CORECLK_NAME_BCLK, CORECLK_NAME_PCLK, CORECLK_NAME_HCLK, +}; + +/* + * Section ".data" must be used because BSS is not available before relocation, + * in board_init_f(), respectively! I.e. global variables can not be used! + */ +static struct clk_dev_peri clk_periphs[] + __attribute__((section(".data"))) = { + CLK_PERI_1S(DEV_NAME_TIMER, 0, CLK_ID_TIMER_0, + PHY_BASEADDR_CLKGEN14, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_TIMER, 1, CLK_ID_TIMER_1, + PHY_BASEADDR_CLKGEN0, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_TIMER, 2, CLK_ID_TIMER_2, + PHY_BASEADDR_CLKGEN1, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_TIMER, 3, CLK_ID_TIMER_3, + PHY_BASEADDR_CLKGEN2, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 0, CLK_ID_UART_0, + PHY_BASEADDR_CLKGEN22, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 1, CLK_ID_UART_1, + PHY_BASEADDR_CLKGEN24, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 2, CLK_ID_UART_2, + PHY_BASEADDR_CLKGEN23, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 3, CLK_ID_UART_3, + PHY_BASEADDR_CLKGEN25, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 4, CLK_ID_UART_4, + PHY_BASEADDR_CLKGEN26, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 5, CLK_ID_UART_5, + PHY_BASEADDR_CLKGEN27, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 0, CLK_ID_PWM_0, + PHY_BASEADDR_CLKGEN13, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 1, CLK_ID_PWM_1, + PHY_BASEADDR_CLKGEN3, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 2, CLK_ID_PWM_2, + PHY_BASEADDR_CLKGEN4, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 3, CLK_ID_PWM_3, + PHY_BASEADDR_CLKGEN5, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_I2C, 0, CLK_ID_I2C_0, + PHY_BASEADDR_CLKGEN6, (I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_I2C, 1, CLK_ID_I2C_1, + PHY_BASEADDR_CLKGEN7, (I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_I2C, 2, CLK_ID_I2C_2, + PHY_BASEADDR_CLKGEN8, (I_GATE_PCLK)), + CLK_PERI_2S(DEV_NAME_GMAC, 0, CLK_ID_GMAC, + PHY_BASEADDR_CLKGEN10, + (I_PLL_0_3 | I_EXTCLK1 | I_EXTCLK1_FORCE), + (I_CLKnOUT)), + CLK_PERI_2S(DEV_NAME_I2S, 0, CLK_ID_I2S_0, + PHY_BASEADDR_CLKGEN15, (I_PLL_0_3 | I_EXTCLK1), + (I_CLKnOUT)), + CLK_PERI_2S(DEV_NAME_I2S, 1, CLK_ID_I2S_1, + PHY_BASEADDR_CLKGEN16, (I_PLL_0_3 | I_EXTCLK1), + (I_CLKnOUT)), + CLK_PERI_2S(DEV_NAME_I2S, 2, CLK_ID_I2S_2, + PHY_BASEADDR_CLKGEN17, (I_PLL_0_3 | I_EXTCLK1), + (I_CLKnOUT)), + CLK_PERI_1S(DEV_NAME_SDHC, 0, CLK_ID_SDHC_0, + PHY_BASEADDR_CLKGEN18, (I_PLL_0_2 | I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_SDHC, 1, CLK_ID_SDHC_1, + PHY_BASEADDR_CLKGEN19, (I_PLL_0_2 | I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_SDHC, 2, CLK_ID_SDHC_2, + PHY_BASEADDR_CLKGEN20, (I_PLL_0_2 | I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_SPI, 0, CLK_ID_SPI_0, + PHY_BASEADDR_CLKGEN37, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_SPI, 1, CLK_ID_SPI_1, + PHY_BASEADDR_CLKGEN38, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_SPI, 2, CLK_ID_SPI_2, + PHY_BASEADDR_CLKGEN39, (I_PLL_0_2)), +}; + +#define CLK_PERI_NUM ((int)ARRAY_SIZE(clk_periphs)) +#define CLK_CORE_NUM ((int)ARRAY_SIZE(clk_core)) +#define CLK_DEVS_NUM (CLK_CORE_NUM + CLK_PERI_NUM) +#define MAX_DIVIDER ((1 << 8) - 1) /* 256, align 2 */ + +static struct clk_dev st_clk_devs[CLK_DEVS_NUM] + __attribute__((section(".data"))); +#define clk_dev_get(n) ((struct clk_dev *)&st_clk_devs[n]) +#define clk_container(p) (container_of(p, struct clk_dev, clk)) + +/* + * Core frequencys + */ +struct _core_hz_ { + unsigned long pll[4]; /* PLL */ + unsigned long cpu_fclk, cpu_bclk; /* cpu */ + unsigned long mem_fclk, mem_dclk, mem_bclk, mem_pclk; /* ddr */ + unsigned long bus_bclk, bus_pclk; /* bus */ +#if defined(CONFIG_ARCH_S5P6818) + unsigned long cci4_bclk, cci4_pclk; /* cci */ +#endif + /* ip */ + unsigned long g3d_bclk; + unsigned long coda_bclk, coda_pclk; +#if defined(CONFIG_ARCH_S5P6818) + unsigned long disp_bclk, disp_pclk; + unsigned long hdmi_pclk; +#endif +}; + +/* + * Section ".data" must be used because BSS is not available before relocation, + * in board_init_f(), respectively! I.e. global variables can not be used! + */ +/* core clock */ +static struct _core_hz_ core_hz __attribute__((section(".data"))); + +#define CORE_HZ_SIZE (sizeof(core_hz) / 4) + +/* + * CLKGEN HW + */ +static inline void clk_dev_bclk(void *base, int on) +{ + struct clk_dev_map *reg = base; + unsigned int val = readl(®->con_enb) & ~(0x3); + + val |= (on ? 3 : 0) & 0x3; /* always BCLK */ + writel(val, ®->con_enb); +} + +static inline void clk_dev_pclk(void *base, int on) +{ + struct clk_dev_map *reg = base; + unsigned int val = 0; + + if (!on) + return; + + val = readl(®->con_enb) & ~(1 << 3); + val |= (1 << 3); + writel(val, ®->con_enb); +} + +static inline void clk_dev_rate(void *base, int step, int src, int div) +{ + struct clk_dev_map *reg = base; + unsigned int val = 0; + + val = readl(®->con_gen[step << 1]); + val &= ~(0x07 << 2); + val |= (src << 2); /* source */ + val &= ~(0xFF << 5); + val |= (div - 1) << 5; /* divider */ + writel(val, ®->con_gen[step << 1]); +} + +static inline void clk_dev_inv(void *base, int step, int inv) +{ + struct clk_dev_map *reg = base; + unsigned int val = readl(®->con_gen[step << 1]) & ~(1 << 1); + + val |= (inv << 1); + writel(val, ®->con_gen[step << 1]); +} + +static inline void clk_dev_enb(void *base, int on) +{ + struct clk_dev_map *reg = base; + unsigned int val = readl(®->con_enb) & ~(1 << 2); + + val |= ((on ? 1 : 0) << 2); + writel(val, ®->con_enb); +} + +/* + * CORE FREQUENCY + * + * PLL0 [P,M,S] ------- | | ----- [DIV0] --- CPU-G0 + * |M| ----- [DIV1] --- BCLK/PCLK + * PLL1 [P,M,S] ------- | | ----- [DIV2] --- DDR + * |U| ----- [DIV3] --- 3D + * PLL2 [P,M,S,K]-------| | ----- [DIV4] --- CODA + * |X| ----- [DIV5] --- DISPLAY + * PLL3 [P,M,S,K]-------| | ----- [DIV6] --- HDMI + * | | ----- [DIV7] --- CPU-G1 + * | | ----- [DIV8] --- CCI-400(FASTBUS) + * + */ + +struct nx_clkpwr_registerset { + u32 clkmodereg0; /* 0x000 : Clock Mode Register0 */ + u32 __reserved0; /* 0x004 */ + u32 pllsetreg[4]; /* 0x008 ~ 0x014 : PLL Setting Register */ + u32 __reserved1[2]; /* 0x018 ~ 0x01C */ + u32 dvoreg[9]; /* 0x020 ~ 0x040 : Divider Setting Register */ + u32 __Reserved2; /* 0x044 */ + u32 pllsetreg_sscg[6]; /* 0x048 ~ 0x05C */ + u32 __reserved3[8]; /* 0x060 ~ 0x07C */ + u8 __reserved4[0x200 - 0x80]; /* padding (0x80 ~ 0x1FF) */ + u32 gpiowakeupriseenb; /* 0x200 : GPIO Rising Edge Detect En. Reg. */ + u32 gpiowakeupfallenb; /* 0x204 : GPIO Falling Edge Detect En. Reg. */ + u32 gpiorstenb; /* 0x208 : GPIO Reset Enable Register */ + u32 gpiowakeupenb; /* 0x20C : GPIO Wakeup Source Enable */ + u32 gpiointenb; /* 0x210 : Interrupt Enable Register */ + u32 gpiointpend; /* 0x214 : Interrupt Pend Register */ + u32 resetstatus; /* 0x218 : Reset Status Register */ + u32 intenable; /* 0x21C : Interrupt Enable Register */ + u32 intpend; /* 0x220 : Interrupt Pend Register */ + u32 pwrcont; /* 0x224 : Power Control Register */ + u32 pwrmode; /* 0x228 : Power Mode Register */ + u32 __reserved5; /* 0x22C : Reserved Region */ + u32 scratch[3]; /* 0x230 ~ 0x238 : Scratch Register */ + u32 sysrstconfig; /* 0x23C : System Reset Configuration Reg. */ + u8 __reserved6[0x2A0 - 0x240]; /* padding (0x240 ~ 0x29F) */ + u32 cpupowerdownreq; /* 0x2A0 : CPU Power Down Request Register */ + u32 cpupoweronreq; /* 0x2A4 : CPU Power On Request Register */ + u32 cpuresetmode; /* 0x2A8 : CPU Reset Mode Register */ + u32 cpuwarmresetreq; /* 0x2AC : CPU Warm Reset Request Register */ + u32 __reserved7; /* 0x2B0 */ + u32 cpustatus; /* 0x2B4 : CPU Status Register */ + u8 __reserved8[0x400 - 0x2B8]; /* padding (0x2B8 ~ 0x33F) */ +}; + +static struct nx_clkpwr_registerset * const clkpwr = + (struct nx_clkpwr_registerset *)PHY_BASEADDR_CLKPWR; + +#define getquotient(v, d) ((v) / (d)) + +#define DIV_CPUG0 0 +#define DIV_BUS 1 +#define DIV_MEM 2 +#define DIV_G3D 3 +#define DIV_CODA 4 +#if defined(CONFIG_ARCH_S5P6818) +#define DIV_DISP 5 +#define DIV_HDMI 6 +#define DIV_CPUG1 7 +#define DIV_CCI4 8 +#endif + +#define DVO0 3 +#define DVO1 9 +#define DVO2 15 +#define DVO3 21 + +static unsigned int pll_rate(unsigned int plln, unsigned int xtal) +{ + unsigned int val, val1, nP, nM, nS, nK; + unsigned int temp = 0; + + val = clkpwr->pllsetreg[plln]; + val1 = clkpwr->pllsetreg_sscg[plln]; + xtal /= 1000; /* Unit Khz */ + + nP = (val >> 18) & 0x03F; + nM = (val >> 8) & 0x3FF; + nS = (val >> 0) & 0x0FF; + nK = (val1 >> 16) & 0xFFFF; + + if (plln > 1 && nK) { + temp = (unsigned int)(getquotient((getquotient((nK * 1000), + 65536) * xtal), nP) >> nS); + } + + temp = (unsigned int)((getquotient((nM * xtal), nP) >> nS) * 1000) + + temp; + return temp; +} + +static unsigned int pll_dvo(int dvo) +{ + unsigned int val; + + val = (clkpwr->dvoreg[dvo] & 0x7); + return val; +} + +static unsigned int pll_div(int dvo) +{ + unsigned int val = clkpwr->dvoreg[dvo]; + + return ((((val >> DVO3) & 0x3F) + 1) << 24) | + ((((val >> DVO2) & 0x3F) + 1) << 16) | + ((((val >> DVO1) & 0x3F) + 1) << 8) | + ((((val >> DVO0) & 0x3F) + 1) << 0); +} + +#define PLLN_RATE(n) (pll_rate(n, CONFIG_SYS_PLLFIN)) /* 0~ 3 */ +#define CPU_FCLK_RATE(n) (pll_rate(pll_dvo(n), CONFIG_SYS_PLLFIN) / \ + ((pll_div(n) >> 0) & 0x3F)) +#define CPU_BCLK_RATE(n) (pll_rate(pll_dvo(n), CONFIG_SYS_PLLFIN) / \ + ((pll_div(n) >> 0) & 0x3F) / \ + ((pll_div(n) >> 8) & 0x3F)) + +#define MEM_FCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 8) & 0x3F)) + +#define MEM_DCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F)) + +#define MEM_BCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 8) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 16) & 0x3F)) +#define MEM_PCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 8) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 16) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 24) & 0x3F)) + +#define BUS_BCLK_RATE() (pll_rate(pll_dvo(DIV_BUS), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_BUS) >> 0) & 0x3F)) +#define BUS_PCLK_RATE() (pll_rate(pll_dvo(DIV_BUS), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_BUS) >> 0) & 0x3F) / \ + ((pll_div(DIV_BUS) >> 8) & 0x3F)) + +#define G3D_BCLK_RATE() (pll_rate(pll_dvo(DIV_G3D), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_G3D) >> 0) & 0x3F)) + +#define MPG_BCLK_RATE() (pll_rate(pll_dvo(DIV_CODA), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CODA) >> 0) & 0x3F)) +#define MPG_PCLK_RATE() (pll_rate(pll_dvo(DIV_CODA), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CODA) >> 0) & 0x3F) / \ + ((pll_div(DIV_CODA) >> 8) & 0x3F)) + +#if defined(CONFIG_ARCH_S5P6818) +#define DISP_BCLK_RATE() (pll_rate(pll_dvo(DIV_DISP), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_DISP) >> 0) & 0x3F)) +#define DISP_PCLK_RATE() (pll_rate(pll_dvo(DIV_DISP), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_DISP) >> 0) & 0x3F) / \ + ((pll_div(DIV_DISP) >> 8) & 0x3F)) + +#define HDMI_PCLK_RATE() (pll_rate(pll_dvo(DIV_HDMI), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_HDMI) >> 0) & 0x3F)) + +#define CCI4_BCLK_RATE() (pll_rate(pll_dvo(DIV_CCI4), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CCI4) >> 0) & 0x3F)) +#define CCI4_PCLK_RATE() (pll_rate(pll_dvo(DIV_CCI4), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CCI4) >> 0) & 0x3F) / \ + ((pll_div(DIV_CCI4) >> 8) & 0x3F)) +#endif + +static void core_update_rate(int type) +{ + switch (type) { + case 0: + core_hz.pll[0] = PLLN_RATE(0); break; + case 1: + core_hz.pll[1] = PLLN_RATE(1); break; + case 2: + core_hz.pll[2] = PLLN_RATE(2); break; + case 3: + core_hz.pll[3] = PLLN_RATE(3); break; + case 4: + core_hz.cpu_fclk = CPU_FCLK_RATE(DIV_CPUG0); break; + case 5: + core_hz.mem_fclk = MEM_FCLK_RATE(); break; + case 6: + core_hz.bus_bclk = BUS_BCLK_RATE(); break; + case 7: + core_hz.bus_pclk = BUS_PCLK_RATE(); break; + case 8: + core_hz.cpu_bclk = CPU_BCLK_RATE(DIV_CPUG0); break; + case 9: + core_hz.mem_dclk = MEM_DCLK_RATE(); break; + case 10: + core_hz.mem_bclk = MEM_BCLK_RATE(); break; + case 11: + core_hz.mem_pclk = MEM_PCLK_RATE(); break; + case 12: + core_hz.g3d_bclk = G3D_BCLK_RATE(); break; + case 13: + core_hz.coda_bclk = MPG_BCLK_RATE(); break; + case 14: + core_hz.coda_pclk = MPG_PCLK_RATE(); break; +#if defined(CONFIG_ARCH_S5P6818) + case 15: + core_hz.disp_bclk = DISP_BCLK_RATE(); break; + case 16: + core_hz.disp_pclk = DISP_PCLK_RATE(); break; + case 17: + core_hz.hdmi_pclk = HDMI_PCLK_RATE(); break; + case 18: + core_hz.cci4_bclk = CCI4_BCLK_RATE(); break; + case 19: + core_hz.cci4_pclk = CCI4_PCLK_RATE(); break; +#endif + }; +} + +static unsigned long core_get_rate(int type) +{ + unsigned long rate = 0; + + switch (type) { + case 0: + rate = core_hz.pll[0]; break; + case 1: + rate = core_hz.pll[1]; break; + case 2: + rate = core_hz.pll[2]; break; + case 3: + rate = core_hz.pll[3]; break; + case 4: + rate = core_hz.cpu_fclk; break; + case 5: + rate = core_hz.mem_fclk; break; + case 6: + rate = core_hz.bus_bclk; break; + case 7: + rate = core_hz.bus_pclk; break; + case 8: + rate = core_hz.cpu_bclk; break; + case 9: + rate = core_hz.mem_dclk; break; + case 10: + rate = core_hz.mem_bclk; break; + case 11: + rate = core_hz.mem_pclk; break; + case 12: + rate = core_hz.g3d_bclk; break; + case 13: + rate = core_hz.coda_bclk; break; + case 14: + rate = core_hz.coda_pclk; break; +#if defined(CONFIG_ARCH_S5P6818) + case 15: + rate = core_hz.disp_bclk; break; + case 16: + rate = core_hz.disp_pclk; break; + case 17: + rate = core_hz.hdmi_pclk; break; + case 18: + rate = core_hz.cci4_bclk; break; + case 19: + rate = core_hz.cci4_pclk; break; +#endif + default: + printf("unknown core clock type %d ...\n", type); + break; + }; + return rate; +} + +static long core_set_rate(struct clk *clk, long rate) +{ + return clk->rate; +} + +static void core_rate_init(void) +{ + int i; + + for (i = 0; i < CORE_HZ_SIZE; i++) + core_update_rate(i); +} + +/* + * Clock Interfaces + */ +static inline long clk_divide(long rate, long request, + int align, int *divide) +{ + int div = (rate / request); + int max = MAX_DIVIDER & ~(align - 1); + int adv = (div & ~(align - 1)) + align; + long ret; + + if (!div) { + if (divide) + *divide = 1; + return rate; + } + + if (div != 1) + div &= ~(align - 1); + + if (div != adv && abs(request - rate / div) > abs(request - rate / adv)) + div = adv; + + div = (div > max ? max : div); + if (divide) + *divide = div; + + ret = rate / div; + return ret; +} + +void clk_put(struct clk *clk) +{ +} + +struct clk *clk_get(const char *id) +{ + struct clk_dev *cdev = clk_dev_get(0); + struct clk *clk = NULL; + const char *str = NULL, *c = NULL; + int i, devid; + + if (id) + str = id; + + for (i = 0; i < CLK_DEVS_NUM; i++, cdev++) { + if (!cdev->name) + continue; + if (!strncmp(cdev->name, str, strlen(cdev->name))) { + c = strrchr((const char *)str, (int)'.'); + if (!c || !cdev->peri) + break; + devid = simple_strtoul(++c, NULL, 10); + if (cdev->peri->dev_id == devid) + break; + } + } + if (i < CLK_DEVS_NUM) + clk = &cdev->clk; + else + clk = &(clk_dev_get(7))->clk; /* pclk */ + + return clk ? clk : ERR_PTR(-ENOENT); +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + struct clk_dev *pll = NULL, *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + unsigned long request = rate, rate_hz = 0; + unsigned int mask; + int step, div[2] = { 0, }; + int i, n, clk2 = 0; + int start_src = 0, max_src = I_CLOCK_NUM; + short s1 = 0, s2 = 0, d1 = 0, d2 = 0; + + if (!peri) + return core_set_rate(clk, rate); + + step = peri->clk_step; + mask = peri->in_mask; + debug("clk: %s.%d request = %ld [input=0x%x]\n", peri->dev_name, + peri->dev_id, rate, mask); + + if (!(I_CLOCK_MASK & mask)) { + if (I_PCLK_MASK & mask) + return core_get_rate(CORECLK_ID_PCLK); + else if (I_BCLK_MASK & mask) + return core_get_rate(CORECLK_ID_BCLK); + else + return clk->rate; + } + +next: + if (peri->in_mask & I_EXTCLK1_FORCE) { + start_src = 4; max_src = 5; + } + for (n = start_src ; max_src > n; n++) { + if (!(((mask & I_CLOCK_MASK) >> n) & 0x1)) + continue; + + if (n == I_EXT1_BIT) { + rate = peri->in_extclk_1; + } else if (n == I_EXT2_BIT) { + rate = peri->in_extclk_2; + } else { + pll = clk_dev_get(n); + rate = pll->clk.rate; + } + + if (!rate) + continue; + + for (i = 0; step > i ; i++) + rate = clk_divide(rate, request, 2, &div[i]); + + if (rate_hz && (abs(rate - request) > abs(rate_hz - request))) + continue; + + debug("clk: %s.%d, pll.%d[%lu] request[%ld] calc[%ld]\n", + peri->dev_name, peri->dev_id, n, pll->clk.rate, + request, rate); + + if (clk2) { + s1 = -1, d1 = -1; /* not use */ + s2 = n, d2 = div[0]; + } else { + s1 = n, d1 = div[0]; + s2 = I_CLKn_BIT, d2 = div[1]; + } + rate_hz = rate; + } + + /* search 2th clock from input */ + if (!clk2 && abs(rate_hz - request) && + peri->in_mask1 & ((1 << I_CLOCK_NUM) - 1)) { + clk2 = 1; + mask = peri->in_mask1; + step = 1; + goto next; + } + if (peri->in_mask & I_EXTCLK1_FORCE) { + if (s1 == 0) { + s1 = 4; s2 = 7; + d1 = 1; d2 = 1; + } + } + + peri->div_src_0 = s1, peri->div_val_0 = d1; + peri->div_src_1 = s2, peri->div_val_1 = d2; + clk->rate = rate_hz; + + debug("clk: %s.%d, step[%d] src[%d,%d] %ld", peri->dev_name, + peri->dev_id, peri->clk_step, peri->div_src_0, peri->div_src_1, + rate); + debug("/(div0: %d * div1: %d) = %ld, %ld diff (%ld)\n", + peri->div_val_0, peri->div_val_1, rate_hz, request, + abs(rate_hz - request)); + + return clk->rate; +} + +unsigned long clk_get_rate(struct clk *clk) +{ + struct clk_dev *cdev = clk_container(clk); + + if (cdev->link) + clk = cdev->link; + return clk->rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + struct clk_dev *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + int i; + + if (!peri) + return core_set_rate(clk, rate); + + clk_round_rate(clk, rate); + + for (i = 0; peri->clk_step > i ; i++) { + int s = (i == 0 ? peri->div_src_0 : peri->div_src_1); + int d = (i == 0 ? peri->div_val_0 : peri->div_val_1); + + if (-1 == s) + continue; + + clk_dev_rate(peri->base, i, s, d); + + debug("clk: %s.%d (%p) set_rate [%d] src[%d] div[%d]\n", + peri->dev_name, peri->dev_id, peri->base, i, s, d); + } + + return clk->rate; +} + +int clk_enable(struct clk *clk) +{ + struct clk_dev *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + int i = 0, inv = 0; + + if (!peri) + return 0; + + debug("clk: %s.%d enable (BCLK=%s, PCLK=%s)\n", peri->dev_name, + peri->dev_id, I_GATE_BCLK & peri->in_mask ? "ON" : "PASS", + I_GATE_PCLK & peri->in_mask ? "ON" : "PASS"); + + if (!(I_CLOCK_MASK & peri->in_mask)) { + /* Gated BCLK/PCLK enable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 1); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 1); + + return 0; + } + + /* invert */ + inv = peri->invert_0; + for (; peri->clk_step > i; i++, inv = peri->invert_1) + clk_dev_inv(peri->base, i, inv); + + /* Gated BCLK/PCLK enable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 1); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 1); + + /* restore clock rate */ + for (i = 0; peri->clk_step > i ; i++) { + int s = (i == 0 ? peri->div_src_0 : peri->div_src_1); + int d = (i == 0 ? peri->div_val_0 : peri->div_val_1); + + if (s == -1) + continue; + clk_dev_rate(peri->base, i, s, d); + } + + clk_dev_enb(peri->base, 1); + + return 0; +} + +void clk_disable(struct clk *clk) +{ + struct clk_dev *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + + if (!peri) + return; + + debug("clk: %s.%d disable\n", peri->dev_name, peri->dev_id); + + if (!(I_CLOCK_MASK & peri->in_mask)) { + /* Gated BCLK/PCLK disable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 0); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 0); + + return; + } + + clk_dev_rate(peri->base, 0, 7, 256); /* for power save */ + clk_dev_enb(peri->base, 0); + + /* Gated BCLK/PCLK disable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 0); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 0); +} + +/* + * Core clocks APIs + */ +void __init clk_init(void) +{ + struct clk_dev *cdev = st_clk_devs; + struct clk_dev_peri *peri = clk_periphs; + struct clk *clk = NULL; + int i = 0; + + memset(cdev, 0, sizeof(st_clk_devs)); + core_rate_init(); + + for (i = 0; (CLK_CORE_NUM + CLK_PERI_NUM) > i; i++, cdev++) { + if (i < CLK_CORE_NUM) { + cdev->name = clk_core[i]; + clk = &cdev->clk; + clk->rate = core_get_rate(i); + continue; + } + + peri = &clk_periphs[i - CLK_CORE_NUM]; + peri->base = (void *)peri->base; + + cdev->peri = peri; + cdev->name = peri->dev_name; + + if (!(I_CLOCK_MASK & peri->in_mask)) { + if (I_BCLK_MASK & peri->in_mask) + cdev->clk.rate = core_get_rate(CORECLK_ID_BCLK); + if (I_PCLK_MASK & peri->in_mask) + cdev->clk.rate = core_get_rate(CORECLK_ID_PCLK); + } + + /* prevent uart clock disable for low step debug message */ + #ifndef CONFIG_DEBUG_NX_UART + if (peri->dev_name) { + #ifdef CONFIG_BACKLIGHT_PWM + if (!strcmp(peri->dev_name, DEV_NAME_PWM)) + continue; + #endif + } + #endif + } + debug("CPU : Clock Generator= %d EA, ", CLK_DEVS_NUM); +} |