1/* linux/arch/arm/mach-s3c2440/clock.c 2 * 3 * Copyright (c) 2004-2005 Simtec Electronics 4 * http://armlinux.simtec.co.uk/ 5 * Ben Dooks <ben@simtec.co.uk> 6 * 7 * S3C2440 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/mutex.h> 35#include <linux/clk.h> 36#include <linux/io.h> 37 38#include <mach/hardware.h> 39#include <asm/atomic.h> 40#include <asm/irq.h> 41 42#include <mach/regs-clock.h> 43 44#include <plat/clock.h> 45#include <plat/cpu.h> 46 47/* S3C2440 extended clock support */ 48 49static unsigned long s3c2440_camif_upll_round(struct clk *clk, 50 unsigned long rate) 51{ 52 unsigned long parent_rate = clk_get_rate(clk->parent); 53 int div; 54 55 if (rate > parent_rate) 56 return parent_rate; 57 58 /* note, we remove the +/- 1 calculations for the divisor */ 59 60 div = (parent_rate / rate) / 2; 61 62 if (div < 1) 63 div = 1; 64 else if (div > 16) 65 div = 16; 66 67 return parent_rate / (div * 2); 68} 69 70static int s3c2440_camif_upll_setrate(struct clk *clk, unsigned long rate) 71{ 72 unsigned long parent_rate = clk_get_rate(clk->parent); 73 unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); 74 75 rate = s3c2440_camif_upll_round(clk, rate); 76 77 camdivn &= ~(S3C2440_CAMDIVN_CAMCLK_SEL | S3C2440_CAMDIVN_CAMCLK_MASK); 78 79 if (rate != parent_rate) { 80 camdivn |= S3C2440_CAMDIVN_CAMCLK_SEL; 81 camdivn |= (((parent_rate / rate) / 2) - 1); 82 } 83 84 __raw_writel(camdivn, S3C2440_CAMDIVN); 85 86 return 0; 87} 88 89/* Extra S3C2440 clocks */ 90 91static struct clk s3c2440_clk_cam = { 92 .name = "camif", 93 .id = -1, 94 .enable = s3c2410_clkcon_enable, 95 .ctrlbit = S3C2440_CLKCON_CAMERA, 96}; 97 98static struct clk s3c2440_clk_cam_upll = { 99 .name = "camif-upll", 100 .id = -1, 101 .ops = &(struct clk_ops) { 102 .set_rate = s3c2440_camif_upll_setrate, 103 .round_rate = s3c2440_camif_upll_round, 104 }, 105}; 106 107static struct clk s3c2440_clk_ac97 = { 108 .name = "ac97", 109 .id = -1, 110 .enable = s3c2410_clkcon_enable, 111 .ctrlbit = S3C2440_CLKCON_CAMERA, 112}; 113 114static int s3c2440_clk_add(struct sys_device *sysdev) 115{ 116 struct clk *clock_upll; 117 struct clk *clock_h; 118 struct clk *clock_p; 119 120 clock_p = clk_get(NULL, "pclk"); 121 clock_h = clk_get(NULL, "hclk"); 122 clock_upll = clk_get(NULL, "upll"); 123 124 if (IS_ERR(clock_p) || IS_ERR(clock_h) || IS_ERR(clock_upll)) { 125 printk(KERN_ERR "S3C2440: Failed to get parent clocks\n"); 126 return -EINVAL; 127 } 128 129 s3c2440_clk_cam.parent = clock_h; 130 s3c2440_clk_ac97.parent = clock_p; 131 s3c2440_clk_cam_upll.parent = clock_upll; 132 133 s3c24xx_register_clock(&s3c2440_clk_ac97); 134 s3c24xx_register_clock(&s3c2440_clk_cam); 135 s3c24xx_register_clock(&s3c2440_clk_cam_upll); 136 137 clk_disable(&s3c2440_clk_ac97); 138 clk_disable(&s3c2440_clk_cam); 139 140 return 0; 141} 142 143static struct sysdev_driver s3c2440_clk_driver = { 144 .add = s3c2440_clk_add, 145}; 146 147static __init int s3c24xx_clk_driver(void) 148{ 149 return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_clk_driver); 150} 151 152arch_initcall(s3c24xx_clk_driver); 153