1/* linux/arch/arm/plat-s3c24xx/clock-dclk.c 2 * 3 * Copyright (c) 2004-2008 Simtec Electronics 4 * Ben Dooks <ben@simtec.co.uk> 5 * http://armlinux.simtec.co.uk/ 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * S3C24XX - definitions for DCLK and CLKOUT registers 12 */ 13 14#include <linux/kernel.h> 15#include <linux/errno.h> 16#include <linux/clk.h> 17#include <linux/io.h> 18 19#include <mach/regs-clock.h> 20#include <mach/regs-gpio.h> 21 22#include <plat/clock.h> 23#include <plat/cpu.h> 24 25/* clocks that could be registered by external code */ 26 27static int s3c24xx_dclk_enable(struct clk *clk, int enable) 28{ 29 unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); 30 31 if (enable) 32 dclkcon |= clk->ctrlbit; 33 else 34 dclkcon &= ~clk->ctrlbit; 35 36 __raw_writel(dclkcon, S3C24XX_DCLKCON); 37 38 return 0; 39} 40 41static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) 42{ 43 unsigned long dclkcon; 44 unsigned int uclk; 45 46 if (parent == &clk_upll) 47 uclk = 1; 48 else if (parent == &clk_p) 49 uclk = 0; 50 else 51 return -EINVAL; 52 53 clk->parent = parent; 54 55 dclkcon = __raw_readl(S3C24XX_DCLKCON); 56 57 if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { 58 if (uclk) 59 dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; 60 else 61 dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; 62 } else { 63 if (uclk) 64 dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; 65 else 66 dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; 67 } 68 69 __raw_writel(dclkcon, S3C24XX_DCLKCON); 70 71 return 0; 72} 73static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate) 74{ 75 unsigned long div; 76 77 if ((rate == 0) || !clk->parent) 78 return 0; 79 80 div = clk_get_rate(clk->parent) / rate; 81 if (div < 2) 82 div = 2; 83 else if (div > 16) 84 div = 16; 85 86 return div; 87} 88 89static unsigned long s3c24xx_round_dclk_rate(struct clk *clk, 90 unsigned long rate) 91{ 92 unsigned long div = s3c24xx_calc_div(clk, rate); 93 94 if (div == 0) 95 return 0; 96 97 return clk_get_rate(clk->parent) / div; 98} 99 100static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate) 101{ 102 unsigned long mask, data, div = s3c24xx_calc_div(clk, rate); 103 104 if (div == 0) 105 return -EINVAL; 106 107 if (clk == &s3c24xx_dclk0) { 108 mask = S3C2410_DCLKCON_DCLK0_DIV_MASK | 109 S3C2410_DCLKCON_DCLK0_CMP_MASK; 110 data = S3C2410_DCLKCON_DCLK0_DIV(div) | 111 S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2); 112 } else if (clk == &s3c24xx_dclk1) { 113 mask = S3C2410_DCLKCON_DCLK1_DIV_MASK | 114 S3C2410_DCLKCON_DCLK1_CMP_MASK; 115 data = S3C2410_DCLKCON_DCLK1_DIV(div) | 116 S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2); 117 } else 118 return -EINVAL; 119 120 clk->rate = clk_get_rate(clk->parent) / div; 121 __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data), 122 S3C24XX_DCLKCON); 123 return clk->rate; 124} 125static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) 126{ 127 unsigned long mask; 128 unsigned long source; 129 130 /* calculate the MISCCR setting for the clock */ 131 132 if (parent == &clk_mpll) 133 source = S3C2410_MISCCR_CLK0_MPLL; 134 else if (parent == &clk_upll) 135 source = S3C2410_MISCCR_CLK0_UPLL; 136 else if (parent == &clk_f) 137 source = S3C2410_MISCCR_CLK0_FCLK; 138 else if (parent == &clk_h) 139 source = S3C2410_MISCCR_CLK0_HCLK; 140 else if (parent == &clk_p) 141 source = S3C2410_MISCCR_CLK0_PCLK; 142 else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) 143 source = S3C2410_MISCCR_CLK0_DCLK0; 144 else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) 145 source = S3C2410_MISCCR_CLK0_DCLK0; 146 else 147 return -EINVAL; 148 149 clk->parent = parent; 150 151 if (clk == &s3c24xx_clkout0) 152 mask = S3C2410_MISCCR_CLK0_MASK; 153 else { 154 source <<= 4; 155 mask = S3C2410_MISCCR_CLK1_MASK; 156 } 157 158 s3c2410_modify_misccr(mask, source); 159 return 0; 160} 161 162/* external clock definitions */ 163 164static struct clk_ops dclk_ops = { 165 .set_parent = s3c24xx_dclk_setparent, 166 .set_rate = s3c24xx_set_dclk_rate, 167 .round_rate = s3c24xx_round_dclk_rate, 168}; 169 170struct clk s3c24xx_dclk0 = { 171 .name = "dclk0", 172 .id = -1, 173 .ctrlbit = S3C2410_DCLKCON_DCLK0EN, 174 .enable = s3c24xx_dclk_enable, 175 .ops = &dclk_ops, 176}; 177 178struct clk s3c24xx_dclk1 = { 179 .name = "dclk1", 180 .id = -1, 181 .ctrlbit = S3C2410_DCLKCON_DCLK1EN, 182 .enable = s3c24xx_dclk_enable, 183 .ops = &dclk_ops, 184}; 185 186static struct clk_ops clkout_ops = { 187 .set_parent = s3c24xx_clkout_setparent, 188}; 189 190struct clk s3c24xx_clkout0 = { 191 .name = "clkout0", 192 .id = -1, 193 .ops = &clkout_ops, 194}; 195 196struct clk s3c24xx_clkout1 = { 197 .name = "clkout1", 198 .id = -1, 199 .ops = &clkout_ops, 200}; 201