1/* linux/arch/arm/plat-s3c24xx/s3c24xx-clock.c 2 * 3 * Copyright (c) 2004-2008 Simtec Electronics 4 * http://armlinux.simtec.co.uk/ 5 * Ben Dooks <ben@simtec.co.uk> 6 * 7 * S3C2440/S3C2442 Common clock support 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 as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22*/ 23 24#include <linux/init.h> 25#include <linux/module.h> 26#include <linux/kernel.h> 27#include <linux/list.h> 28#include <linux/errno.h> 29#include <linux/err.h> 30#include <linux/device.h> 31#include <linux/sysdev.h> 32#include <linux/interrupt.h> 33#include <linux/ioport.h> 34#include <linux/clk.h> 35#include <linux/io.h> 36 37#include <mach/hardware.h> 38#include <asm/atomic.h> 39#include <asm/irq.h> 40 41#include <mach/regs-clock.h> 42 43#include <plat/clock.h> 44#include <plat/cpu.h> 45 46static int s3c2440_setparent_armclk(struct clk *clk, struct clk *parent) 47{ 48 unsigned long camdivn; 49 unsigned long dvs; 50 51 if (parent == &clk_f) 52 dvs = 0; 53 else if (parent == &clk_h) 54 dvs = S3C2440_CAMDIVN_DVSEN; 55 else 56 return -EINVAL; 57 58 clk->parent = parent; 59 60 camdivn = __raw_readl(S3C2440_CAMDIVN); 61 camdivn &= ~S3C2440_CAMDIVN_DVSEN; 62 camdivn |= dvs; 63 __raw_writel(camdivn, S3C2440_CAMDIVN); 64 65 return 0; 66} 67 68static struct clk clk_arm = { 69 .name = "armclk", 70 .id = -1, 71 .ops = &(struct clk_ops) { 72 .set_parent = s3c2440_setparent_armclk, 73 }, 74}; 75 76static int s3c244x_clk_add(struct sys_device *sysdev) 77{ 78 unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); 79 unsigned long clkdivn; 80 struct clk *clock_upll; 81 int ret; 82 83 printk("S3C244X: Clock Support, DVS %s\n", 84 (camdivn & S3C2440_CAMDIVN_DVSEN) ? "on" : "off"); 85 86 clk_arm.parent = (camdivn & S3C2440_CAMDIVN_DVSEN) ? &clk_h : &clk_f; 87 88 ret = s3c24xx_register_clock(&clk_arm); 89 if (ret < 0) { 90 printk(KERN_ERR "S3C24XX: Failed to add armclk (%d)\n", ret); 91 return ret; 92 } 93 94 clock_upll = clk_get(NULL, "upll"); 95 if (IS_ERR(clock_upll)) { 96 printk(KERN_ERR "S3C244X: Failed to get upll clock\n"); 97 return -ENOENT; 98 } 99 100 /* check rate of UPLL, and if it is near 96MHz, then change 101 * to using half the UPLL rate for the system */ 102 103 if (clk_get_rate(clock_upll) > (94 * MHZ)) { 104 clk_usb_bus.rate = clk_get_rate(clock_upll) / 2; 105 106 spin_lock(&clocks_lock); 107 108 clkdivn = __raw_readl(S3C2410_CLKDIVN); 109 clkdivn |= S3C2440_CLKDIVN_UCLK; 110 __raw_writel(clkdivn, S3C2410_CLKDIVN); 111 112 spin_unlock(&clocks_lock); 113 } 114 115 return 0; 116} 117 118static struct sysdev_driver s3c2440_clk_driver = { 119 .add = s3c244x_clk_add, 120}; 121 122static int s3c2440_clk_init(void) 123{ 124 return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_clk_driver); 125} 126 127arch_initcall(s3c2440_clk_init); 128 129static struct sysdev_driver s3c2442_clk_driver = { 130 .add = s3c244x_clk_add, 131}; 132 133static int s3c2442_clk_init(void) 134{ 135 return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_clk_driver); 136} 137 138arch_initcall(s3c2442_clk_init); 139