1/* linux/arch/arm/mach-s3c2442/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 * S3C2442 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 37#include <asm/hardware.h> 38#include <asm/atomic.h> 39#include <asm/irq.h> 40#include <asm/io.h> 41 42#include <asm/arch/regs-clock.h> 43 44#include <asm/plat-s3c24xx/clock.h> 45#include <asm/plat-s3c24xx/cpu.h> 46 47/* S3C2442 extended clock support */ 48 49static unsigned long s3c2442_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 div = parent_rate / rate; 59 60 if (div == 3) 61 return parent_rate / 3; 62 63 /* note, we remove the +/- 1 calculations for the divisor */ 64 65 div /= 2; 66 67 if (div < 1) 68 div = 1; 69 else if (div > 16) 70 div = 16; 71 72 return parent_rate / (div * 2); 73} 74 75static int s3c2442_camif_upll_setrate(struct clk *clk, unsigned long rate) 76{ 77 unsigned long parent_rate = clk_get_rate(clk->parent); 78 unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); 79 80 rate = s3c2442_camif_upll_round(clk, rate); 81 82 camdivn &= ~S3C2442_CAMDIVN_CAMCLK_DIV3; 83 84 if (rate == parent_rate) { 85 camdivn &= ~S3C2440_CAMDIVN_CAMCLK_SEL; 86 } else if ((parent_rate / rate) == 3) { 87 camdivn |= S3C2440_CAMDIVN_CAMCLK_SEL; 88 camdivn |= S3C2442_CAMDIVN_CAMCLK_DIV3; 89 } else { 90 camdivn &= ~S3C2440_CAMDIVN_CAMCLK_MASK; 91 camdivn |= S3C2440_CAMDIVN_CAMCLK_SEL; 92 camdivn |= (((parent_rate / rate) / 2) - 1); 93 } 94 95 __raw_writel(camdivn, S3C2440_CAMDIVN); 96 97 return 0; 98} 99 100/* Extra S3C2442 clocks */ 101 102static struct clk s3c2442_clk_cam = { 103 .name = "camif", 104 .id = -1, 105 .enable = s3c2410_clkcon_enable, 106 .ctrlbit = S3C2440_CLKCON_CAMERA, 107}; 108 109static struct clk s3c2442_clk_cam_upll = { 110 .name = "camif-upll", 111 .id = -1, 112 .set_rate = s3c2442_camif_upll_setrate, 113 .round_rate = s3c2442_camif_upll_round, 114}; 115 116static int s3c2442_clk_add(struct sys_device *sysdev) 117{ 118 unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); 119 unsigned long clkdivn; 120 struct clk *clock_h; 121 struct clk *clock_p; 122 struct clk *clock_upll; 123 124 printk("S3C2442: Clock Support, DVS %s\n", 125 (camdivn & S3C2440_CAMDIVN_DVSEN) ? "on" : "off"); 126 127 clock_p = clk_get(NULL, "pclk"); 128 clock_h = clk_get(NULL, "hclk"); 129 clock_upll = clk_get(NULL, "upll"); 130 131 if (IS_ERR(clock_p) || IS_ERR(clock_h) || IS_ERR(clock_upll)) { 132 printk(KERN_ERR "S3C2442: Failed to get parent clocks\n"); 133 return -EINVAL; 134 } 135 136 /* check rate of UPLL, and if it is near 96MHz, then change 137 * to using half the UPLL rate for the system */ 138 139 if (clk_get_rate(clock_upll) > (94 * MHZ)) { 140 clk_usb_bus.rate = clk_get_rate(clock_upll) / 2; 141 142 mutex_lock(&clocks_mutex); 143 144 clkdivn = __raw_readl(S3C2410_CLKDIVN); 145 clkdivn |= S3C2440_CLKDIVN_UCLK; 146 __raw_writel(clkdivn, S3C2410_CLKDIVN); 147 148 mutex_unlock(&clocks_mutex); 149 } 150 151 s3c2442_clk_cam.parent = clock_h; 152 s3c2442_clk_cam_upll.parent = clock_upll; 153 154 s3c24xx_register_clock(&s3c2442_clk_cam); 155 s3c24xx_register_clock(&s3c2442_clk_cam_upll); 156 157 clk_disable(&s3c2442_clk_cam); 158 159 return 0; 160} 161 162static struct sysdev_driver s3c2442_clk_driver = { 163 .add = s3c2442_clk_add, 164}; 165 166static __init int s3c2442_clk_init(void) 167{ 168 return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_clk_driver); 169} 170 171arch_initcall(s3c2442_clk_init); 172