1/* 2 * Clock management for AT32AP CPUs 3 * 4 * Copyright (C) 2006 Atmel Corporation 5 * 6 * Based on arch/arm/mach-at91/clock.c 7 * Copyright (C) 2005 David Brownell 8 * Copyright (C) 2005 Ivan Kokshaysky 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14#include <linux/clk.h> 15#include <linux/err.h> 16#include <linux/export.h> 17#include <linux/device.h> 18#include <linux/string.h> 19#include <linux/list.h> 20 21#include <mach/chip.h> 22 23#include "clock.h" 24 25/* at32 clock list */ 26static LIST_HEAD(at32_clock_list); 27 28static DEFINE_SPINLOCK(clk_lock); 29static DEFINE_SPINLOCK(clk_list_lock); 30 31void at32_clk_register(struct clk *clk) 32{ 33 spin_lock(&clk_list_lock); 34 /* add the new item to the end of the list */ 35 list_add_tail(&clk->list, &at32_clock_list); 36 spin_unlock(&clk_list_lock); 37} 38 39static struct clk *__clk_get(struct device *dev, const char *id) 40{ 41 struct clk *clk; 42 43 list_for_each_entry(clk, &at32_clock_list, list) { 44 if (clk->dev == dev && strcmp(id, clk->name) == 0) { 45 return clk; 46 } 47 } 48 49 return ERR_PTR(-ENOENT); 50} 51 52struct clk *clk_get(struct device *dev, const char *id) 53{ 54 struct clk *clk; 55 56 spin_lock(&clk_list_lock); 57 clk = __clk_get(dev, id); 58 spin_unlock(&clk_list_lock); 59 60 return clk; 61} 62 63EXPORT_SYMBOL(clk_get); 64 65void clk_put(struct clk *clk) 66{ 67 /* clocks are static for now, we can't free them */ 68} 69EXPORT_SYMBOL(clk_put); 70 71static void __clk_enable(struct clk *clk) 72{ 73 if (clk->parent) 74 __clk_enable(clk->parent); 75 if (clk->users++ == 0 && clk->mode) 76 clk->mode(clk, 1); 77} 78 79int clk_enable(struct clk *clk) 80{ 81 unsigned long flags; 82 83 if (!clk) 84 return 0; 85 86 spin_lock_irqsave(&clk_lock, flags); 87 __clk_enable(clk); 88 spin_unlock_irqrestore(&clk_lock, flags); 89 90 return 0; 91} 92EXPORT_SYMBOL(clk_enable); 93 94static void __clk_disable(struct clk *clk) 95{ 96 if (clk->users == 0) { 97 printk(KERN_ERR "%s: mismatched disable\n", clk->name); 98 WARN_ON(1); 99 return; 100 } 101 102 if (--clk->users == 0 && clk->mode) 103 clk->mode(clk, 0); 104 if (clk->parent) 105 __clk_disable(clk->parent); 106} 107 108void clk_disable(struct clk *clk) 109{ 110 unsigned long flags; 111 112 if (IS_ERR_OR_NULL(clk)) 113 return; 114 115 spin_lock_irqsave(&clk_lock, flags); 116 __clk_disable(clk); 117 spin_unlock_irqrestore(&clk_lock, flags); 118} 119EXPORT_SYMBOL(clk_disable); 120 121unsigned long clk_get_rate(struct clk *clk) 122{ 123 unsigned long flags; 124 unsigned long rate; 125 126 if (!clk) 127 return 0; 128 129 spin_lock_irqsave(&clk_lock, flags); 130 rate = clk->get_rate(clk); 131 spin_unlock_irqrestore(&clk_lock, flags); 132 133 return rate; 134} 135EXPORT_SYMBOL(clk_get_rate); 136 137long clk_round_rate(struct clk *clk, unsigned long rate) 138{ 139 unsigned long flags, actual_rate; 140 141 if (!clk) 142 return 0; 143 144 if (!clk->set_rate) 145 return -ENOSYS; 146 147 spin_lock_irqsave(&clk_lock, flags); 148 actual_rate = clk->set_rate(clk, rate, 0); 149 spin_unlock_irqrestore(&clk_lock, flags); 150 151 return actual_rate; 152} 153EXPORT_SYMBOL(clk_round_rate); 154 155int clk_set_rate(struct clk *clk, unsigned long rate) 156{ 157 unsigned long flags; 158 long ret; 159 160 if (!clk) 161 return 0; 162 163 if (!clk->set_rate) 164 return -ENOSYS; 165 166 spin_lock_irqsave(&clk_lock, flags); 167 ret = clk->set_rate(clk, rate, 1); 168 spin_unlock_irqrestore(&clk_lock, flags); 169 170 return (ret < 0) ? ret : 0; 171} 172EXPORT_SYMBOL(clk_set_rate); 173 174int clk_set_parent(struct clk *clk, struct clk *parent) 175{ 176 unsigned long flags; 177 int ret; 178 179 if (!clk) 180 return 0; 181 182 if (!clk->set_parent) 183 return -ENOSYS; 184 185 spin_lock_irqsave(&clk_lock, flags); 186 ret = clk->set_parent(clk, parent); 187 spin_unlock_irqrestore(&clk_lock, flags); 188 189 return ret; 190} 191EXPORT_SYMBOL(clk_set_parent); 192 193struct clk *clk_get_parent(struct clk *clk) 194{ 195 return !clk ? NULL : clk->parent; 196} 197EXPORT_SYMBOL(clk_get_parent); 198 199 200 201#ifdef CONFIG_DEBUG_FS 202 203/* /sys/kernel/debug/at32ap_clk */ 204 205#include <linux/io.h> 206#include <linux/debugfs.h> 207#include <linux/seq_file.h> 208#include "pm.h" 209 210 211#define NEST_DELTA 2 212#define NEST_MAX 6 213 214struct clkinf { 215 struct seq_file *s; 216 unsigned nest; 217}; 218 219static void 220dump_clock(struct clk *parent, struct clkinf *r) 221{ 222 unsigned nest = r->nest; 223 char buf[16 + NEST_MAX]; 224 struct clk *clk; 225 unsigned i; 226 227 /* skip clocks coupled to devices that aren't registered */ 228 if (parent->dev && !dev_name(parent->dev) && !parent->users) 229 return; 230 231 /* <nest spaces> name <pad to end> */ 232 memset(buf, ' ', sizeof(buf) - 1); 233 buf[sizeof(buf) - 1] = 0; 234 i = strlen(parent->name); 235 memcpy(buf + nest, parent->name, 236 min(i, (unsigned)(sizeof(buf) - 1 - nest))); 237 238 seq_printf(r->s, "%s%c users=%2d %-3s %9ld Hz", 239 buf, parent->set_parent ? '*' : ' ', 240 parent->users, 241 parent->users ? "on" : "off", /* NOTE: not-paranoid!! */ 242 clk_get_rate(parent)); 243 if (parent->dev) 244 seq_printf(r->s, ", for %s", dev_name(parent->dev)); 245 seq_printf(r->s, "\n"); 246 247 /* cost of this scan is small, but not linear... */ 248 r->nest = nest + NEST_DELTA; 249 250 list_for_each_entry(clk, &at32_clock_list, list) { 251 if (clk->parent == parent) 252 dump_clock(clk, r); 253 } 254 r->nest = nest; 255} 256 257static int clk_show(struct seq_file *s, void *unused) 258{ 259 struct clkinf r; 260 int i; 261 struct clk *clk; 262 263 /* show all the power manager registers */ 264 seq_printf(s, "MCCTRL = %8x\n", pm_readl(MCCTRL)); 265 seq_printf(s, "CKSEL = %8x\n", pm_readl(CKSEL)); 266 seq_printf(s, "CPUMASK = %8x\n", pm_readl(CPU_MASK)); 267 seq_printf(s, "HSBMASK = %8x\n", pm_readl(HSB_MASK)); 268 seq_printf(s, "PBAMASK = %8x\n", pm_readl(PBA_MASK)); 269 seq_printf(s, "PBBMASK = %8x\n", pm_readl(PBB_MASK)); 270 seq_printf(s, "PLL0 = %8x\n", pm_readl(PLL0)); 271 seq_printf(s, "PLL1 = %8x\n", pm_readl(PLL1)); 272 seq_printf(s, "IMR = %8x\n", pm_readl(IMR)); 273 for (i = 0; i < 8; i++) { 274 if (i == 5) 275 continue; 276 seq_printf(s, "GCCTRL%d = %8x\n", i, pm_readl(GCCTRL(i))); 277 } 278 279 seq_printf(s, "\n"); 280 281 r.s = s; 282 r.nest = 0; 283 /* protected from changes on the list while dumping */ 284 spin_lock(&clk_list_lock); 285 286 /* show clock tree as derived from the three oscillators */ 287 clk = __clk_get(NULL, "osc32k"); 288 dump_clock(clk, &r); 289 clk_put(clk); 290 291 clk = __clk_get(NULL, "osc0"); 292 dump_clock(clk, &r); 293 clk_put(clk); 294 295 clk = __clk_get(NULL, "osc1"); 296 dump_clock(clk, &r); 297 clk_put(clk); 298 299 spin_unlock(&clk_list_lock); 300 301 return 0; 302} 303 304static int clk_open(struct inode *inode, struct file *file) 305{ 306 return single_open(file, clk_show, NULL); 307} 308 309static const struct file_operations clk_operations = { 310 .open = clk_open, 311 .read = seq_read, 312 .llseek = seq_lseek, 313 .release = single_release, 314}; 315 316static int __init clk_debugfs_init(void) 317{ 318 (void) debugfs_create_file("at32ap_clk", S_IFREG | S_IRUGO, 319 NULL, NULL, &clk_operations); 320 321 return 0; 322} 323postcore_initcall(clk_debugfs_init); 324 325#endif 326