1/* linux/arch/arm/plat-samsung/clock-clksrc.c 2 * 3 * Copyright 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 12#include <linux/init.h> 13#include <linux/module.h> 14#include <linux/kernel.h> 15#include <linux/list.h> 16#include <linux/errno.h> 17#include <linux/err.h> 18#include <linux/clk.h> 19#include <linux/sysdev.h> 20#include <linux/io.h> 21 22#include <plat/clock.h> 23#include <plat/clock-clksrc.h> 24#include <plat/cpu-freq.h> 25 26static inline struct clksrc_clk *to_clksrc(struct clk *clk) 27{ 28 return container_of(clk, struct clksrc_clk, clk); 29} 30 31static inline u32 bit_mask(u32 shift, u32 nr_bits) 32{ 33 u32 mask = 0xffffffff >> (32 - nr_bits); 34 35 return mask << shift; 36} 37 38static unsigned long s3c_getrate_clksrc(struct clk *clk) 39{ 40 struct clksrc_clk *sclk = to_clksrc(clk); 41 unsigned long rate = clk_get_rate(clk->parent); 42 u32 clkdiv = __raw_readl(sclk->reg_div.reg); 43 u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size); 44 45 clkdiv &= mask; 46 clkdiv >>= sclk->reg_div.shift; 47 clkdiv++; 48 49 rate /= clkdiv; 50 return rate; 51} 52 53static int s3c_setrate_clksrc(struct clk *clk, unsigned long rate) 54{ 55 struct clksrc_clk *sclk = to_clksrc(clk); 56 void __iomem *reg = sclk->reg_div.reg; 57 unsigned int div; 58 u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size); 59 u32 val; 60 61 rate = clk_round_rate(clk, rate); 62 div = clk_get_rate(clk->parent) / rate; 63 if (div > (1 << sclk->reg_div.size)) 64 return -EINVAL; 65 66 val = __raw_readl(reg); 67 val &= ~mask; 68 val |= (div - 1) << sclk->reg_div.shift; 69 __raw_writel(val, reg); 70 71 return 0; 72} 73 74static int s3c_setparent_clksrc(struct clk *clk, struct clk *parent) 75{ 76 struct clksrc_clk *sclk = to_clksrc(clk); 77 struct clksrc_sources *srcs = sclk->sources; 78 u32 clksrc = __raw_readl(sclk->reg_src.reg); 79 u32 mask = bit_mask(sclk->reg_src.shift, sclk->reg_src.size); 80 int src_nr = -1; 81 int ptr; 82 83 for (ptr = 0; ptr < srcs->nr_sources; ptr++) 84 if (srcs->sources[ptr] == parent) { 85 src_nr = ptr; 86 break; 87 } 88 89 if (src_nr >= 0) { 90 clk->parent = parent; 91 92 clksrc &= ~mask; 93 clksrc |= src_nr << sclk->reg_src.shift; 94 95 __raw_writel(clksrc, sclk->reg_src.reg); 96 return 0; 97 } 98 99 return -EINVAL; 100} 101 102static unsigned long s3c_roundrate_clksrc(struct clk *clk, 103 unsigned long rate) 104{ 105 struct clksrc_clk *sclk = to_clksrc(clk); 106 unsigned long parent_rate = clk_get_rate(clk->parent); 107 int max_div = 1 << sclk->reg_div.size; 108 int div; 109 110 if (rate >= parent_rate) 111 rate = parent_rate; 112 else { 113 div = parent_rate / rate; 114 if (parent_rate % rate) 115 div++; 116 117 if (div == 0) 118 div = 1; 119 if (div > max_div) 120 div = max_div; 121 122 rate = parent_rate / div; 123 } 124 125 return rate; 126} 127 128/* Clock initialisation code */ 129 130void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk, bool announce) 131{ 132 struct clksrc_sources *srcs = clk->sources; 133 u32 mask = bit_mask(clk->reg_src.shift, clk->reg_src.size); 134 u32 clksrc; 135 136 if (!clk->reg_src.reg) { 137 if (!clk->clk.parent) 138 printk(KERN_ERR "%s: no parent clock specified\n", 139 clk->clk.name); 140 return; 141 } 142 143 clksrc = __raw_readl(clk->reg_src.reg); 144 clksrc &= mask; 145 clksrc >>= clk->reg_src.shift; 146 147 if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) { 148 printk(KERN_ERR "%s: bad source %d\n", 149 clk->clk.name, clksrc); 150 return; 151 } 152 153 clk->clk.parent = srcs->sources[clksrc]; 154 155 if (announce) 156 printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", 157 clk->clk.name, clk->clk.parent->name, clksrc, 158 clk_get_rate(&clk->clk)); 159} 160 161static struct clk_ops clksrc_ops = { 162 .set_parent = s3c_setparent_clksrc, 163 .get_rate = s3c_getrate_clksrc, 164 .set_rate = s3c_setrate_clksrc, 165 .round_rate = s3c_roundrate_clksrc, 166}; 167 168static struct clk_ops clksrc_ops_nodiv = { 169 .set_parent = s3c_setparent_clksrc, 170}; 171 172static struct clk_ops clksrc_ops_nosrc = { 173 .get_rate = s3c_getrate_clksrc, 174 .set_rate = s3c_setrate_clksrc, 175 .round_rate = s3c_roundrate_clksrc, 176}; 177 178void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) 179{ 180 int ret; 181 182 for (; size > 0; size--, clksrc++) { 183 if (!clksrc->reg_div.reg && !clksrc->reg_src.reg) 184 printk(KERN_ERR "%s: clock %s has no registers set\n", 185 __func__, clksrc->clk.name); 186 187 /* fill in the default functions */ 188 189 if (!clksrc->clk.ops) { 190 if (!clksrc->reg_div.reg) 191 clksrc->clk.ops = &clksrc_ops_nodiv; 192 else if (!clksrc->reg_src.reg) 193 clksrc->clk.ops = &clksrc_ops_nosrc; 194 else 195 clksrc->clk.ops = &clksrc_ops; 196 } 197 198 /* setup the clocksource, but do not announce it 199 * as it may be re-set by the setup routines 200 * called after the rest of the clocks have been 201 * registered 202 */ 203 s3c_set_clksrc(clksrc, false); 204 205 ret = s3c24xx_register_clock(&clksrc->clk); 206 207 if (ret < 0) { 208 printk(KERN_ERR "%s: failed to register %s (%d)\n", 209 __func__, clksrc->clk.name, ret); 210 } 211 } 212} 213