1/* linux/arch/arm/plat-s3c24xx/clock.c 2 * 3 * Copyright (c) 2004-2005 Simtec Electronics 4 * Ben Dooks <ben@simtec.co.uk> 5 * 6 * S3C24XX Core clock control support 7 * 8 * Based on, and code from linux/arch/arm/mach-versatile/clock.c 9 ** 10 ** Copyright (C) 2004 ARM Limited. 11 ** Written by Deep Blue Solutions Limited. 12 * 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation; either version 2 of the License, or 17 * (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 27*/ 28 29#include <linux/init.h> 30#include <linux/module.h> 31#include <linux/kernel.h> 32#include <linux/list.h> 33#include <linux/errno.h> 34#include <linux/err.h> 35#include <linux/platform_device.h> 36#include <linux/sysdev.h> 37#include <linux/interrupt.h> 38#include <linux/ioport.h> 39#include <linux/clk.h> 40#include <linux/mutex.h> 41#include <linux/delay.h> 42 43#include <asm/hardware.h> 44#include <asm/irq.h> 45#include <asm/io.h> 46 47#include <asm/arch/regs-clock.h> 48#include <asm/arch/regs-gpio.h> 49 50#include <asm/plat-s3c24xx/clock.h> 51#include <asm/plat-s3c24xx/cpu.h> 52 53/* clock information */ 54 55static LIST_HEAD(clocks); 56 57DEFINE_MUTEX(clocks_mutex); 58 59/* enable and disable calls for use with the clk struct */ 60 61static int clk_null_enable(struct clk *clk, int enable) 62{ 63 return 0; 64} 65 66/* Clock API calls */ 67 68struct clk *clk_get(struct device *dev, const char *id) 69{ 70 struct clk *p; 71 struct clk *clk = ERR_PTR(-ENOENT); 72 int idno; 73 74 if (dev == NULL || dev->bus != &platform_bus_type) 75 idno = -1; 76 else 77 idno = to_platform_device(dev)->id; 78 79 mutex_lock(&clocks_mutex); 80 81 list_for_each_entry(p, &clocks, list) { 82 if (p->id == idno && 83 strcmp(id, p->name) == 0 && 84 try_module_get(p->owner)) { 85 clk = p; 86 break; 87 } 88 } 89 90 /* check for the case where a device was supplied, but the 91 * clock that was being searched for is not device specific */ 92 93 if (IS_ERR(clk)) { 94 list_for_each_entry(p, &clocks, list) { 95 if (p->id == -1 && strcmp(id, p->name) == 0 && 96 try_module_get(p->owner)) { 97 clk = p; 98 break; 99 } 100 } 101 } 102 103 mutex_unlock(&clocks_mutex); 104 return clk; 105} 106 107void clk_put(struct clk *clk) 108{ 109 module_put(clk->owner); 110} 111 112int clk_enable(struct clk *clk) 113{ 114 if (IS_ERR(clk) || clk == NULL) 115 return -EINVAL; 116 117 clk_enable(clk->parent); 118 119 mutex_lock(&clocks_mutex); 120 121 if ((clk->usage++) == 0) 122 (clk->enable)(clk, 1); 123 124 mutex_unlock(&clocks_mutex); 125 return 0; 126} 127 128void clk_disable(struct clk *clk) 129{ 130 if (IS_ERR(clk) || clk == NULL) 131 return; 132 133 mutex_lock(&clocks_mutex); 134 135 if ((--clk->usage) == 0) 136 (clk->enable)(clk, 0); 137 138 mutex_unlock(&clocks_mutex); 139 clk_disable(clk->parent); 140} 141 142 143unsigned long clk_get_rate(struct clk *clk) 144{ 145 if (IS_ERR(clk)) 146 return 0; 147 148 if (clk->rate != 0) 149 return clk->rate; 150 151 if (clk->get_rate != NULL) 152 return (clk->get_rate)(clk); 153 154 if (clk->parent != NULL) 155 return clk_get_rate(clk->parent); 156 157 return clk->rate; 158} 159 160long clk_round_rate(struct clk *clk, unsigned long rate) 161{ 162 if (!IS_ERR(clk) && clk->round_rate) 163 return (clk->round_rate)(clk, rate); 164 165 return rate; 166} 167 168int clk_set_rate(struct clk *clk, unsigned long rate) 169{ 170 int ret; 171 172 if (IS_ERR(clk)) 173 return -EINVAL; 174 175 mutex_lock(&clocks_mutex); 176 ret = (clk->set_rate)(clk, rate); 177 mutex_unlock(&clocks_mutex); 178 179 return ret; 180} 181 182struct clk *clk_get_parent(struct clk *clk) 183{ 184 return clk->parent; 185} 186 187int clk_set_parent(struct clk *clk, struct clk *parent) 188{ 189 int ret = 0; 190 191 if (IS_ERR(clk)) 192 return -EINVAL; 193 194 mutex_lock(&clocks_mutex); 195 196 if (clk->set_parent) 197 ret = (clk->set_parent)(clk, parent); 198 199 mutex_unlock(&clocks_mutex); 200 201 return ret; 202} 203 204EXPORT_SYMBOL(clk_get); 205EXPORT_SYMBOL(clk_put); 206EXPORT_SYMBOL(clk_enable); 207EXPORT_SYMBOL(clk_disable); 208EXPORT_SYMBOL(clk_get_rate); 209EXPORT_SYMBOL(clk_round_rate); 210EXPORT_SYMBOL(clk_set_rate); 211EXPORT_SYMBOL(clk_get_parent); 212EXPORT_SYMBOL(clk_set_parent); 213 214/* base clocks */ 215 216struct clk clk_xtal = { 217 .name = "xtal", 218 .id = -1, 219 .rate = 0, 220 .parent = NULL, 221 .ctrlbit = 0, 222}; 223 224struct clk clk_mpll = { 225 .name = "mpll", 226 .id = -1, 227}; 228 229struct clk clk_upll = { 230 .name = "upll", 231 .id = -1, 232 .parent = NULL, 233 .ctrlbit = 0, 234}; 235 236struct clk clk_f = { 237 .name = "fclk", 238 .id = -1, 239 .rate = 0, 240 .parent = &clk_mpll, 241 .ctrlbit = 0, 242}; 243 244struct clk clk_h = { 245 .name = "hclk", 246 .id = -1, 247 .rate = 0, 248 .parent = NULL, 249 .ctrlbit = 0, 250}; 251 252struct clk clk_p = { 253 .name = "pclk", 254 .id = -1, 255 .rate = 0, 256 .parent = NULL, 257 .ctrlbit = 0, 258}; 259 260struct clk clk_usb_bus = { 261 .name = "usb-bus", 262 .id = -1, 263 .rate = 0, 264 .parent = &clk_upll, 265}; 266 267/* clocks that could be registered by external code */ 268 269static int s3c24xx_dclk_enable(struct clk *clk, int enable) 270{ 271 unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); 272 273 if (enable) 274 dclkcon |= clk->ctrlbit; 275 else 276 dclkcon &= ~clk->ctrlbit; 277 278 __raw_writel(dclkcon, S3C24XX_DCLKCON); 279 280 return 0; 281} 282 283static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) 284{ 285 unsigned long dclkcon; 286 unsigned int uclk; 287 288 if (parent == &clk_upll) 289 uclk = 1; 290 else if (parent == &clk_p) 291 uclk = 0; 292 else 293 return -EINVAL; 294 295 clk->parent = parent; 296 297 dclkcon = __raw_readl(S3C24XX_DCLKCON); 298 299 if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { 300 if (uclk) 301 dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; 302 else 303 dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; 304 } else { 305 if (uclk) 306 dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; 307 else 308 dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; 309 } 310 311 __raw_writel(dclkcon, S3C24XX_DCLKCON); 312 313 return 0; 314} 315 316 317static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) 318{ 319 unsigned long mask; 320 unsigned long source; 321 322 /* calculate the MISCCR setting for the clock */ 323 324 if (parent == &clk_xtal) 325 source = S3C2410_MISCCR_CLK0_MPLL; 326 else if (parent == &clk_upll) 327 source = S3C2410_MISCCR_CLK0_UPLL; 328 else if (parent == &clk_f) 329 source = S3C2410_MISCCR_CLK0_FCLK; 330 else if (parent == &clk_h) 331 source = S3C2410_MISCCR_CLK0_HCLK; 332 else if (parent == &clk_p) 333 source = S3C2410_MISCCR_CLK0_PCLK; 334 else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) 335 source = S3C2410_MISCCR_CLK0_DCLK0; 336 else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) 337 source = S3C2410_MISCCR_CLK0_DCLK0; 338 else 339 return -EINVAL; 340 341 clk->parent = parent; 342 343 if (clk == &s3c24xx_dclk0) 344 mask = S3C2410_MISCCR_CLK0_MASK; 345 else { 346 source <<= 4; 347 mask = S3C2410_MISCCR_CLK1_MASK; 348 } 349 350 s3c2410_modify_misccr(mask, source); 351 return 0; 352} 353 354/* external clock definitions */ 355 356struct clk s3c24xx_dclk0 = { 357 .name = "dclk0", 358 .id = -1, 359 .ctrlbit = S3C2410_DCLKCON_DCLK0EN, 360 .enable = s3c24xx_dclk_enable, 361 .set_parent = s3c24xx_dclk_setparent, 362}; 363 364struct clk s3c24xx_dclk1 = { 365 .name = "dclk1", 366 .id = -1, 367 .ctrlbit = S3C2410_DCLKCON_DCLK0EN, 368 .enable = s3c24xx_dclk_enable, 369 .set_parent = s3c24xx_dclk_setparent, 370}; 371 372struct clk s3c24xx_clkout0 = { 373 .name = "clkout0", 374 .id = -1, 375 .set_parent = s3c24xx_clkout_setparent, 376}; 377 378struct clk s3c24xx_clkout1 = { 379 .name = "clkout1", 380 .id = -1, 381 .set_parent = s3c24xx_clkout_setparent, 382}; 383 384struct clk s3c24xx_uclk = { 385 .name = "uclk", 386 .id = -1, 387}; 388 389/* initialise the clock system */ 390 391int s3c24xx_register_clock(struct clk *clk) 392{ 393 clk->owner = THIS_MODULE; 394 395 if (clk->enable == NULL) 396 clk->enable = clk_null_enable; 397 398 /* add to the list of available clocks */ 399 400 mutex_lock(&clocks_mutex); 401 list_add(&clk->list, &clocks); 402 mutex_unlock(&clocks_mutex); 403 404 return 0; 405} 406 407int s3c24xx_register_clocks(struct clk **clks, int nr_clks) 408{ 409 int fails = 0; 410 411 for (; nr_clks > 0; nr_clks--, clks++) { 412 if (s3c24xx_register_clock(*clks) < 0) 413 fails++; 414 } 415 416 return fails; 417} 418 419/* initalise all the clocks */ 420 421int __init s3c24xx_setup_clocks(unsigned long xtal, 422 unsigned long fclk, 423 unsigned long hclk, 424 unsigned long pclk) 425{ 426 printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); 427 428 /* initialise the main system clocks */ 429 430 clk_xtal.rate = xtal; 431 clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); 432 433 clk_mpll.rate = fclk; 434 clk_h.rate = hclk; 435 clk_p.rate = pclk; 436 clk_f.rate = fclk; 437 438 /* assume uart clocks are correctly setup */ 439 440 /* register our clocks */ 441 442 if (s3c24xx_register_clock(&clk_xtal) < 0) 443 printk(KERN_ERR "failed to register master xtal\n"); 444 445 if (s3c24xx_register_clock(&clk_mpll) < 0) 446 printk(KERN_ERR "failed to register mpll clock\n"); 447 448 if (s3c24xx_register_clock(&clk_upll) < 0) 449 printk(KERN_ERR "failed to register upll clock\n"); 450 451 if (s3c24xx_register_clock(&clk_f) < 0) 452 printk(KERN_ERR "failed to register cpu fclk\n"); 453 454 if (s3c24xx_register_clock(&clk_h) < 0) 455 printk(KERN_ERR "failed to register cpu hclk\n"); 456 457 if (s3c24xx_register_clock(&clk_p) < 0) 458 printk(KERN_ERR "failed to register cpu pclk\n"); 459 460 return 0; 461} 462