| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 1 | /* linux/arch/arm/plat-s3c64xx/clock.c | 
|  | 2 | * | 
|  | 3 | * Copyright 2008 Openmoko, Inc. | 
|  | 4 | * Copyright 2008 Simtec Electronics | 
|  | 5 | *	Ben Dooks <ben@simtec.co.uk> | 
|  | 6 | *	http://armlinux.simtec.co.uk/ | 
|  | 7 | * | 
|  | 8 | * S3C64XX Base clock support | 
|  | 9 | * | 
|  | 10 | * This program is free software; you can redistribute it and/or modify | 
|  | 11 | * it under the terms of the GNU General Public License version 2 as | 
|  | 12 | * published by the Free Software Foundation. | 
|  | 13 | */ | 
|  | 14 |  | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/module.h> | 
|  | 17 | #include <linux/interrupt.h> | 
|  | 18 | #include <linux/ioport.h> | 
| Ben Dooks | 62acb2f | 2010-01-26 14:53:19 +0900 | [diff] [blame] | 19 | #include <linux/clk.h> | 
|  | 20 | #include <linux/err.h> | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 21 | #include <linux/io.h> | 
|  | 22 |  | 
|  | 23 | #include <mach/hardware.h> | 
|  | 24 | #include <mach/map.h> | 
|  | 25 |  | 
| Ben Dooks | 3501c9a | 2010-01-26 10:45:40 +0900 | [diff] [blame] | 26 | #include <mach/regs-sys.h> | 
|  | 27 | #include <mach/regs-clock.h> | 
| Ben Dooks | f7be9ab | 2010-01-26 13:41:30 +0900 | [diff] [blame] | 28 | #include <mach/pll.h> | 
|  | 29 |  | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 30 | #include <plat/cpu.h> | 
|  | 31 | #include <plat/devs.h> | 
| Ben Dooks | 62acb2f | 2010-01-26 14:53:19 +0900 | [diff] [blame] | 32 | #include <plat/cpu-freq.h> | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 33 | #include <plat/clock.h> | 
| Ben Dooks | 62acb2f | 2010-01-26 14:53:19 +0900 | [diff] [blame] | 34 | #include <plat/clock-clksrc.h> | 
|  | 35 |  | 
|  | 36 | /* fin_apll, fin_mpll and fin_epll are all the same clock, which we call | 
|  | 37 | * ext_xtal_mux for want of an actual name from the manual. | 
|  | 38 | */ | 
|  | 39 |  | 
|  | 40 | static struct clk clk_ext_xtal_mux = { | 
|  | 41 | .name		= "ext_xtal", | 
|  | 42 | .id		= -1, | 
|  | 43 | }; | 
|  | 44 |  | 
|  | 45 | #define clk_fin_apll clk_ext_xtal_mux | 
|  | 46 | #define clk_fin_mpll clk_ext_xtal_mux | 
|  | 47 | #define clk_fin_epll clk_ext_xtal_mux | 
|  | 48 |  | 
|  | 49 | #define clk_fout_mpll	clk_mpll | 
|  | 50 | #define clk_fout_epll	clk_epll | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 51 |  | 
| Werner Almesberger | a03f7da | 2009-03-05 11:43:13 +0800 | [diff] [blame] | 52 | struct clk clk_h2 = { | 
|  | 53 | .name		= "hclk2", | 
|  | 54 | .id		= -1, | 
|  | 55 | .rate		= 0, | 
|  | 56 | }; | 
|  | 57 |  | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 58 | struct clk clk_27m = { | 
|  | 59 | .name		= "clk_27m", | 
|  | 60 | .id		= -1, | 
|  | 61 | .rate		= 27000000, | 
|  | 62 | }; | 
|  | 63 |  | 
| Ben Dooks | 3627379f | 2008-10-31 16:14:36 +0000 | [diff] [blame] | 64 | static int clk_48m_ctrl(struct clk *clk, int enable) | 
|  | 65 | { | 
|  | 66 | unsigned long flags; | 
|  | 67 | u32 val; | 
|  | 68 |  | 
|  | 69 | /* can't rely on clock lock, this register has other usages */ | 
|  | 70 | local_irq_save(flags); | 
|  | 71 |  | 
|  | 72 | val = __raw_readl(S3C64XX_OTHERS); | 
|  | 73 | if (enable) | 
|  | 74 | val |= S3C64XX_OTHERS_USBMASK; | 
|  | 75 | else | 
|  | 76 | val &= ~S3C64XX_OTHERS_USBMASK; | 
|  | 77 |  | 
|  | 78 | __raw_writel(val, S3C64XX_OTHERS); | 
|  | 79 | local_irq_restore(flags); | 
|  | 80 |  | 
|  | 81 | return 0; | 
|  | 82 | } | 
|  | 83 |  | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 84 | struct clk clk_48m = { | 
|  | 85 | .name		= "clk_48m", | 
|  | 86 | .id		= -1, | 
|  | 87 | .rate		= 48000000, | 
| Ben Dooks | 3627379f | 2008-10-31 16:14:36 +0000 | [diff] [blame] | 88 | .enable		= clk_48m_ctrl, | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 89 | }; | 
|  | 90 |  | 
|  | 91 | static int inline s3c64xx_gate(void __iomem *reg, | 
|  | 92 | struct clk *clk, | 
|  | 93 | int enable) | 
|  | 94 | { | 
|  | 95 | unsigned int ctrlbit = clk->ctrlbit; | 
|  | 96 | u32 con; | 
|  | 97 |  | 
|  | 98 | con = __raw_readl(reg); | 
|  | 99 |  | 
|  | 100 | if (enable) | 
|  | 101 | con |= ctrlbit; | 
|  | 102 | else | 
|  | 103 | con &= ~ctrlbit; | 
|  | 104 |  | 
|  | 105 | __raw_writel(con, reg); | 
|  | 106 | return 0; | 
|  | 107 | } | 
|  | 108 |  | 
|  | 109 | static int s3c64xx_pclk_ctrl(struct clk *clk, int enable) | 
|  | 110 | { | 
|  | 111 | return s3c64xx_gate(S3C_PCLK_GATE, clk, enable); | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | static int s3c64xx_hclk_ctrl(struct clk *clk, int enable) | 
|  | 115 | { | 
|  | 116 | return s3c64xx_gate(S3C_HCLK_GATE, clk, enable); | 
|  | 117 | } | 
|  | 118 |  | 
| Ben Dooks | cf18acf | 2008-10-21 14:07:02 +0100 | [diff] [blame] | 119 | int s3c64xx_sclk_ctrl(struct clk *clk, int enable) | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 120 | { | 
|  | 121 | return s3c64xx_gate(S3C_SCLK_GATE, clk, enable); | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | static struct clk init_clocks_disable[] = { | 
|  | 125 | { | 
|  | 126 | .name		= "nand", | 
|  | 127 | .id		= -1, | 
|  | 128 | .parent		= &clk_h, | 
|  | 129 | }, { | 
|  | 130 | .name		= "adc", | 
|  | 131 | .id		= -1, | 
|  | 132 | .parent		= &clk_p, | 
|  | 133 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 134 | .ctrlbit	= S3C_CLKCON_PCLK_TSADC, | 
|  | 135 | }, { | 
|  | 136 | .name		= "i2c", | 
|  | 137 | .id		= -1, | 
|  | 138 | .parent		= &clk_p, | 
|  | 139 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 140 | .ctrlbit	= S3C_CLKCON_PCLK_IIC, | 
|  | 141 | }, { | 
|  | 142 | .name		= "iis", | 
|  | 143 | .id		= 0, | 
|  | 144 | .parent		= &clk_p, | 
|  | 145 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 146 | .ctrlbit	= S3C_CLKCON_PCLK_IIS0, | 
|  | 147 | }, { | 
|  | 148 | .name		= "iis", | 
|  | 149 | .id		= 1, | 
|  | 150 | .parent		= &clk_p, | 
|  | 151 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 152 | .ctrlbit	= S3C_CLKCON_PCLK_IIS1, | 
|  | 153 | }, { | 
| Jassi Brar | 2e5070b | 2010-02-17 19:03:19 +0000 | [diff] [blame] | 154 | #ifdef CONFIG_CPU_S3C6410 | 
|  | 155 | .name		= "iis", | 
|  | 156 | .id		= -1,  /* There's only one IISv4 port */ | 
|  | 157 | .parent		= &clk_p, | 
|  | 158 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 159 | .ctrlbit	= S3C6410_CLKCON_PCLK_IIS2, | 
|  | 160 | }, { | 
|  | 161 | #endif | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 162 | .name		= "spi", | 
|  | 163 | .id		= 0, | 
|  | 164 | .parent		= &clk_p, | 
|  | 165 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 166 | .ctrlbit	= S3C_CLKCON_PCLK_SPI0, | 
|  | 167 | }, { | 
|  | 168 | .name		= "spi", | 
|  | 169 | .id		= 1, | 
|  | 170 | .parent		= &clk_p, | 
|  | 171 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 172 | .ctrlbit	= S3C_CLKCON_PCLK_SPI1, | 
|  | 173 | }, { | 
| Jassi Brar | 87315a8 | 2010-01-18 16:15:08 +0900 | [diff] [blame] | 174 | .name		= "spi_48m", | 
|  | 175 | .id		= 0, | 
|  | 176 | .parent		= &clk_48m, | 
|  | 177 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 178 | .ctrlbit	= S3C_CLKCON_SCLK_SPI0_48, | 
|  | 179 | }, { | 
|  | 180 | .name		= "spi_48m", | 
|  | 181 | .id		= 1, | 
|  | 182 | .parent		= &clk_48m, | 
|  | 183 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 184 | .ctrlbit	= S3C_CLKCON_SCLK_SPI1_48, | 
|  | 185 | }, { | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 186 | .name		= "48m", | 
|  | 187 | .id		= 0, | 
|  | 188 | .parent		= &clk_48m, | 
|  | 189 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 190 | .ctrlbit	= S3C_CLKCON_SCLK_MMC0_48, | 
|  | 191 | }, { | 
|  | 192 | .name		= "48m", | 
|  | 193 | .id		= 1, | 
|  | 194 | .parent		= &clk_48m, | 
|  | 195 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 196 | .ctrlbit	= S3C_CLKCON_SCLK_MMC1_48, | 
|  | 197 | }, { | 
|  | 198 | .name		= "48m", | 
|  | 199 | .id		= 2, | 
|  | 200 | .parent		= &clk_48m, | 
|  | 201 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 202 | .ctrlbit	= S3C_CLKCON_SCLK_MMC2_48, | 
| Mark Brown | 8f1ecf1 | 2009-04-28 16:06:24 +0100 | [diff] [blame] | 203 | }, { | 
|  | 204 | .name		= "dma0", | 
|  | 205 | .id		= -1, | 
|  | 206 | .parent		= &clk_h, | 
|  | 207 | .enable		= s3c64xx_hclk_ctrl, | 
|  | 208 | .ctrlbit	= S3C_CLKCON_HCLK_DMA0, | 
|  | 209 | }, { | 
|  | 210 | .name		= "dma1", | 
|  | 211 | .id		= -1, | 
|  | 212 | .parent		= &clk_h, | 
|  | 213 | .enable		= s3c64xx_hclk_ctrl, | 
|  | 214 | .ctrlbit	= S3C_CLKCON_HCLK_DMA1, | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 215 | }, | 
|  | 216 | }; | 
|  | 217 |  | 
|  | 218 | static struct clk init_clocks[] = { | 
|  | 219 | { | 
|  | 220 | .name		= "lcd", | 
|  | 221 | .id		= -1, | 
|  | 222 | .parent		= &clk_h, | 
|  | 223 | .enable		= s3c64xx_hclk_ctrl, | 
|  | 224 | .ctrlbit	= S3C_CLKCON_HCLK_LCD, | 
|  | 225 | }, { | 
|  | 226 | .name		= "gpio", | 
|  | 227 | .id		= -1, | 
|  | 228 | .parent		= &clk_p, | 
|  | 229 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 230 | .ctrlbit	= S3C_CLKCON_PCLK_GPIO, | 
|  | 231 | }, { | 
|  | 232 | .name		= "usb-host", | 
|  | 233 | .id		= -1, | 
|  | 234 | .parent		= &clk_h, | 
|  | 235 | .enable		= s3c64xx_hclk_ctrl, | 
| Peter Korsgaard | 386f435 | 2009-06-18 23:54:44 +0200 | [diff] [blame] | 236 | .ctrlbit	= S3C_CLKCON_HCLK_UHOST, | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 237 | }, { | 
|  | 238 | .name		= "hsmmc", | 
|  | 239 | .id		= 0, | 
|  | 240 | .parent		= &clk_h, | 
|  | 241 | .enable		= s3c64xx_hclk_ctrl, | 
|  | 242 | .ctrlbit	= S3C_CLKCON_HCLK_HSMMC0, | 
|  | 243 | }, { | 
|  | 244 | .name		= "hsmmc", | 
|  | 245 | .id		= 1, | 
|  | 246 | .parent		= &clk_h, | 
|  | 247 | .enable		= s3c64xx_hclk_ctrl, | 
|  | 248 | .ctrlbit	= S3C_CLKCON_HCLK_HSMMC1, | 
|  | 249 | }, { | 
|  | 250 | .name		= "hsmmc", | 
|  | 251 | .id		= 2, | 
|  | 252 | .parent		= &clk_h, | 
|  | 253 | .enable		= s3c64xx_hclk_ctrl, | 
|  | 254 | .ctrlbit	= S3C_CLKCON_HCLK_HSMMC2, | 
|  | 255 | }, { | 
|  | 256 | .name		= "timers", | 
|  | 257 | .id		= -1, | 
|  | 258 | .parent		= &clk_p, | 
|  | 259 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 260 | .ctrlbit	= S3C_CLKCON_PCLK_PWM, | 
|  | 261 | }, { | 
|  | 262 | .name		= "uart", | 
|  | 263 | .id		= 0, | 
|  | 264 | .parent		= &clk_p, | 
|  | 265 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 266 | .ctrlbit	= S3C_CLKCON_PCLK_UART0, | 
|  | 267 | }, { | 
|  | 268 | .name		= "uart", | 
|  | 269 | .id		= 1, | 
|  | 270 | .parent		= &clk_p, | 
|  | 271 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 272 | .ctrlbit	= S3C_CLKCON_PCLK_UART1, | 
|  | 273 | }, { | 
|  | 274 | .name		= "uart", | 
|  | 275 | .id		= 2, | 
|  | 276 | .parent		= &clk_p, | 
|  | 277 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 278 | .ctrlbit	= S3C_CLKCON_PCLK_UART2, | 
|  | 279 | }, { | 
|  | 280 | .name		= "uart", | 
|  | 281 | .id		= 3, | 
|  | 282 | .parent		= &clk_p, | 
|  | 283 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 284 | .ctrlbit	= S3C_CLKCON_PCLK_UART3, | 
|  | 285 | }, { | 
|  | 286 | .name		= "rtc", | 
|  | 287 | .id		= -1, | 
|  | 288 | .parent		= &clk_p, | 
|  | 289 | .enable		= s3c64xx_pclk_ctrl, | 
|  | 290 | .ctrlbit	= S3C_CLKCON_PCLK_RTC, | 
|  | 291 | }, { | 
|  | 292 | .name		= "watchdog", | 
|  | 293 | .id		= -1, | 
|  | 294 | .parent		= &clk_p, | 
|  | 295 | .ctrlbit	= S3C_CLKCON_PCLK_WDT, | 
|  | 296 | }, { | 
|  | 297 | .name		= "ac97", | 
|  | 298 | .id		= -1, | 
|  | 299 | .parent		= &clk_p, | 
|  | 300 | .ctrlbit	= S3C_CLKCON_PCLK_AC97, | 
|  | 301 | } | 
|  | 302 | }; | 
|  | 303 |  | 
| Ben Dooks | 62acb2f | 2010-01-26 14:53:19 +0900 | [diff] [blame] | 304 |  | 
|  | 305 | static struct clk clk_fout_apll = { | 
|  | 306 | .name		= "fout_apll", | 
|  | 307 | .id		= -1, | 
|  | 308 | }; | 
|  | 309 |  | 
|  | 310 | static struct clk *clk_src_apll_list[] = { | 
|  | 311 | [0] = &clk_fin_apll, | 
|  | 312 | [1] = &clk_fout_apll, | 
|  | 313 | }; | 
|  | 314 |  | 
|  | 315 | static struct clksrc_sources clk_src_apll = { | 
|  | 316 | .sources	= clk_src_apll_list, | 
|  | 317 | .nr_sources	= ARRAY_SIZE(clk_src_apll_list), | 
|  | 318 | }; | 
|  | 319 |  | 
|  | 320 | static struct clksrc_clk clk_mout_apll = { | 
|  | 321 | .clk	= { | 
|  | 322 | .name		= "mout_apll", | 
|  | 323 | .id		= -1, | 
|  | 324 | }, | 
|  | 325 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 0, .size = 1  }, | 
|  | 326 | .sources	= &clk_src_apll, | 
|  | 327 | }; | 
|  | 328 |  | 
|  | 329 | static struct clk *clk_src_epll_list[] = { | 
|  | 330 | [0] = &clk_fin_epll, | 
|  | 331 | [1] = &clk_fout_epll, | 
|  | 332 | }; | 
|  | 333 |  | 
|  | 334 | static struct clksrc_sources clk_src_epll = { | 
|  | 335 | .sources	= clk_src_epll_list, | 
|  | 336 | .nr_sources	= ARRAY_SIZE(clk_src_epll_list), | 
|  | 337 | }; | 
|  | 338 |  | 
|  | 339 | static struct clksrc_clk clk_mout_epll = { | 
|  | 340 | .clk	= { | 
|  | 341 | .name		= "mout_epll", | 
|  | 342 | .id		= -1, | 
|  | 343 | }, | 
|  | 344 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 2, .size = 1  }, | 
|  | 345 | .sources	= &clk_src_epll, | 
|  | 346 | }; | 
|  | 347 |  | 
|  | 348 | static struct clk *clk_src_mpll_list[] = { | 
|  | 349 | [0] = &clk_fin_mpll, | 
|  | 350 | [1] = &clk_fout_mpll, | 
|  | 351 | }; | 
|  | 352 |  | 
|  | 353 | static struct clksrc_sources clk_src_mpll = { | 
|  | 354 | .sources	= clk_src_mpll_list, | 
|  | 355 | .nr_sources	= ARRAY_SIZE(clk_src_mpll_list), | 
|  | 356 | }; | 
|  | 357 |  | 
|  | 358 | static struct clksrc_clk clk_mout_mpll = { | 
|  | 359 | .clk = { | 
|  | 360 | .name		= "mout_mpll", | 
|  | 361 | .id		= -1, | 
|  | 362 | }, | 
|  | 363 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 1, .size = 1  }, | 
|  | 364 | .sources	= &clk_src_mpll, | 
|  | 365 | }; | 
|  | 366 |  | 
|  | 367 | static unsigned int armclk_mask; | 
|  | 368 |  | 
|  | 369 | static unsigned long s3c64xx_clk_arm_get_rate(struct clk *clk) | 
|  | 370 | { | 
|  | 371 | unsigned long rate = clk_get_rate(clk->parent); | 
|  | 372 | u32 clkdiv; | 
|  | 373 |  | 
|  | 374 | /* divisor mask starts at bit0, so no need to shift */ | 
|  | 375 | clkdiv = __raw_readl(S3C_CLK_DIV0) & armclk_mask; | 
|  | 376 |  | 
|  | 377 | return rate / (clkdiv + 1); | 
|  | 378 | } | 
|  | 379 |  | 
|  | 380 | static unsigned long s3c64xx_clk_arm_round_rate(struct clk *clk, | 
|  | 381 | unsigned long rate) | 
|  | 382 | { | 
|  | 383 | unsigned long parent = clk_get_rate(clk->parent); | 
|  | 384 | u32 div; | 
|  | 385 |  | 
|  | 386 | if (parent < rate) | 
|  | 387 | return parent; | 
|  | 388 |  | 
|  | 389 | div = (parent / rate) - 1; | 
|  | 390 | if (div > armclk_mask) | 
|  | 391 | div = armclk_mask; | 
|  | 392 |  | 
|  | 393 | return parent / (div + 1); | 
|  | 394 | } | 
|  | 395 |  | 
|  | 396 | static int s3c64xx_clk_arm_set_rate(struct clk *clk, unsigned long rate) | 
|  | 397 | { | 
|  | 398 | unsigned long parent = clk_get_rate(clk->parent); | 
|  | 399 | u32 div; | 
|  | 400 | u32 val; | 
|  | 401 |  | 
|  | 402 | if (rate < parent / (armclk_mask + 1)) | 
|  | 403 | return -EINVAL; | 
|  | 404 |  | 
|  | 405 | rate = clk_round_rate(clk, rate); | 
|  | 406 | div = clk_get_rate(clk->parent) / rate; | 
|  | 407 |  | 
|  | 408 | val = __raw_readl(S3C_CLK_DIV0); | 
|  | 409 | val &= ~armclk_mask; | 
|  | 410 | val |= (div - 1); | 
|  | 411 | __raw_writel(val, S3C_CLK_DIV0); | 
|  | 412 |  | 
|  | 413 | return 0; | 
|  | 414 |  | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | static struct clk clk_arm = { | 
|  | 418 | .name		= "armclk", | 
|  | 419 | .id		= -1, | 
|  | 420 | .parent		= &clk_mout_apll.clk, | 
|  | 421 | .ops		= &(struct clk_ops) { | 
|  | 422 | .get_rate	= s3c64xx_clk_arm_get_rate, | 
|  | 423 | .set_rate	= s3c64xx_clk_arm_set_rate, | 
|  | 424 | .round_rate	= s3c64xx_clk_arm_round_rate, | 
|  | 425 | }, | 
|  | 426 | }; | 
|  | 427 |  | 
|  | 428 | static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) | 
|  | 429 | { | 
|  | 430 | unsigned long rate = clk_get_rate(clk->parent); | 
|  | 431 |  | 
|  | 432 | printk(KERN_DEBUG "%s: parent is %ld\n", __func__, rate); | 
|  | 433 |  | 
|  | 434 | if (__raw_readl(S3C_CLK_DIV0) & S3C6400_CLKDIV0_MPLL_MASK) | 
|  | 435 | rate /= 2; | 
|  | 436 |  | 
|  | 437 | return rate; | 
|  | 438 | } | 
|  | 439 |  | 
|  | 440 | static struct clk_ops clk_dout_ops = { | 
|  | 441 | .get_rate	= s3c64xx_clk_doutmpll_get_rate, | 
|  | 442 | }; | 
|  | 443 |  | 
|  | 444 | static struct clk clk_dout_mpll = { | 
|  | 445 | .name		= "dout_mpll", | 
|  | 446 | .id		= -1, | 
|  | 447 | .parent		= &clk_mout_mpll.clk, | 
|  | 448 | .ops		= &clk_dout_ops, | 
|  | 449 | }; | 
|  | 450 |  | 
|  | 451 | static struct clk *clkset_spi_mmc_list[] = { | 
|  | 452 | &clk_mout_epll.clk, | 
|  | 453 | &clk_dout_mpll, | 
|  | 454 | &clk_fin_epll, | 
|  | 455 | &clk_27m, | 
|  | 456 | }; | 
|  | 457 |  | 
|  | 458 | static struct clksrc_sources clkset_spi_mmc = { | 
|  | 459 | .sources	= clkset_spi_mmc_list, | 
|  | 460 | .nr_sources	= ARRAY_SIZE(clkset_spi_mmc_list), | 
|  | 461 | }; | 
|  | 462 |  | 
|  | 463 | static struct clk *clkset_irda_list[] = { | 
|  | 464 | &clk_mout_epll.clk, | 
|  | 465 | &clk_dout_mpll, | 
|  | 466 | NULL, | 
|  | 467 | &clk_27m, | 
|  | 468 | }; | 
|  | 469 |  | 
|  | 470 | static struct clksrc_sources clkset_irda = { | 
|  | 471 | .sources	= clkset_irda_list, | 
|  | 472 | .nr_sources	= ARRAY_SIZE(clkset_irda_list), | 
|  | 473 | }; | 
|  | 474 |  | 
|  | 475 | static struct clk *clkset_uart_list[] = { | 
|  | 476 | &clk_mout_epll.clk, | 
|  | 477 | &clk_dout_mpll, | 
|  | 478 | NULL, | 
|  | 479 | NULL | 
|  | 480 | }; | 
|  | 481 |  | 
|  | 482 | static struct clksrc_sources clkset_uart = { | 
|  | 483 | .sources	= clkset_uart_list, | 
|  | 484 | .nr_sources	= ARRAY_SIZE(clkset_uart_list), | 
|  | 485 | }; | 
|  | 486 |  | 
|  | 487 | static struct clk *clkset_uhost_list[] = { | 
|  | 488 | &clk_48m, | 
|  | 489 | &clk_mout_epll.clk, | 
|  | 490 | &clk_dout_mpll, | 
|  | 491 | &clk_fin_epll, | 
|  | 492 | }; | 
|  | 493 |  | 
|  | 494 | static struct clksrc_sources clkset_uhost = { | 
|  | 495 | .sources	= clkset_uhost_list, | 
|  | 496 | .nr_sources	= ARRAY_SIZE(clkset_uhost_list), | 
|  | 497 | }; | 
|  | 498 |  | 
|  | 499 | /* The peripheral clocks are all controlled via clocksource followed | 
|  | 500 | * by an optional divider and gate stage. We currently roll this into | 
|  | 501 | * one clock which hides the intermediate clock from the mux. | 
|  | 502 | * | 
|  | 503 | * Note, the JPEG clock can only be an even divider... | 
|  | 504 | * | 
|  | 505 | * The scaler and LCD clocks depend on the S3C64XX version, and also | 
|  | 506 | * have a common parent divisor so are not included here. | 
|  | 507 | */ | 
|  | 508 |  | 
|  | 509 | /* clocks that feed other parts of the clock source tree */ | 
|  | 510 |  | 
|  | 511 | static struct clk clk_iis_cd0 = { | 
|  | 512 | .name		= "iis_cdclk0", | 
|  | 513 | .id		= -1, | 
|  | 514 | }; | 
|  | 515 |  | 
|  | 516 | static struct clk clk_iis_cd1 = { | 
|  | 517 | .name		= "iis_cdclk1", | 
|  | 518 | .id		= -1, | 
|  | 519 | }; | 
|  | 520 |  | 
|  | 521 | static struct clk clk_pcm_cd = { | 
|  | 522 | .name		= "pcm_cdclk", | 
|  | 523 | .id		= -1, | 
|  | 524 | }; | 
|  | 525 |  | 
|  | 526 | static struct clk *clkset_audio0_list[] = { | 
|  | 527 | [0] = &clk_mout_epll.clk, | 
|  | 528 | [1] = &clk_dout_mpll, | 
|  | 529 | [2] = &clk_fin_epll, | 
|  | 530 | [3] = &clk_iis_cd0, | 
|  | 531 | [4] = &clk_pcm_cd, | 
|  | 532 | }; | 
|  | 533 |  | 
|  | 534 | static struct clksrc_sources clkset_audio0 = { | 
|  | 535 | .sources	= clkset_audio0_list, | 
|  | 536 | .nr_sources	= ARRAY_SIZE(clkset_audio0_list), | 
|  | 537 | }; | 
|  | 538 |  | 
|  | 539 | static struct clk *clkset_audio1_list[] = { | 
|  | 540 | [0] = &clk_mout_epll.clk, | 
|  | 541 | [1] = &clk_dout_mpll, | 
|  | 542 | [2] = &clk_fin_epll, | 
|  | 543 | [3] = &clk_iis_cd1, | 
|  | 544 | [4] = &clk_pcm_cd, | 
|  | 545 | }; | 
|  | 546 |  | 
|  | 547 | static struct clksrc_sources clkset_audio1 = { | 
|  | 548 | .sources	= clkset_audio1_list, | 
|  | 549 | .nr_sources	= ARRAY_SIZE(clkset_audio1_list), | 
|  | 550 | }; | 
|  | 551 |  | 
|  | 552 | static struct clk *clkset_camif_list[] = { | 
|  | 553 | &clk_h2, | 
|  | 554 | }; | 
|  | 555 |  | 
|  | 556 | static struct clksrc_sources clkset_camif = { | 
|  | 557 | .sources	= clkset_camif_list, | 
|  | 558 | .nr_sources	= ARRAY_SIZE(clkset_camif_list), | 
|  | 559 | }; | 
|  | 560 |  | 
|  | 561 | static struct clksrc_clk clksrcs[] = { | 
|  | 562 | { | 
|  | 563 | .clk	= { | 
|  | 564 | .name		= "mmc_bus", | 
|  | 565 | .id		= 0, | 
|  | 566 | .ctrlbit        = S3C_CLKCON_SCLK_MMC0, | 
|  | 567 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 568 | }, | 
|  | 569 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 18, .size = 2  }, | 
|  | 570 | .reg_div	= { .reg = S3C_CLK_DIV1, .shift = 0, .size = 4  }, | 
|  | 571 | .sources	= &clkset_spi_mmc, | 
|  | 572 | }, { | 
|  | 573 | .clk	= { | 
|  | 574 | .name		= "mmc_bus", | 
|  | 575 | .id		= 1, | 
|  | 576 | .ctrlbit        = S3C_CLKCON_SCLK_MMC1, | 
|  | 577 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 578 | }, | 
|  | 579 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 20, .size = 2  }, | 
|  | 580 | .reg_div	= { .reg = S3C_CLK_DIV1, .shift = 4, .size = 4  }, | 
|  | 581 | .sources	= &clkset_spi_mmc, | 
|  | 582 | }, { | 
|  | 583 | .clk	= { | 
|  | 584 | .name		= "mmc_bus", | 
|  | 585 | .id		= 2, | 
|  | 586 | .ctrlbit        = S3C_CLKCON_SCLK_MMC2, | 
|  | 587 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 588 | }, | 
|  | 589 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 22, .size = 2  }, | 
|  | 590 | .reg_div 	= { .reg = S3C_CLK_DIV1, .shift = 8, .size = 4  }, | 
|  | 591 | .sources	= &clkset_spi_mmc, | 
|  | 592 | }, { | 
|  | 593 | .clk	= { | 
|  | 594 | .name		= "usb-bus-host", | 
|  | 595 | .id		= -1, | 
|  | 596 | .ctrlbit        = S3C_CLKCON_SCLK_UHOST, | 
|  | 597 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 598 | }, | 
|  | 599 | .reg_src 	= { .reg = S3C_CLK_SRC, .shift = 5, .size = 2  }, | 
|  | 600 | .reg_div	= { .reg = S3C_CLK_DIV1, .shift = 20, .size = 4  }, | 
|  | 601 | .sources	= &clkset_uhost, | 
|  | 602 | }, { | 
|  | 603 | .clk	= { | 
|  | 604 | .name		= "uclk1", | 
|  | 605 | .id		= -1, | 
|  | 606 | .ctrlbit        = S3C_CLKCON_SCLK_UART, | 
|  | 607 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 608 | }, | 
|  | 609 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 13, .size = 1  }, | 
|  | 610 | .reg_div	= { .reg = S3C_CLK_DIV2, .shift = 16, .size = 4  }, | 
|  | 611 | .sources	= &clkset_uart, | 
|  | 612 | }, { | 
|  | 613 | /* Where does UCLK0 come from? */ | 
|  | 614 | .clk	= { | 
|  | 615 | .name		= "spi-bus", | 
|  | 616 | .id		= 0, | 
|  | 617 | .ctrlbit        = S3C_CLKCON_SCLK_SPI0, | 
|  | 618 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 619 | }, | 
|  | 620 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 14, .size = 2  }, | 
|  | 621 | .reg_div	= { .reg = S3C_CLK_DIV2, .shift = 0, .size = 4  }, | 
|  | 622 | .sources	= &clkset_spi_mmc, | 
|  | 623 | }, { | 
|  | 624 | .clk	= { | 
|  | 625 | .name		= "spi-bus", | 
|  | 626 | .id		= 1, | 
|  | 627 | .ctrlbit        = S3C_CLKCON_SCLK_SPI1, | 
|  | 628 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 629 | }, | 
|  | 630 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 16, .size = 2  }, | 
|  | 631 | .reg_div	= { .reg = S3C_CLK_DIV2, .shift = 4, .size = 4  }, | 
|  | 632 | .sources	= &clkset_spi_mmc, | 
|  | 633 | }, { | 
|  | 634 | .clk	= { | 
|  | 635 | .name		= "audio-bus", | 
|  | 636 | .id		= 0, | 
|  | 637 | .ctrlbit        = S3C_CLKCON_SCLK_AUDIO0, | 
|  | 638 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 639 | }, | 
|  | 640 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 7, .size = 3  }, | 
|  | 641 | .reg_div	= { .reg = S3C_CLK_DIV2, .shift = 8, .size = 4  }, | 
|  | 642 | .sources	= &clkset_audio0, | 
|  | 643 | }, { | 
|  | 644 | .clk	= { | 
|  | 645 | .name		= "audio-bus", | 
|  | 646 | .id		= 1, | 
|  | 647 | .ctrlbit        = S3C_CLKCON_SCLK_AUDIO1, | 
|  | 648 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 649 | }, | 
|  | 650 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 10, .size = 3  }, | 
|  | 651 | .reg_div	= { .reg = S3C_CLK_DIV2, .shift = 12, .size = 4  }, | 
|  | 652 | .sources	= &clkset_audio1, | 
|  | 653 | }, { | 
|  | 654 | .clk	= { | 
|  | 655 | .name		= "irda-bus", | 
|  | 656 | .id		= 0, | 
|  | 657 | .ctrlbit        = S3C_CLKCON_SCLK_IRDA, | 
|  | 658 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 659 | }, | 
|  | 660 | .reg_src	= { .reg = S3C_CLK_SRC, .shift = 24, .size = 2  }, | 
|  | 661 | .reg_div	= { .reg = S3C_CLK_DIV2, .shift = 20, .size = 4  }, | 
|  | 662 | .sources	= &clkset_irda, | 
|  | 663 | }, { | 
|  | 664 | .clk	= { | 
|  | 665 | .name		= "camera", | 
|  | 666 | .id		= -1, | 
|  | 667 | .ctrlbit        = S3C_CLKCON_SCLK_CAM, | 
|  | 668 | .enable		= s3c64xx_sclk_ctrl, | 
|  | 669 | }, | 
|  | 670 | .reg_div	= { .reg = S3C_CLK_DIV0, .shift = 20, .size = 4  }, | 
|  | 671 | .reg_src	= { .reg = NULL, .shift = 0, .size = 0  }, | 
|  | 672 | .sources	= &clkset_camif, | 
|  | 673 | }, | 
|  | 674 | }; | 
|  | 675 |  | 
|  | 676 | /* Clock initialisation code */ | 
|  | 677 |  | 
|  | 678 | static struct clksrc_clk *init_parents[] = { | 
|  | 679 | &clk_mout_apll, | 
|  | 680 | &clk_mout_epll, | 
|  | 681 | &clk_mout_mpll, | 
|  | 682 | }; | 
|  | 683 |  | 
|  | 684 | #define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1) | 
|  | 685 |  | 
|  | 686 | void __init_or_cpufreq s3c6400_setup_clocks(void) | 
|  | 687 | { | 
|  | 688 | struct clk *xtal_clk; | 
|  | 689 | unsigned long xtal; | 
|  | 690 | unsigned long fclk; | 
|  | 691 | unsigned long hclk; | 
|  | 692 | unsigned long hclk2; | 
|  | 693 | unsigned long pclk; | 
|  | 694 | unsigned long epll; | 
|  | 695 | unsigned long apll; | 
|  | 696 | unsigned long mpll; | 
|  | 697 | unsigned int ptr; | 
|  | 698 | u32 clkdiv0; | 
|  | 699 |  | 
|  | 700 | printk(KERN_DEBUG "%s: registering clocks\n", __func__); | 
|  | 701 |  | 
|  | 702 | clkdiv0 = __raw_readl(S3C_CLK_DIV0); | 
|  | 703 | printk(KERN_DEBUG "%s: clkdiv0 = %08x\n", __func__, clkdiv0); | 
|  | 704 |  | 
|  | 705 | xtal_clk = clk_get(NULL, "xtal"); | 
|  | 706 | BUG_ON(IS_ERR(xtal_clk)); | 
|  | 707 |  | 
|  | 708 | xtal = clk_get_rate(xtal_clk); | 
|  | 709 | clk_put(xtal_clk); | 
|  | 710 |  | 
|  | 711 | printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal); | 
|  | 712 |  | 
|  | 713 | /* For now assume the mux always selects the crystal */ | 
|  | 714 | clk_ext_xtal_mux.parent = xtal_clk; | 
|  | 715 |  | 
|  | 716 | epll = s3c6400_get_epll(xtal); | 
|  | 717 | mpll = s3c6400_get_pll(xtal, __raw_readl(S3C_MPLL_CON)); | 
|  | 718 | apll = s3c6400_get_pll(xtal, __raw_readl(S3C_APLL_CON)); | 
|  | 719 |  | 
|  | 720 | fclk = mpll; | 
|  | 721 |  | 
|  | 722 | printk(KERN_INFO "S3C64XX: PLL settings, A=%ld, M=%ld, E=%ld\n", | 
|  | 723 | apll, mpll, epll); | 
|  | 724 |  | 
|  | 725 | hclk2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2); | 
|  | 726 | hclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK); | 
|  | 727 | pclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_PCLK); | 
|  | 728 |  | 
|  | 729 | printk(KERN_INFO "S3C64XX: HCLK2=%ld, HCLK=%ld, PCLK=%ld\n", | 
|  | 730 | hclk2, hclk, pclk); | 
|  | 731 |  | 
|  | 732 | clk_fout_mpll.rate = mpll; | 
|  | 733 | clk_fout_epll.rate = epll; | 
|  | 734 | clk_fout_apll.rate = apll; | 
|  | 735 |  | 
|  | 736 | clk_h2.rate = hclk2; | 
|  | 737 | clk_h.rate = hclk; | 
|  | 738 | clk_p.rate = pclk; | 
|  | 739 | clk_f.rate = fclk; | 
|  | 740 |  | 
|  | 741 | for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) | 
|  | 742 | s3c_set_clksrc(init_parents[ptr], true); | 
|  | 743 |  | 
|  | 744 | for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) | 
|  | 745 | s3c_set_clksrc(&clksrcs[ptr], true); | 
|  | 746 | } | 
|  | 747 |  | 
|  | 748 | static struct clk *clks1[] __initdata = { | 
|  | 749 | &clk_ext_xtal_mux, | 
|  | 750 | &clk_iis_cd0, | 
|  | 751 | &clk_iis_cd1, | 
|  | 752 | &clk_pcm_cd, | 
|  | 753 | &clk_mout_epll.clk, | 
|  | 754 | &clk_mout_mpll.clk, | 
|  | 755 | &clk_dout_mpll, | 
|  | 756 | &clk_arm, | 
|  | 757 | }; | 
|  | 758 |  | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 759 | static struct clk *clks[] __initdata = { | 
|  | 760 | &clk_ext, | 
|  | 761 | &clk_epll, | 
|  | 762 | &clk_27m, | 
|  | 763 | &clk_48m, | 
| Werner Almesberger | a03f7da | 2009-03-05 11:43:13 +0800 | [diff] [blame] | 764 | &clk_h2, | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 765 | }; | 
|  | 766 |  | 
| Ben Dooks | 55bf926 | 2010-01-26 15:10:38 +0900 | [diff] [blame] | 767 | /** | 
|  | 768 | * s3c64xx_register_clocks - register clocks for s3c6400 and s3c6410 | 
|  | 769 | * @xtal: The rate for the clock crystal feeding the PLLs. | 
|  | 770 | * @armclk_divlimit: Divisor mask for ARMCLK. | 
|  | 771 | * | 
|  | 772 | * Register the clocks for the S3C6400 and S3C6410 SoC range, such | 
|  | 773 | * as ARMCLK as well as the necessary parent clocks. | 
|  | 774 | * | 
|  | 775 | * This call does not setup the clocks, which is left to the | 
|  | 776 | * s3c6400_setup_clocks() call which may be needed by the cpufreq | 
|  | 777 | * or resume code to re-set the clocks if the bootloader has changed | 
|  | 778 | * them. | 
|  | 779 | */ | 
|  | 780 | void __init s3c64xx_register_clocks(unsigned long xtal, | 
|  | 781 | unsigned armclk_divlimit) | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 782 | { | 
|  | 783 | struct clk *clkp; | 
|  | 784 | int ret; | 
|  | 785 | int ptr; | 
|  | 786 |  | 
| Ben Dooks | 55bf926 | 2010-01-26 15:10:38 +0900 | [diff] [blame] | 787 | armclk_mask = armclk_divlimit; | 
|  | 788 |  | 
|  | 789 | s3c24xx_register_baseclocks(xtal); | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 790 | s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); | 
| Ben Dooks | 55bf926 | 2010-01-26 15:10:38 +0900 | [diff] [blame] | 791 |  | 
| Ben Dooks | 1d9f13c | 2010-01-06 01:21:38 +0900 | [diff] [blame] | 792 | s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 793 |  | 
|  | 794 | clkp = init_clocks_disable; | 
|  | 795 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { | 
|  | 796 |  | 
|  | 797 | ret = s3c24xx_register_clock(clkp); | 
|  | 798 | if (ret < 0) { | 
|  | 799 | printk(KERN_ERR "Failed to register clock %s (%d)\n", | 
|  | 800 | clkp->name, ret); | 
|  | 801 | } | 
|  | 802 |  | 
|  | 803 | (clkp->enable)(clkp, 0); | 
|  | 804 | } | 
| Ben Dooks | 9d325f2 | 2008-11-21 10:36:05 +0000 | [diff] [blame] | 805 |  | 
| Ben Dooks | 55bf926 | 2010-01-26 15:10:38 +0900 | [diff] [blame] | 806 | s3c24xx_register_clocks(clks1, ARRAY_SIZE(clks1)); | 
|  | 807 | s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs)); | 
| Ben Dooks | 9d325f2 | 2008-11-21 10:36:05 +0000 | [diff] [blame] | 808 | s3c_pwmclk_init(); | 
| Ben Dooks | 4b31d8b | 2008-10-21 14:07:00 +0100 | [diff] [blame] | 809 | } |