diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /arch/arm/mach-tcc8k | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'arch/arm/mach-tcc8k')
| -rw-r--r-- | arch/arm/mach-tcc8k/Kconfig | 11 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/Makefile | 9 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/Makefile.boot | 3 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/board-tcc8000-sdk.c | 81 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/clock.c | 580 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/common.h | 15 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/devices.c | 239 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/io.c | 62 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/irq.c | 111 | ||||
| -rw-r--r-- | arch/arm/mach-tcc8k/time.c | 134 |
10 files changed, 1245 insertions, 0 deletions
diff --git a/arch/arm/mach-tcc8k/Kconfig b/arch/arm/mach-tcc8k/Kconfig new file mode 100644 index 00000000000..ad86415d157 --- /dev/null +++ b/arch/arm/mach-tcc8k/Kconfig | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | if ARCH_TCC8K | ||
| 2 | |||
| 3 | comment "TCC8000 systems:" | ||
| 4 | |||
| 5 | config MACH_TCC8000_SDK | ||
| 6 | bool "Telechips TCC8000-SDK development kit" | ||
| 7 | default y | ||
| 8 | help | ||
| 9 | Support for the Telechips TCC8000-SDK board. | ||
| 10 | |||
| 11 | endif | ||
diff --git a/arch/arm/mach-tcc8k/Makefile b/arch/arm/mach-tcc8k/Makefile new file mode 100644 index 00000000000..9bacf31e49b --- /dev/null +++ b/arch/arm/mach-tcc8k/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # | ||
| 2 | # Makefile for TCC8K boards and common files. | ||
| 3 | # | ||
| 4 | |||
| 5 | # Common support | ||
| 6 | obj-y += clock.o irq.o time.o io.o devices.o | ||
| 7 | |||
| 8 | # Board specific support | ||
| 9 | obj-$(CONFIG_MACH_TCC8000_SDK) += board-tcc8000-sdk.o | ||
diff --git a/arch/arm/mach-tcc8k/Makefile.boot b/arch/arm/mach-tcc8k/Makefile.boot new file mode 100644 index 00000000000..f135c9deae1 --- /dev/null +++ b/arch/arm/mach-tcc8k/Makefile.boot | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | zreladdr-y := 0x20008000 | ||
| 2 | params_phys-y := 0x20000100 | ||
| 3 | initrd_phys-y := 0x20800000 | ||
diff --git a/arch/arm/mach-tcc8k/board-tcc8000-sdk.c b/arch/arm/mach-tcc8k/board-tcc8000-sdk.c new file mode 100644 index 00000000000..4cb3c2dd905 --- /dev/null +++ b/arch/arm/mach-tcc8k/board-tcc8000-sdk.c | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2009 Hans J. Koch <hjk@linutronix.de> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/init.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/platform_device.h> | ||
| 13 | |||
| 14 | #include <asm/mach-types.h> | ||
| 15 | |||
| 16 | #include <asm/mach/arch.h> | ||
| 17 | #include <asm/mach/map.h> | ||
| 18 | #include <asm/mach/time.h> | ||
| 19 | |||
| 20 | #include <mach/clock.h> | ||
| 21 | #include <mach/tcc-nand.h> | ||
| 22 | #include <mach/tcc8k-regs.h> | ||
| 23 | |||
| 24 | #include "common.h" | ||
| 25 | |||
| 26 | #define XI_FREQUENCY 12000000 | ||
| 27 | #define XTI_FREQUENCY 32768 | ||
| 28 | |||
| 29 | #ifdef CONFIG_MTD_NAND_TCC | ||
| 30 | /* NAND */ | ||
| 31 | static struct tcc_nand_platform_data tcc8k_sdk_nand_data = { | ||
| 32 | .width = 1, | ||
| 33 | .hw_ecc = 0, | ||
| 34 | }; | ||
| 35 | #endif | ||
| 36 | |||
| 37 | static void __init tcc8k_init(void) | ||
| 38 | { | ||
| 39 | #ifdef CONFIG_MTD_NAND_TCC | ||
| 40 | tcc_nand_device.dev.platform_data = &tcc8k_sdk_nand_data; | ||
| 41 | platform_device_register(&tcc_nand_device); | ||
| 42 | #endif | ||
| 43 | } | ||
| 44 | |||
| 45 | static void __init tcc8k_init_timer(void) | ||
| 46 | { | ||
| 47 | tcc_clocks_init(XI_FREQUENCY, XTI_FREQUENCY); | ||
| 48 | } | ||
| 49 | |||
| 50 | static struct sys_timer tcc8k_timer = { | ||
| 51 | .init = tcc8k_init_timer, | ||
| 52 | }; | ||
| 53 | |||
| 54 | static void __init tcc8k_map_io(void) | ||
| 55 | { | ||
| 56 | tcc8k_map_common_io(); | ||
| 57 | |||
| 58 | /* set PLL0 clock to 96MHz, adapt UART0 divisor */ | ||
| 59 | __raw_writel(0x00026003, CKC_BASE + PLL0CFG_OFFS); | ||
| 60 | __raw_writel(0x10000001, CKC_BASE + ACLKUART0_OFFS); | ||
| 61 | |||
| 62 | /* set PLL1 clock to 192MHz */ | ||
| 63 | __raw_writel(0x00016003, CKC_BASE + PLL1CFG_OFFS); | ||
| 64 | |||
| 65 | /* set PLL2 clock to 48MHz */ | ||
| 66 | __raw_writel(0x00036003, CKC_BASE + PLL2CFG_OFFS); | ||
| 67 | |||
| 68 | /* with CPU freq higher than 150 MHz, need extra DTCM wait */ | ||
| 69 | __raw_writel(0x00000001, SCFG_BASE + DTCMWAIT_OFFS); | ||
| 70 | |||
| 71 | /* PLL locking time as specified */ | ||
| 72 | udelay(300); | ||
| 73 | } | ||
| 74 | |||
| 75 | MACHINE_START(TCC8000_SDK, "Telechips TCC8000-SDK Demo Board") | ||
| 76 | .boot_params = PLAT_PHYS_OFFSET + 0x00000100, | ||
| 77 | .map_io = tcc8k_map_io, | ||
| 78 | .init_irq = tcc8k_init_irq, | ||
| 79 | .init_machine = tcc8k_init, | ||
| 80 | .timer = &tcc8k_timer, | ||
| 81 | MACHINE_END | ||
diff --git a/arch/arm/mach-tcc8k/clock.c b/arch/arm/mach-tcc8k/clock.c new file mode 100644 index 00000000000..e7cdae5c77a --- /dev/null +++ b/arch/arm/mach-tcc8k/clock.c | |||
| @@ -0,0 +1,580 @@ | |||
| 1 | /* | ||
| 2 | * Lowlevel clock handling for Telechips TCC8xxx SoCs | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 by Hans J. Koch <hjk@linutronix.de> | ||
| 5 | * | ||
| 6 | * Licensed under the terms of the GPL v2 | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/clk.h> | ||
| 10 | #include <linux/delay.h> | ||
| 11 | #include <linux/err.h> | ||
| 12 | #include <linux/io.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/spinlock.h> | ||
| 15 | #include <linux/clkdev.h> | ||
| 16 | |||
| 17 | #include <mach/clock.h> | ||
| 18 | #include <mach/irqs.h> | ||
| 19 | #include <mach/tcc8k-regs.h> | ||
| 20 | |||
| 21 | #include "common.h" | ||
| 22 | |||
| 23 | #define BCLKCTR0 (CKC_BASE + BCLKCTR0_OFFS) | ||
| 24 | #define BCLKCTR1 (CKC_BASE + BCLKCTR1_OFFS) | ||
| 25 | |||
| 26 | #define ACLKREF (CKC_BASE + ACLKREF_OFFS) | ||
| 27 | #define ACLKUART0 (CKC_BASE + ACLKUART0_OFFS) | ||
| 28 | #define ACLKUART1 (CKC_BASE + ACLKUART1_OFFS) | ||
| 29 | #define ACLKUART2 (CKC_BASE + ACLKUART2_OFFS) | ||
| 30 | #define ACLKUART3 (CKC_BASE + ACLKUART3_OFFS) | ||
| 31 | #define ACLKUART4 (CKC_BASE + ACLKUART4_OFFS) | ||
| 32 | #define ACLKI2C (CKC_BASE + ACLKI2C_OFFS) | ||
| 33 | #define ACLKADC (CKC_BASE + ACLKADC_OFFS) | ||
| 34 | #define ACLKUSBH (CKC_BASE + ACLKUSBH_OFFS) | ||
| 35 | #define ACLKLCD (CKC_BASE + ACLKLCD_OFFS) | ||
| 36 | #define ACLKSDH0 (CKC_BASE + ACLKSDH0_OFFS) | ||
| 37 | #define ACLKSDH1 (CKC_BASE + ACLKSDH1_OFFS) | ||
| 38 | #define ACLKSPI0 (CKC_BASE + ACLKSPI0_OFFS) | ||
| 39 | #define ACLKSPI1 (CKC_BASE + ACLKSPI1_OFFS) | ||
| 40 | #define ACLKSPDIF (CKC_BASE + ACLKSPDIF_OFFS) | ||
| 41 | #define ACLKC3DEC (CKC_BASE + ACLKC3DEC_OFFS) | ||
| 42 | #define ACLKCAN0 (CKC_BASE + ACLKCAN0_OFFS) | ||
| 43 | #define ACLKCAN1 (CKC_BASE + ACLKCAN1_OFFS) | ||
| 44 | #define ACLKGSB0 (CKC_BASE + ACLKGSB0_OFFS) | ||
| 45 | #define ACLKGSB1 (CKC_BASE + ACLKGSB1_OFFS) | ||
| 46 | #define ACLKGSB2 (CKC_BASE + ACLKGSB2_OFFS) | ||
| 47 | #define ACLKGSB3 (CKC_BASE + ACLKGSB3_OFFS) | ||
| 48 | #define ACLKTCT (CKC_BASE + ACLKTCT_OFFS) | ||
| 49 | #define ACLKTCX (CKC_BASE + ACLKTCX_OFFS) | ||
| 50 | #define ACLKTCZ (CKC_BASE + ACLKTCZ_OFFS) | ||
| 51 | |||
| 52 | #define ACLK_MAX_DIV (0xfff + 1) | ||
| 53 | |||
| 54 | /* Crystal frequencies */ | ||
| 55 | static unsigned long xi_rate, xti_rate; | ||
| 56 | |||
| 57 | static void __iomem *pll_cfg_addr(int pll) | ||
| 58 | { | ||
| 59 | switch (pll) { | ||
| 60 | case 0: return (CKC_BASE + PLL0CFG_OFFS); | ||
| 61 | case 1: return (CKC_BASE + PLL1CFG_OFFS); | ||
| 62 | case 2: return (CKC_BASE + PLL2CFG_OFFS); | ||
| 63 | default: | ||
| 64 | BUG(); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | static int pll_enable(int pll, int enable) | ||
| 69 | { | ||
| 70 | u32 reg; | ||
| 71 | void __iomem *addr = pll_cfg_addr(pll); | ||
| 72 | |||
| 73 | reg = __raw_readl(addr); | ||
| 74 | if (enable) | ||
| 75 | reg &= ~PLLxCFG_PD; | ||
| 76 | else | ||
| 77 | reg |= PLLxCFG_PD; | ||
| 78 | |||
| 79 | __raw_writel(reg, addr); | ||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | static int xi_enable(int enable) | ||
| 84 | { | ||
| 85 | u32 reg; | ||
| 86 | |||
| 87 | reg = __raw_readl(CKC_BASE + CLKCTRL_OFFS); | ||
| 88 | if (enable) | ||
| 89 | reg |= CLKCTRL_XE; | ||
| 90 | else | ||
| 91 | reg &= ~CLKCTRL_XE; | ||
| 92 | |||
| 93 | __raw_writel(reg, CKC_BASE + CLKCTRL_OFFS); | ||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | static int root_clk_enable(enum root_clks src) | ||
| 98 | { | ||
| 99 | switch (src) { | ||
| 100 | case CLK_SRC_PLL0: return pll_enable(0, 1); | ||
| 101 | case CLK_SRC_PLL1: return pll_enable(1, 1); | ||
| 102 | case CLK_SRC_PLL2: return pll_enable(2, 1); | ||
| 103 | case CLK_SRC_XI: return xi_enable(1); | ||
| 104 | default: | ||
| 105 | BUG(); | ||
| 106 | } | ||
| 107 | return 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int root_clk_disable(enum root_clks src) | ||
| 111 | { | ||
| 112 | switch (src) { | ||
| 113 | case CLK_SRC_PLL0: return pll_enable(0, 0); | ||
| 114 | case CLK_SRC_PLL1: return pll_enable(1, 0); | ||
| 115 | case CLK_SRC_PLL2: return pll_enable(2, 0); | ||
| 116 | case CLK_SRC_XI: return xi_enable(0); | ||
| 117 | default: | ||
| 118 | BUG(); | ||
| 119 | } | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | static int enable_clk(struct clk *clk) | ||
| 124 | { | ||
| 125 | u32 reg; | ||
| 126 | |||
| 127 | if (clk->root_id != CLK_SRC_NOROOT) | ||
| 128 | return root_clk_enable(clk->root_id); | ||
| 129 | |||
| 130 | if (clk->aclkreg) { | ||
| 131 | reg = __raw_readl(clk->aclkreg); | ||
| 132 | reg |= ACLK_EN; | ||
| 133 | __raw_writel(reg, clk->aclkreg); | ||
| 134 | } | ||
| 135 | if (clk->bclkctr) { | ||
| 136 | reg = __raw_readl(clk->bclkctr); | ||
| 137 | reg |= 1 << clk->bclk_shift; | ||
| 138 | __raw_writel(reg, clk->bclkctr); | ||
| 139 | } | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | static void disable_clk(struct clk *clk) | ||
| 144 | { | ||
| 145 | u32 reg; | ||
| 146 | |||
| 147 | if (clk->root_id != CLK_SRC_NOROOT) { | ||
| 148 | root_clk_disable(clk->root_id); | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | |||
| 152 | if (clk->bclkctr) { | ||
| 153 | reg = __raw_readl(clk->bclkctr); | ||
| 154 | reg &= ~(1 << clk->bclk_shift); | ||
| 155 | __raw_writel(reg, clk->bclkctr); | ||
| 156 | } | ||
| 157 | if (clk->aclkreg) { | ||
| 158 | reg = __raw_readl(clk->aclkreg); | ||
| 159 | reg &= ~ACLK_EN; | ||
| 160 | __raw_writel(reg, clk->aclkreg); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | static unsigned long get_rate_pll(int pll) | ||
| 165 | { | ||
| 166 | u32 reg; | ||
| 167 | unsigned long s, m, p; | ||
| 168 | void __iomem *addr = pll_cfg_addr(pll); | ||
| 169 | |||
| 170 | reg = __raw_readl(addr); | ||
| 171 | s = (reg >> 16) & 0x07; | ||
| 172 | m = (reg >> 8) & 0xff; | ||
| 173 | p = reg & 0x3f; | ||
| 174 | |||
| 175 | return (m * xi_rate) / (p * (1 << s)); | ||
| 176 | } | ||
| 177 | |||
| 178 | static unsigned long get_rate_pll_div(int pll) | ||
| 179 | { | ||
| 180 | u32 reg; | ||
| 181 | unsigned long div = 0; | ||
| 182 | void __iomem *addr; | ||
| 183 | |||
| 184 | switch (pll) { | ||
| 185 | case 0: | ||
| 186 | addr = CKC_BASE + CLKDIVC0_OFFS; | ||
| 187 | reg = __raw_readl(addr); | ||
| 188 | if (reg & CLKDIVC0_P0E) | ||
| 189 | div = (reg >> 24) & 0x3f; | ||
| 190 | break; | ||
| 191 | case 1: | ||
| 192 | addr = CKC_BASE + CLKDIVC0_OFFS; | ||
| 193 | reg = __raw_readl(addr); | ||
| 194 | if (reg & CLKDIVC0_P1E) | ||
| 195 | div = (reg >> 16) & 0x3f; | ||
| 196 | break; | ||
| 197 | case 2: | ||
| 198 | addr = CKC_BASE + CLKDIVC1_OFFS; | ||
| 199 | reg = __raw_readl(addr); | ||
| 200 | if (reg & CLKDIVC1_P2E) | ||
| 201 | div = reg & 0x3f; | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | return get_rate_pll(pll) / (div + 1); | ||
| 205 | } | ||
| 206 | |||
| 207 | static unsigned long get_rate_xi_div(void) | ||
| 208 | { | ||
| 209 | unsigned long div = 0; | ||
| 210 | u32 reg = __raw_readl(CKC_BASE + CLKDIVC0_OFFS); | ||
| 211 | |||
| 212 | if (reg & CLKDIVC0_XE) | ||
| 213 | div = (reg >> 8) & 0x3f; | ||
| 214 | |||
| 215 | return xi_rate / (div + 1); | ||
| 216 | } | ||
| 217 | |||
| 218 | static unsigned long get_rate_xti_div(void) | ||
| 219 | { | ||
| 220 | unsigned long div = 0; | ||
| 221 | u32 reg = __raw_readl(CKC_BASE + CLKDIVC0_OFFS); | ||
| 222 | |||
| 223 | if (reg & CLKDIVC0_XTE) | ||
| 224 | div = reg & 0x3f; | ||
| 225 | |||
| 226 | return xti_rate / (div + 1); | ||
| 227 | } | ||
| 228 | |||
| 229 | static unsigned long root_clk_get_rate(enum root_clks src) | ||
| 230 | { | ||
| 231 | switch (src) { | ||
| 232 | case CLK_SRC_PLL0: return get_rate_pll(0); | ||
| 233 | case CLK_SRC_PLL1: return get_rate_pll(1); | ||
| 234 | case CLK_SRC_PLL2: return get_rate_pll(2); | ||
| 235 | case CLK_SRC_PLL0DIV: return get_rate_pll_div(0); | ||
| 236 | case CLK_SRC_PLL1DIV: return get_rate_pll_div(1); | ||
| 237 | case CLK_SRC_PLL2DIV: return get_rate_pll_div(2); | ||
| 238 | case CLK_SRC_XI: return xi_rate; | ||
| 239 | case CLK_SRC_XTI: return xti_rate; | ||
| 240 | case CLK_SRC_XIDIV: return get_rate_xi_div(); | ||
| 241 | case CLK_SRC_XTIDIV: return get_rate_xti_div(); | ||
| 242 | default: return 0; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | static unsigned long aclk_get_rate(struct clk *clk) | ||
| 247 | { | ||
| 248 | u32 reg; | ||
| 249 | unsigned long div; | ||
| 250 | unsigned int src; | ||
| 251 | |||
| 252 | reg = __raw_readl(clk->aclkreg); | ||
| 253 | div = reg & 0x0fff; | ||
| 254 | src = (reg >> ACLK_SEL_SHIFT) & CLK_SRC_MASK; | ||
| 255 | return root_clk_get_rate(src) / (div + 1); | ||
| 256 | } | ||
| 257 | |||
| 258 | static unsigned long aclk_best_div(struct clk *clk, unsigned long rate) | ||
| 259 | { | ||
| 260 | unsigned long div, src, freq, r1, r2; | ||
| 261 | |||
| 262 | if (!rate) | ||
| 263 | return ACLK_MAX_DIV; | ||
| 264 | |||
| 265 | src = __raw_readl(clk->aclkreg) >> ACLK_SEL_SHIFT; | ||
| 266 | src &= CLK_SRC_MASK; | ||
| 267 | freq = root_clk_get_rate(src); | ||
| 268 | div = freq / rate; | ||
| 269 | if (!div) | ||
| 270 | return 1; | ||
| 271 | if (div >= ACLK_MAX_DIV) | ||
| 272 | return ACLK_MAX_DIV; | ||
| 273 | r1 = freq / div; | ||
| 274 | r2 = freq / (div + 1); | ||
| 275 | if ((rate - r2) < (r1 - rate)) | ||
| 276 | return div + 1; | ||
| 277 | |||
| 278 | return div; | ||
| 279 | } | ||
| 280 | |||
| 281 | static unsigned long aclk_round_rate(struct clk *clk, unsigned long rate) | ||
| 282 | { | ||
| 283 | unsigned int src; | ||
| 284 | |||
| 285 | src = __raw_readl(clk->aclkreg) >> ACLK_SEL_SHIFT; | ||
| 286 | src &= CLK_SRC_MASK; | ||
| 287 | |||
| 288 | return root_clk_get_rate(src) / aclk_best_div(clk, rate); | ||
| 289 | } | ||
| 290 | |||
| 291 | static int aclk_set_rate(struct clk *clk, unsigned long rate) | ||
| 292 | { | ||
| 293 | u32 reg; | ||
| 294 | |||
| 295 | reg = __raw_readl(clk->aclkreg) & ~ACLK_DIV_MASK; | ||
| 296 | reg |= aclk_best_div(clk, rate) - 1; | ||
| 297 | __raw_writel(reg, clk->aclkreg); | ||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static unsigned long get_rate_sys(struct clk *clk) | ||
| 302 | { | ||
| 303 | unsigned int src; | ||
| 304 | |||
| 305 | src = __raw_readl(CKC_BASE + CLKCTRL_OFFS) & CLK_SRC_MASK; | ||
| 306 | return root_clk_get_rate(src); | ||
| 307 | } | ||
| 308 | |||
| 309 | static unsigned long get_rate_bus(struct clk *clk) | ||
| 310 | { | ||
| 311 | unsigned int reg, sdiv, bdiv, rate; | ||
| 312 | |||
| 313 | reg = __raw_readl(CKC_BASE + CLKCTRL_OFFS); | ||
| 314 | rate = get_rate_sys(clk); | ||
| 315 | sdiv = (reg >> 20) & 3; | ||
| 316 | if (sdiv) | ||
| 317 | rate /= sdiv + 1; | ||
| 318 | bdiv = (reg >> 4) & 0xff; | ||
| 319 | if (bdiv) | ||
| 320 | rate /= bdiv + 1; | ||
| 321 | return rate; | ||
| 322 | } | ||
| 323 | |||
| 324 | static unsigned long get_rate_cpu(struct clk *clk) | ||
| 325 | { | ||
| 326 | unsigned int reg, div, fsys, fbus; | ||
| 327 | |||
| 328 | fbus = get_rate_bus(clk); | ||
| 329 | reg = __raw_readl(CKC_BASE + CLKCTRL_OFFS); | ||
| 330 | if (reg & (1 << 29)) | ||
| 331 | return fbus; | ||
| 332 | fsys = get_rate_sys(clk); | ||
| 333 | div = (reg >> 16) & 0x0f; | ||
| 334 | return fbus + ((fsys - fbus) * (div + 1)) / 16; | ||
| 335 | } | ||
| 336 | |||
| 337 | static unsigned long get_rate_root(struct clk *clk) | ||
| 338 | { | ||
| 339 | return root_clk_get_rate(clk->root_id); | ||
| 340 | } | ||
| 341 | |||
| 342 | static int aclk_set_parent(struct clk *clock, struct clk *parent) | ||
| 343 | { | ||
| 344 | u32 reg; | ||
| 345 | |||
| 346 | if (clock->parent == parent) | ||
| 347 | return 0; | ||
| 348 | |||
| 349 | clock->parent = parent; | ||
| 350 | |||
| 351 | if (!parent) | ||
| 352 | return 0; | ||
| 353 | |||
| 354 | if (parent->root_id == CLK_SRC_NOROOT) | ||
| 355 | return 0; | ||
| 356 | reg = __raw_readl(clock->aclkreg); | ||
| 357 | reg &= ~ACLK_SEL_MASK; | ||
| 358 | reg |= (parent->root_id << ACLK_SEL_SHIFT) & ACLK_SEL_MASK; | ||
| 359 | __raw_writel(reg, clock->aclkreg); | ||
| 360 | |||
| 361 | return 0; | ||
| 362 | } | ||
| 363 | |||
| 364 | #define DEFINE_ROOT_CLOCK(name, ri, p) \ | ||
| 365 | static struct clk name = { \ | ||
| 366 | .root_id = ri, \ | ||
| 367 | .get_rate = get_rate_root, \ | ||
| 368 | .enable = enable_clk, \ | ||
| 369 | .disable = disable_clk, \ | ||
| 370 | .parent = p, \ | ||
| 371 | }; | ||
| 372 | |||
| 373 | #define DEFINE_SPECIAL_CLOCK(name, gr, p) \ | ||
| 374 | static struct clk name = { \ | ||
| 375 | .root_id = CLK_SRC_NOROOT, \ | ||
| 376 | .get_rate = gr, \ | ||
| 377 | .parent = p, \ | ||
| 378 | }; | ||
| 379 | |||
| 380 | #define DEFINE_ACLOCK(name, bc, bs, ar) \ | ||
| 381 | static struct clk name = { \ | ||
| 382 | .root_id = CLK_SRC_NOROOT, \ | ||
| 383 | .bclkctr = bc, \ | ||
| 384 | .bclk_shift = bs, \ | ||
| 385 | .aclkreg = ar, \ | ||
| 386 | .get_rate = aclk_get_rate, \ | ||
| 387 | .set_rate = aclk_set_rate, \ | ||
| 388 | .round_rate = aclk_round_rate, \ | ||
| 389 | .enable = enable_clk, \ | ||
| 390 | .disable = disable_clk, \ | ||
| 391 | .set_parent = aclk_set_parent, \ | ||
| 392 | }; | ||
| 393 | |||
| 394 | #define DEFINE_BCLOCK(name, bc, bs, gr, p) \ | ||
| 395 | static struct clk name = { \ | ||
| 396 | .root_id = CLK_SRC_NOROOT, \ | ||
| 397 | .bclkctr = bc, \ | ||
| 398 | .bclk_shift = bs, \ | ||
| 399 | .get_rate = gr, \ | ||
| 400 | .enable = enable_clk, \ | ||
| 401 | .disable = disable_clk, \ | ||
| 402 | .parent = p, \ | ||
| 403 | }; | ||
| 404 | |||
| 405 | DEFINE_ROOT_CLOCK(xi, CLK_SRC_XI, NULL) | ||
| 406 | DEFINE_ROOT_CLOCK(xti, CLK_SRC_XTI, NULL) | ||
| 407 | DEFINE_ROOT_CLOCK(xidiv, CLK_SRC_XIDIV, &xi) | ||
| 408 | DEFINE_ROOT_CLOCK(xtidiv, CLK_SRC_XTIDIV, &xti) | ||
| 409 | DEFINE_ROOT_CLOCK(pll0, CLK_SRC_PLL0, &xi) | ||
| 410 | DEFINE_ROOT_CLOCK(pll1, CLK_SRC_PLL1, &xi) | ||
| 411 | DEFINE_ROOT_CLOCK(pll2, CLK_SRC_PLL2, &xi) | ||
| 412 | DEFINE_ROOT_CLOCK(pll0div, CLK_SRC_PLL0DIV, &pll0) | ||
| 413 | DEFINE_ROOT_CLOCK(pll1div, CLK_SRC_PLL1DIV, &pll1) | ||
| 414 | DEFINE_ROOT_CLOCK(pll2div, CLK_SRC_PLL2DIV, &pll2) | ||
| 415 | |||
| 416 | /* The following 3 clocks are special and are initialized explicitly later */ | ||
| 417 | DEFINE_SPECIAL_CLOCK(sys, get_rate_sys, NULL) | ||
| 418 | DEFINE_SPECIAL_CLOCK(bus, get_rate_bus, &sys) | ||
| 419 | DEFINE_SPECIAL_CLOCK(cpu, get_rate_cpu, &sys) | ||
| 420 | |||
| 421 | DEFINE_ACLOCK(tct, NULL, 0, ACLKTCT) | ||
| 422 | DEFINE_ACLOCK(tcx, NULL, 0, ACLKTCX) | ||
| 423 | DEFINE_ACLOCK(tcz, NULL, 0, ACLKTCZ) | ||
| 424 | DEFINE_ACLOCK(ref, NULL, 0, ACLKREF) | ||
| 425 | DEFINE_ACLOCK(uart0, BCLKCTR0, 5, ACLKUART0) | ||
| 426 | DEFINE_ACLOCK(uart1, BCLKCTR0, 23, ACLKUART1) | ||
| 427 | DEFINE_ACLOCK(uart2, BCLKCTR0, 6, ACLKUART2) | ||
| 428 | DEFINE_ACLOCK(uart3, BCLKCTR0, 8, ACLKUART3) | ||
| 429 | DEFINE_ACLOCK(uart4, BCLKCTR1, 6, ACLKUART4) | ||
| 430 | DEFINE_ACLOCK(i2c, BCLKCTR0, 7, ACLKI2C) | ||
| 431 | DEFINE_ACLOCK(adc, BCLKCTR0, 10, ACLKADC) | ||
| 432 | DEFINE_ACLOCK(usbh0, BCLKCTR0, 11, ACLKUSBH) | ||
| 433 | DEFINE_ACLOCK(lcd, BCLKCTR0, 13, ACLKLCD) | ||
| 434 | DEFINE_ACLOCK(sd0, BCLKCTR0, 17, ACLKSDH0) | ||
| 435 | DEFINE_ACLOCK(sd1, BCLKCTR1, 5, ACLKSDH1) | ||
| 436 | DEFINE_ACLOCK(spi0, BCLKCTR0, 24, ACLKSPI0) | ||
| 437 | DEFINE_ACLOCK(spi1, BCLKCTR0, 30, ACLKSPI1) | ||
| 438 | DEFINE_ACLOCK(spdif, BCLKCTR1, 2, ACLKSPDIF) | ||
| 439 | DEFINE_ACLOCK(c3dec, BCLKCTR1, 9, ACLKC3DEC) | ||
| 440 | DEFINE_ACLOCK(can0, BCLKCTR1, 10, ACLKCAN0) | ||
| 441 | DEFINE_ACLOCK(can1, BCLKCTR1, 11, ACLKCAN1) | ||
| 442 | DEFINE_ACLOCK(gsb0, BCLKCTR1, 13, ACLKGSB0) | ||
| 443 | DEFINE_ACLOCK(gsb1, BCLKCTR1, 14, ACLKGSB1) | ||
| 444 | DEFINE_ACLOCK(gsb2, BCLKCTR1, 15, ACLKGSB2) | ||
| 445 | DEFINE_ACLOCK(gsb3, BCLKCTR1, 16, ACLKGSB3) | ||
| 446 | DEFINE_ACLOCK(usbh1, BCLKCTR1, 20, ACLKUSBH) | ||
| 447 | |||
| 448 | DEFINE_BCLOCK(dai0, BCLKCTR0, 0, NULL, NULL) | ||
| 449 | DEFINE_BCLOCK(pic, BCLKCTR0, 1, NULL, NULL) | ||
| 450 | DEFINE_BCLOCK(tc, BCLKCTR0, 2, NULL, NULL) | ||
| 451 | DEFINE_BCLOCK(gpio, BCLKCTR0, 3, NULL, NULL) | ||
| 452 | DEFINE_BCLOCK(usbd, BCLKCTR0, 4, NULL, NULL) | ||
| 453 | DEFINE_BCLOCK(ecc, BCLKCTR0, 9, NULL, NULL) | ||
| 454 | DEFINE_BCLOCK(gdma0, BCLKCTR0, 12, NULL, NULL) | ||
| 455 | DEFINE_BCLOCK(rtc, BCLKCTR0, 15, NULL, NULL) | ||
| 456 | DEFINE_BCLOCK(nfc, BCLKCTR0, 16, NULL, NULL) | ||
| 457 | DEFINE_BCLOCK(g2d, BCLKCTR0, 18, NULL, NULL) | ||
| 458 | DEFINE_BCLOCK(gdma1, BCLKCTR0, 22, NULL, NULL) | ||
| 459 | DEFINE_BCLOCK(mscl, BCLKCTR0, 25, NULL, NULL) | ||
| 460 | DEFINE_BCLOCK(bdma, BCLKCTR1, 0, NULL, NULL) | ||
| 461 | DEFINE_BCLOCK(adma0, BCLKCTR1, 1, NULL, NULL) | ||
| 462 | DEFINE_BCLOCK(scfg, BCLKCTR1, 3, NULL, NULL) | ||
| 463 | DEFINE_BCLOCK(cid, BCLKCTR1, 4, NULL, NULL) | ||
| 464 | DEFINE_BCLOCK(dai1, BCLKCTR1, 7, NULL, NULL) | ||
| 465 | DEFINE_BCLOCK(adma1, BCLKCTR1, 8, NULL, NULL) | ||
| 466 | DEFINE_BCLOCK(gps, BCLKCTR1, 12, NULL, NULL) | ||
| 467 | DEFINE_BCLOCK(gdma2, BCLKCTR1, 17, NULL, NULL) | ||
| 468 | DEFINE_BCLOCK(gdma3, BCLKCTR1, 18, NULL, NULL) | ||
| 469 | DEFINE_BCLOCK(ddrc, BCLKCTR1, 19, NULL, NULL) | ||
| 470 | |||
| 471 | #define _REGISTER_CLOCK(d, n, c) \ | ||
| 472 | { \ | ||
| 473 | .dev_id = d, \ | ||
| 474 | .con_id = n, \ | ||
| 475 | .clk = &c, \ | ||
| 476 | }, | ||
| 477 | |||
| 478 | static struct clk_lookup lookups[] = { | ||
| 479 | _REGISTER_CLOCK(NULL, "bus", bus) | ||
| 480 | _REGISTER_CLOCK(NULL, "cpu", cpu) | ||
| 481 | _REGISTER_CLOCK(NULL, "tct", tct) | ||
| 482 | _REGISTER_CLOCK(NULL, "tcx", tcx) | ||
| 483 | _REGISTER_CLOCK(NULL, "tcz", tcz) | ||
| 484 | _REGISTER_CLOCK(NULL, "ref", ref) | ||
| 485 | _REGISTER_CLOCK(NULL, "dai0", dai0) | ||
| 486 | _REGISTER_CLOCK(NULL, "pic", pic) | ||
| 487 | _REGISTER_CLOCK(NULL, "tc", tc) | ||
| 488 | _REGISTER_CLOCK(NULL, "gpio", gpio) | ||
| 489 | _REGISTER_CLOCK(NULL, "usbd", usbd) | ||
| 490 | _REGISTER_CLOCK("tcc-uart.0", NULL, uart0) | ||
| 491 | _REGISTER_CLOCK("tcc-uart.2", NULL, uart2) | ||
| 492 | _REGISTER_CLOCK("tcc-i2c", NULL, i2c) | ||
| 493 | _REGISTER_CLOCK("tcc-uart.3", NULL, uart3) | ||
| 494 | _REGISTER_CLOCK(NULL, "ecc", ecc) | ||
| 495 | _REGISTER_CLOCK(NULL, "adc", adc) | ||
| 496 | _REGISTER_CLOCK("tcc-usbh.0", "usb", usbh0) | ||
| 497 | _REGISTER_CLOCK(NULL, "gdma0", gdma0) | ||
| 498 | _REGISTER_CLOCK(NULL, "lcd", lcd) | ||
| 499 | _REGISTER_CLOCK(NULL, "rtc", rtc) | ||
| 500 | _REGISTER_CLOCK(NULL, "nfc", nfc) | ||
| 501 | _REGISTER_CLOCK("tcc-mmc.0", NULL, sd0) | ||
| 502 | _REGISTER_CLOCK(NULL, "g2d", g2d) | ||
| 503 | _REGISTER_CLOCK(NULL, "gdma1", gdma1) | ||
| 504 | _REGISTER_CLOCK("tcc-uart.1", NULL, uart1) | ||
| 505 | _REGISTER_CLOCK("tcc-spi.0", NULL, spi0) | ||
| 506 | _REGISTER_CLOCK(NULL, "mscl", mscl) | ||
| 507 | _REGISTER_CLOCK("tcc-spi.1", NULL, spi1) | ||
| 508 | _REGISTER_CLOCK(NULL, "bdma", bdma) | ||
| 509 | _REGISTER_CLOCK(NULL, "adma0", adma0) | ||
| 510 | _REGISTER_CLOCK(NULL, "spdif", spdif) | ||
| 511 | _REGISTER_CLOCK(NULL, "scfg", scfg) | ||
| 512 | _REGISTER_CLOCK(NULL, "cid", cid) | ||
| 513 | _REGISTER_CLOCK("tcc-mmc.1", NULL, sd1) | ||
| 514 | _REGISTER_CLOCK("tcc-uart.4", NULL, uart4) | ||
| 515 | _REGISTER_CLOCK(NULL, "dai1", dai1) | ||
| 516 | _REGISTER_CLOCK(NULL, "adma1", adma1) | ||
| 517 | _REGISTER_CLOCK(NULL, "c3dec", c3dec) | ||
| 518 | _REGISTER_CLOCK("tcc-can.0", NULL, can0) | ||
| 519 | _REGISTER_CLOCK("tcc-can.1", NULL, can1) | ||
| 520 | _REGISTER_CLOCK(NULL, "gps", gps) | ||
| 521 | _REGISTER_CLOCK("tcc-gsb.0", NULL, gsb0) | ||
| 522 | _REGISTER_CLOCK("tcc-gsb.1", NULL, gsb1) | ||
| 523 | _REGISTER_CLOCK("tcc-gsb.2", NULL, gsb2) | ||
| 524 | _REGISTER_CLOCK("tcc-gsb.3", NULL, gsb3) | ||
| 525 | _REGISTER_CLOCK(NULL, "gdma2", gdma2) | ||
| 526 | _REGISTER_CLOCK(NULL, "gdma3", gdma3) | ||
| 527 | _REGISTER_CLOCK(NULL, "ddrc", ddrc) | ||
| 528 | _REGISTER_CLOCK("tcc-usbh.1", "usb", usbh1) | ||
| 529 | }; | ||
| 530 | |||
| 531 | static struct clk *root_clk_by_index(enum root_clks src) | ||
| 532 | { | ||
| 533 | switch (src) { | ||
| 534 | case CLK_SRC_PLL0: return &pll0; | ||
| 535 | case CLK_SRC_PLL1: return &pll1; | ||
| 536 | case CLK_SRC_PLL2: return &pll2; | ||
| 537 | case CLK_SRC_PLL0DIV: return &pll0div; | ||
| 538 | case CLK_SRC_PLL1DIV: return &pll1div; | ||
| 539 | case CLK_SRC_PLL2DIV: return &pll2div; | ||
| 540 | case CLK_SRC_XI: return ξ | ||
| 541 | case CLK_SRC_XTI: return &xti; | ||
| 542 | case CLK_SRC_XIDIV: return &xidiv; | ||
| 543 | case CLK_SRC_XTIDIV: return &xtidiv; | ||
| 544 | default: return NULL; | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | static void find_aclk_parent(struct clk *clk) | ||
| 549 | { | ||
| 550 | unsigned int src; | ||
| 551 | struct clk *clock; | ||
| 552 | |||
| 553 | if (!clk->aclkreg) | ||
| 554 | return; | ||
| 555 | |||
| 556 | src = __raw_readl(clk->aclkreg) >> ACLK_SEL_SHIFT; | ||
| 557 | src &= CLK_SRC_MASK; | ||
| 558 | |||
| 559 | clock = root_clk_by_index(src); | ||
| 560 | if (!clock) | ||
| 561 | return; | ||
| 562 | |||
| 563 | clk->parent = clock; | ||
| 564 | clk->set_parent = aclk_set_parent; | ||
| 565 | } | ||
| 566 | |||
| 567 | void __init tcc_clocks_init(unsigned long xi_freq, unsigned long xti_freq) | ||
| 568 | { | ||
| 569 | int i; | ||
| 570 | |||
| 571 | xi_rate = xi_freq; | ||
| 572 | xti_rate = xti_freq; | ||
| 573 | |||
| 574 | /* fixup parents and add the clock */ | ||
| 575 | for (i = 0; i < ARRAY_SIZE(lookups); i++) { | ||
| 576 | find_aclk_parent(lookups[i].clk); | ||
| 577 | clkdev_add(&lookups[i]); | ||
| 578 | } | ||
| 579 | tcc8k_timer_init(&tcz, (void __iomem *)TIMER_BASE, INT_TC32); | ||
| 580 | } | ||
diff --git a/arch/arm/mach-tcc8k/common.h b/arch/arm/mach-tcc8k/common.h new file mode 100644 index 00000000000..705690add39 --- /dev/null +++ b/arch/arm/mach-tcc8k/common.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef MACH_TCC8K_COMMON_H | ||
| 2 | #define MACH_TCC8K_COMMON_H | ||
| 3 | |||
| 4 | #include <linux/platform_device.h> | ||
| 5 | |||
| 6 | extern struct platform_device tcc_nand_device; | ||
| 7 | |||
| 8 | struct clk; | ||
| 9 | |||
| 10 | extern void tcc_clocks_init(unsigned long xi_freq, unsigned long xti_freq); | ||
| 11 | extern void tcc8k_timer_init(struct clk *clock, void __iomem *base, int irq); | ||
| 12 | extern void tcc8k_init_irq(void); | ||
| 13 | extern void tcc8k_map_common_io(void); | ||
| 14 | |||
| 15 | #endif | ||
diff --git a/arch/arm/mach-tcc8k/devices.c b/arch/arm/mach-tcc8k/devices.c new file mode 100644 index 00000000000..6722ad7c283 --- /dev/null +++ b/arch/arm/mach-tcc8k/devices.c | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/mach-tcc8k/devices.c | ||
| 3 | * | ||
| 4 | * Copyright (C) Telechips, Inc. | ||
| 5 | * Copyright (C) 2009 Hans J. Koch <hjk@linutronix.de> | ||
| 6 | * | ||
| 7 | * Licensed under the terms of GPL v2. | ||
| 8 | * | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/dma-mapping.h> | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/io.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | |||
| 17 | #include <asm/mach/map.h> | ||
| 18 | |||
| 19 | #include <mach/tcc8k-regs.h> | ||
| 20 | #include <mach/irqs.h> | ||
| 21 | |||
| 22 | #include "common.h" | ||
| 23 | |||
| 24 | static u64 tcc8k_dmamask = DMA_BIT_MASK(32); | ||
| 25 | |||
| 26 | #ifdef CONFIG_MTD_NAND_TCC | ||
| 27 | /* NAND controller */ | ||
| 28 | static struct resource tcc_nand_resources[] = { | ||
| 29 | { | ||
| 30 | .start = (resource_size_t)NFC_BASE, | ||
| 31 | .end = (resource_size_t)NFC_BASE + 0x7f, | ||
| 32 | .flags = IORESOURCE_MEM, | ||
| 33 | }, { | ||
| 34 | .start = INT_NFC, | ||
| 35 | .end = INT_NFC, | ||
| 36 | .flags = IORESOURCE_IRQ, | ||
| 37 | }, | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct platform_device tcc_nand_device = { | ||
| 41 | .name = "tcc_nand", | ||
| 42 | .id = 0, | ||
| 43 | .num_resources = ARRAY_SIZE(tcc_nand_resources), | ||
| 44 | .resource = tcc_nand_resources, | ||
| 45 | }; | ||
| 46 | #endif | ||
| 47 | |||
| 48 | #ifdef CONFIG_MMC_TCC8K | ||
| 49 | /* MMC controller */ | ||
| 50 | static struct resource tcc8k_mmc0_resource[] = { | ||
| 51 | { | ||
| 52 | .start = INT_SD0, | ||
| 53 | .end = INT_SD0, | ||
| 54 | .flags = IORESOURCE_IRQ, | ||
| 55 | }, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static struct resource tcc8k_mmc1_resource[] = { | ||
| 59 | { | ||
| 60 | .start = INT_SD1, | ||
| 61 | .end = INT_SD1, | ||
| 62 | .flags = IORESOURCE_IRQ, | ||
| 63 | }, | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct platform_device tcc8k_mmc0_device = { | ||
| 67 | .name = "tcc-mmc", | ||
| 68 | .id = 0, | ||
| 69 | .num_resources = ARRAY_SIZE(tcc8k_mmc0_resource), | ||
| 70 | .resource = tcc8k_mmc0_resource, | ||
| 71 | .dev = { | ||
| 72 | .dma_mask = &tcc8k_dmamask, | ||
| 73 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
| 74 | } | ||
| 75 | }; | ||
| 76 | |||
| 77 | struct platform_device tcc8k_mmc1_device = { | ||
| 78 | .name = "tcc-mmc", | ||
| 79 | .id = 1, | ||
| 80 | .num_resources = ARRAY_SIZE(tcc8k_mmc1_resource), | ||
| 81 | .resource = tcc8k_mmc1_resource, | ||
| 82 | .dev = { | ||
| 83 | .dma_mask = &tcc8k_dmamask, | ||
| 84 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
| 85 | } | ||
| 86 | }; | ||
| 87 | |||
| 88 | static inline void tcc8k_init_mmc(void) | ||
| 89 | { | ||
| 90 | u32 reg = __raw_readl(GPIOPS_BASE + GPIOPS_FS1_OFFS); | ||
| 91 | |||
| 92 | reg |= GPIOPS_FS1_SDH0_BITS | GPIOPS_FS1_SDH1_BITS; | ||
| 93 | __raw_writel(reg, GPIOPS_BASE + GPIOPS_FS1_OFFS); | ||
| 94 | |||
| 95 | platform_device_register(&tcc8k_mmc0_device); | ||
| 96 | platform_device_register(&tcc8k_mmc1_device); | ||
| 97 | } | ||
| 98 | #else | ||
| 99 | static inline void tcc8k_init_mmc(void) { } | ||
| 100 | #endif | ||
| 101 | |||
| 102 | #ifdef CONFIG_USB_OHCI_HCD | ||
| 103 | static int tcc8k_ohci_init(struct device *dev) | ||
| 104 | { | ||
| 105 | u32 reg; | ||
| 106 | |||
| 107 | /* Use GPIO PK19 as VBUS control output */ | ||
| 108 | reg = __raw_readl(GPIOPK_BASE + GPIOPK_FS0_OFFS); | ||
| 109 | reg &= ~(1 << 19); | ||
| 110 | __raw_writel(reg, GPIOPK_BASE + GPIOPK_FS0_OFFS); | ||
| 111 | reg = __raw_readl(GPIOPK_BASE + GPIOPK_FS1_OFFS); | ||
| 112 | reg &= ~(1 << 19); | ||
| 113 | __raw_writel(reg, GPIOPK_BASE + GPIOPK_FS1_OFFS); | ||
| 114 | |||
| 115 | reg = __raw_readl(GPIOPK_BASE + GPIOPK_DOE_OFFS); | ||
| 116 | reg |= (1 << 19); | ||
| 117 | __raw_writel(reg, GPIOPK_BASE + GPIOPK_DOE_OFFS); | ||
| 118 | /* Turn on VBUS */ | ||
| 119 | reg = __raw_readl(GPIOPK_BASE + GPIOPK_DAT_OFFS); | ||
| 120 | reg |= (1 << 19); | ||
| 121 | __raw_writel(reg, GPIOPK_BASE + GPIOPK_DAT_OFFS); | ||
| 122 | |||
| 123 | return 0; | ||
| 124 | } | ||
| 125 | |||
| 126 | static struct resource tcc8k_ohci0_resources[] = { | ||
| 127 | [0] = { | ||
| 128 | .start = (resource_size_t)USBH0_BASE, | ||
| 129 | .end = (resource_size_t)USBH0_BASE + 0x5c, | ||
| 130 | .flags = IORESOURCE_MEM, | ||
| 131 | }, | ||
| 132 | [1] = { | ||
| 133 | .start = INT_USBH0, | ||
| 134 | .end = INT_USBH0, | ||
| 135 | .flags = IORESOURCE_IRQ, | ||
| 136 | } | ||
| 137 | }; | ||
| 138 | |||
| 139 | static struct resource tcc8k_ohci1_resources[] = { | ||
| 140 | [0] = { | ||
| 141 | .start = (resource_size_t)USBH1_BASE, | ||
| 142 | .end = (resource_size_t)USBH1_BASE + 0x5c, | ||
| 143 | .flags = IORESOURCE_MEM, | ||
| 144 | }, | ||
| 145 | [1] = { | ||
| 146 | .start = INT_USBH1, | ||
| 147 | .end = INT_USBH1, | ||
| 148 | .flags = IORESOURCE_IRQ, | ||
| 149 | } | ||
| 150 | }; | ||
| 151 | |||
| 152 | static struct tccohci_platform_data tcc8k_ohci0_platform_data = { | ||
| 153 | .controller = 0, | ||
| 154 | .port_mode = PMM_PERPORT_MODE, | ||
| 155 | .init = tcc8k_ohci_init, | ||
| 156 | }; | ||
| 157 | |||
| 158 | static struct tccohci_platform_data tcc8k_ohci1_platform_data = { | ||
| 159 | .controller = 1, | ||
| 160 | .port_mode = PMM_PERPORT_MODE, | ||
| 161 | .init = tcc8k_ohci_init, | ||
| 162 | }; | ||
| 163 | |||
| 164 | static struct platform_device ohci0_device = { | ||
| 165 | .name = "tcc-ohci", | ||
| 166 | .id = 0, | ||
| 167 | .dev = { | ||
| 168 | .dma_mask = &tcc8k_dmamask, | ||
| 169 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
| 170 | .platform_data = &tcc8k_ohci0_platform_data, | ||
| 171 | }, | ||
| 172 | .num_resources = ARRAY_SIZE(tcc8k_ohci0_resources), | ||
| 173 | .resource = tcc8k_ohci0_resources, | ||
| 174 | }; | ||
| 175 | |||
| 176 | static struct platform_device ohci1_device = { | ||
| 177 | .name = "tcc-ohci", | ||
| 178 | .id = 1, | ||
| 179 | .dev = { | ||
| 180 | .dma_mask = &tcc8k_dmamask, | ||
| 181 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
| 182 | .platform_data = &tcc8k_ohci1_platform_data, | ||
| 183 | }, | ||
| 184 | .num_resources = ARRAY_SIZE(tcc8k_ohci1_resources), | ||
| 185 | .resource = tcc8k_ohci1_resources, | ||
| 186 | }; | ||
| 187 | |||
| 188 | static void __init tcc8k_init_usbhost(void) | ||
| 189 | { | ||
| 190 | platform_device_register(&ohci0_device); | ||
| 191 | platform_device_register(&ohci1_device); | ||
| 192 | } | ||
| 193 | #else | ||
| 194 | static void __init tcc8k_init_usbhost(void) { } | ||
| 195 | #endif | ||
| 196 | |||
| 197 | /* USB device controller*/ | ||
| 198 | #ifdef CONFIG_USB_GADGET_TCC8K | ||
| 199 | static struct resource udc_resources[] = { | ||
| 200 | [0] = { | ||
| 201 | .start = INT_USBD, | ||
| 202 | .end = INT_USBD, | ||
| 203 | .flags = IORESOURCE_IRQ, | ||
| 204 | }, | ||
| 205 | [1] = { | ||
| 206 | .start = INT_UDMA, | ||
| 207 | .end = INT_UDMA, | ||
| 208 | .flags = IORESOURCE_IRQ, | ||
| 209 | }, | ||
| 210 | }; | ||
| 211 | |||
| 212 | static struct platform_device tcc8k_udc_device = { | ||
| 213 | .name = "tcc-udc", | ||
| 214 | .id = 0, | ||
| 215 | .resource = udc_resources, | ||
| 216 | .num_resources = ARRAY_SIZE(udc_resources), | ||
| 217 | .dev = { | ||
| 218 | .dma_mask = &tcc8k_dmamask, | ||
| 219 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
| 220 | }, | ||
| 221 | }; | ||
| 222 | |||
| 223 | static void __init tcc8k_init_usb_gadget(void) | ||
| 224 | { | ||
| 225 | platform_device_register(&tcc8k_udc_device); | ||
| 226 | } | ||
| 227 | #else | ||
| 228 | static void __init tcc8k_init_usb_gadget(void) { } | ||
| 229 | #endif /* CONFIG_USB_GADGET_TCC83X */ | ||
| 230 | |||
| 231 | static int __init tcc8k_init_devices(void) | ||
| 232 | { | ||
| 233 | tcc8k_init_mmc(); | ||
| 234 | tcc8k_init_usbhost(); | ||
| 235 | tcc8k_init_usb_gadget(); | ||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | arch_initcall(tcc8k_init_devices); | ||
diff --git a/arch/arm/mach-tcc8k/io.c b/arch/arm/mach-tcc8k/io.c new file mode 100644 index 00000000000..9b39d7fa658 --- /dev/null +++ b/arch/arm/mach-tcc8k/io.c | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/mach-tcc8k/io.c | ||
| 3 | * | ||
| 4 | * (C) 2009 Hans J. Koch <hjk@linutronix.de> | ||
| 5 | * | ||
| 6 | * derived from TCC83xx io.c | ||
| 7 | * Copyright (C) Telechips, Inc. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/init.h> | ||
| 15 | #include <linux/io.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | |||
| 18 | #include <asm/mach/map.h> | ||
| 19 | |||
| 20 | #include <mach/tcc8k-regs.h> | ||
| 21 | |||
| 22 | /* | ||
| 23 | * The machine specific code may provide the extra mapping besides the | ||
| 24 | * default mapping provided here. | ||
| 25 | */ | ||
| 26 | static struct map_desc tcc8k_io_desc[] __initdata = { | ||
| 27 | { | ||
| 28 | .virtual = (unsigned long)CS1_BASE_VIRT, | ||
| 29 | .pfn = __phys_to_pfn(CS1_BASE), | ||
| 30 | .length = CS1_SIZE, | ||
| 31 | .type = MT_DEVICE, | ||
| 32 | }, { | ||
| 33 | .virtual = (unsigned long)AHB_PERI_BASE_VIRT, | ||
| 34 | .pfn = __phys_to_pfn(AHB_PERI_BASE), | ||
| 35 | .length = AHB_PERI_SIZE, | ||
| 36 | .type = MT_DEVICE, | ||
| 37 | }, { | ||
| 38 | .virtual = (unsigned long)APB0_PERI_BASE_VIRT, | ||
| 39 | .pfn = __phys_to_pfn(APB0_PERI_BASE), | ||
| 40 | .length = APB0_PERI_SIZE, | ||
| 41 | .type = MT_DEVICE, | ||
| 42 | }, { | ||
| 43 | .virtual = (unsigned long)APB1_PERI_BASE_VIRT, | ||
| 44 | .pfn = __phys_to_pfn(APB1_PERI_BASE), | ||
| 45 | .length = APB1_PERI_SIZE, | ||
| 46 | .type = MT_DEVICE, | ||
| 47 | }, { | ||
| 48 | .virtual = (unsigned long)EXT_MEM_CTRL_BASE_VIRT, | ||
| 49 | .pfn = __phys_to_pfn(EXT_MEM_CTRL_BASE), | ||
| 50 | .length = EXT_MEM_CTRL_SIZE, | ||
| 51 | .type = MT_DEVICE, | ||
| 52 | }, | ||
| 53 | }; | ||
| 54 | |||
| 55 | /* | ||
| 56 | * Maps common IO regions for tcc8k. | ||
| 57 | * | ||
| 58 | */ | ||
| 59 | void __init tcc8k_map_common_io(void) | ||
| 60 | { | ||
| 61 | iotable_init(tcc8k_io_desc, ARRAY_SIZE(tcc8k_io_desc)); | ||
| 62 | } | ||
diff --git a/arch/arm/mach-tcc8k/irq.c b/arch/arm/mach-tcc8k/irq.c new file mode 100644 index 00000000000..209fa5c65d4 --- /dev/null +++ b/arch/arm/mach-tcc8k/irq.c | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) Telechips, Inc. | ||
| 3 | * Copyright (C) 2009-2010 Hans J. Koch <hjk@linutronix.de> | ||
| 4 | * | ||
| 5 | * Licensed under the terms of the GNU GPL version 2. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/init.h> | ||
| 9 | #include <linux/interrupt.h> | ||
| 10 | #include <linux/io.h> | ||
| 11 | |||
| 12 | #include <asm/irq.h> | ||
| 13 | #include <asm/mach/irq.h> | ||
| 14 | |||
| 15 | #include <mach/tcc8k-regs.h> | ||
| 16 | #include <mach/irqs.h> | ||
| 17 | |||
| 18 | #include "common.h" | ||
| 19 | |||
| 20 | /* Disable IRQ */ | ||
| 21 | static void tcc8000_mask_ack_irq0(struct irq_data *d) | ||
| 22 | { | ||
| 23 | PIC0_IEN &= ~(1 << d->irq); | ||
| 24 | PIC0_CREQ |= (1 << d->irq); | ||
| 25 | } | ||
| 26 | |||
| 27 | static void tcc8000_mask_ack_irq1(struct irq_data *d) | ||
| 28 | { | ||
| 29 | PIC1_IEN &= ~(1 << (d->irq - 32)); | ||
| 30 | PIC1_CREQ |= (1 << (d->irq - 32)); | ||
| 31 | } | ||
| 32 | |||
| 33 | static void tcc8000_mask_irq0(struct irq_data *d) | ||
| 34 | { | ||
| 35 | PIC0_IEN &= ~(1 << d->irq); | ||
| 36 | } | ||
| 37 | |||
| 38 | static void tcc8000_mask_irq1(struct irq_data *d) | ||
| 39 | { | ||
| 40 | PIC1_IEN &= ~(1 << (d->irq - 32)); | ||
| 41 | } | ||
| 42 | |||
| 43 | static void tcc8000_ack_irq0(struct irq_data *d) | ||
| 44 | { | ||
| 45 | PIC0_CREQ |= (1 << d->irq); | ||
| 46 | } | ||
| 47 | |||
| 48 | static void tcc8000_ack_irq1(struct irq_data *d) | ||
| 49 | { | ||
| 50 | PIC1_CREQ |= (1 << (d->irq - 32)); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Enable IRQ */ | ||
| 54 | static void tcc8000_unmask_irq0(struct irq_data *d) | ||
| 55 | { | ||
| 56 | PIC0_IEN |= (1 << d->irq); | ||
| 57 | PIC0_INTOEN |= (1 << d->irq); | ||
| 58 | } | ||
| 59 | |||
| 60 | static void tcc8000_unmask_irq1(struct irq_data *d) | ||
| 61 | { | ||
| 62 | PIC1_IEN |= (1 << (d->irq - 32)); | ||
| 63 | PIC1_INTOEN |= (1 << (d->irq - 32)); | ||
| 64 | } | ||
| 65 | |||
| 66 | static struct irq_chip tcc8000_irq_chip0 = { | ||
| 67 | .name = "tcc_irq0", | ||
| 68 | .irq_mask = tcc8000_mask_irq0, | ||
| 69 | .irq_ack = tcc8000_ack_irq0, | ||
| 70 | .irq_mask_ack = tcc8000_mask_ack_irq0, | ||
| 71 | .irq_unmask = tcc8000_unmask_irq0, | ||
| 72 | }; | ||
| 73 | |||
| 74 | static struct irq_chip tcc8000_irq_chip1 = { | ||
| 75 | .name = "tcc_irq1", | ||
| 76 | .irq_mask = tcc8000_mask_irq1, | ||
| 77 | .irq_ack = tcc8000_ack_irq1, | ||
| 78 | .irq_mask_ack = tcc8000_mask_ack_irq1, | ||
| 79 | .irq_unmask = tcc8000_unmask_irq1, | ||
| 80 | }; | ||
| 81 | |||
| 82 | void __init tcc8k_init_irq(void) | ||
| 83 | { | ||
| 84 | int irqno; | ||
| 85 | |||
| 86 | /* Mask and clear all interrupts */ | ||
| 87 | PIC0_IEN = 0x00000000; | ||
| 88 | PIC0_CREQ = 0xffffffff; | ||
| 89 | PIC1_IEN = 0x00000000; | ||
| 90 | PIC1_CREQ = 0xffffffff; | ||
| 91 | |||
| 92 | PIC0_MEN0 = 0x00000003; | ||
| 93 | PIC1_MEN1 = 0x00000003; | ||
| 94 | PIC1_MEN = 0x00000003; | ||
| 95 | |||
| 96 | /* let all IRQs be level triggered */ | ||
| 97 | PIC0_TMODE = 0xffffffff; | ||
| 98 | PIC1_TMODE = 0xffffffff; | ||
| 99 | /* all IRQs are IRQs (not FIQs) */ | ||
| 100 | PIC0_IRQSEL = 0xffffffff; | ||
| 101 | PIC1_IRQSEL = 0xffffffff; | ||
| 102 | |||
| 103 | for (irqno = 0; irqno < NR_IRQS; irqno++) { | ||
| 104 | if (irqno < 32) | ||
| 105 | irq_set_chip(irqno, &tcc8000_irq_chip0); | ||
| 106 | else | ||
| 107 | irq_set_chip(irqno, &tcc8000_irq_chip1); | ||
| 108 | irq_set_handler(irqno, handle_level_irq); | ||
| 109 | set_irq_flags(irqno, IRQF_VALID); | ||
| 110 | } | ||
| 111 | } | ||
diff --git a/arch/arm/mach-tcc8k/time.c b/arch/arm/mach-tcc8k/time.c new file mode 100644 index 00000000000..a96babe8377 --- /dev/null +++ b/arch/arm/mach-tcc8k/time.c | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* | ||
| 2 | * TCC8000 system timer setup | ||
| 3 | * | ||
| 4 | * (C) 2009 Hans J. Koch <hjk@linutronix.de> | ||
| 5 | * | ||
| 6 | * Licensed under the terms of the GPL version 2. | ||
| 7 | * | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/clk.h> | ||
| 11 | #include <linux/clockchips.h> | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include <linux/io.h> | ||
| 15 | #include <linux/irq.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/spinlock.h> | ||
| 18 | |||
| 19 | #include <asm/mach/time.h> | ||
| 20 | |||
| 21 | #include <mach/tcc8k-regs.h> | ||
| 22 | #include <mach/irqs.h> | ||
| 23 | |||
| 24 | #include "common.h" | ||
| 25 | |||
| 26 | static void __iomem *timer_base; | ||
| 27 | |||
| 28 | static int tcc_set_next_event(unsigned long evt, | ||
| 29 | struct clock_event_device *unused) | ||
| 30 | { | ||
| 31 | unsigned long reg = __raw_readl(timer_base + TC32MCNT_OFFS); | ||
| 32 | |||
| 33 | __raw_writel(reg + evt, timer_base + TC32CMP0_OFFS); | ||
| 34 | return 0; | ||
| 35 | } | ||
| 36 | |||
| 37 | static void tcc_set_mode(enum clock_event_mode mode, | ||
| 38 | struct clock_event_device *evt) | ||
| 39 | { | ||
| 40 | unsigned long tc32irq; | ||
| 41 | |||
| 42 | switch (mode) { | ||
| 43 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 44 | tc32irq = __raw_readl(timer_base + TC32IRQ_OFFS); | ||
| 45 | tc32irq |= TC32IRQ_IRQEN0; | ||
| 46 | __raw_writel(tc32irq, timer_base + TC32IRQ_OFFS); | ||
| 47 | break; | ||
| 48 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 49 | case CLOCK_EVT_MODE_UNUSED: | ||
| 50 | tc32irq = __raw_readl(timer_base + TC32IRQ_OFFS); | ||
| 51 | tc32irq &= ~TC32IRQ_IRQEN0; | ||
| 52 | __raw_writel(tc32irq, timer_base + TC32IRQ_OFFS); | ||
| 53 | break; | ||
| 54 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 55 | case CLOCK_EVT_MODE_RESUME: | ||
| 56 | break; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | static irqreturn_t tcc8k_timer_interrupt(int irq, void *dev_id) | ||
| 61 | { | ||
| 62 | struct clock_event_device *evt = dev_id; | ||
| 63 | |||
| 64 | /* Acknowledge TC32 interrupt by reading TC32IRQ */ | ||
| 65 | __raw_readl(timer_base + TC32IRQ_OFFS); | ||
| 66 | |||
| 67 | evt->event_handler(evt); | ||
| 68 | |||
| 69 | return IRQ_HANDLED; | ||
| 70 | } | ||
| 71 | |||
| 72 | static struct clock_event_device clockevent_tcc = { | ||
| 73 | .name = "tcc_timer1", | ||
| 74 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
| 75 | .shift = 32, | ||
| 76 | .set_mode = tcc_set_mode, | ||
| 77 | .set_next_event = tcc_set_next_event, | ||
| 78 | .rating = 200, | ||
| 79 | }; | ||
| 80 | |||
| 81 | static struct irqaction tcc8k_timer_irq = { | ||
| 82 | .name = "TC32_timer", | ||
| 83 | .flags = IRQF_DISABLED | IRQF_TIMER, | ||
| 84 | .handler = tcc8k_timer_interrupt, | ||
| 85 | .dev_id = &clockevent_tcc, | ||
| 86 | }; | ||
| 87 | |||
| 88 | static int __init tcc_clockevent_init(struct clk *clock) | ||
| 89 | { | ||
| 90 | unsigned int c = clk_get_rate(clock); | ||
| 91 | |||
| 92 | clocksource_mmio_init(timer_base + TC32MCNT_OFFS, "tcc_tc32", c, | ||
| 93 | 200, 32, clocksource_mmio_readl_up); | ||
| 94 | |||
| 95 | clockevent_tcc.mult = div_sc(c, NSEC_PER_SEC, | ||
| 96 | clockevent_tcc.shift); | ||
| 97 | clockevent_tcc.max_delta_ns = | ||
| 98 | clockevent_delta2ns(0xfffffffe, &clockevent_tcc); | ||
| 99 | clockevent_tcc.min_delta_ns = | ||
| 100 | clockevent_delta2ns(0xff, &clockevent_tcc); | ||
| 101 | |||
| 102 | clockevent_tcc.cpumask = cpumask_of(0); | ||
| 103 | |||
| 104 | clockevents_register_device(&clockevent_tcc); | ||
| 105 | |||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | void __init tcc8k_timer_init(struct clk *clock, void __iomem *base, int irq) | ||
| 110 | { | ||
| 111 | u32 reg; | ||
| 112 | |||
| 113 | timer_base = base; | ||
| 114 | tcc8k_timer_irq.irq = irq; | ||
| 115 | |||
| 116 | /* Enable clocks */ | ||
| 117 | clk_enable(clock); | ||
| 118 | |||
| 119 | /* Initialize 32-bit timer */ | ||
| 120 | reg = __raw_readl(timer_base + TC32EN_OFFS); | ||
| 121 | reg &= ~TC32EN_ENABLE; /* Disable timer */ | ||
| 122 | __raw_writel(reg, timer_base + TC32EN_OFFS); | ||
| 123 | /* Free running timer, counting from 0 to 0xffffffff */ | ||
| 124 | __raw_writel(0, timer_base + TC32EN_OFFS); | ||
| 125 | __raw_writel(0, timer_base + TC32LDV_OFFS); | ||
| 126 | reg = __raw_readl(timer_base + TC32IRQ_OFFS); | ||
| 127 | reg |= TC32IRQ_IRQEN0; /* irq at match with CMP0 */ | ||
| 128 | __raw_writel(reg, timer_base + TC32IRQ_OFFS); | ||
| 129 | |||
| 130 | __raw_writel(TC32EN_ENABLE, timer_base + TC32EN_OFFS); | ||
| 131 | |||
| 132 | tcc_clockevent_init(clock); | ||
| 133 | setup_irq(irq, &tcc8k_timer_irq); | ||
| 134 | } | ||
