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/device.h> 17#include <linux/string.h> 18 19#include "clock.h" 20 21static DEFINE_SPINLOCK(clk_lock); 22 23struct clk *clk_get(struct device *dev, const char *id) 24{ 25 int i; 26 27 for (i = 0; i < at32_nr_clocks; i++) { 28 struct clk *clk = at32_clock_list[i]; 29 30 if (clk->dev == dev && strcmp(id, clk->name) == 0) 31 return clk; 32 } 33 34 return ERR_PTR(-ENOENT); 35} 36EXPORT_SYMBOL(clk_get); 37 38void clk_put(struct clk *clk) 39{ 40 /* clocks are static for now, we can't free them */ 41} 42EXPORT_SYMBOL(clk_put); 43 44static void __clk_enable(struct clk *clk) 45{ 46 if (clk->parent) 47 __clk_enable(clk->parent); 48 if (clk->users++ == 0 && clk->mode) 49 clk->mode(clk, 1); 50} 51 52int clk_enable(struct clk *clk) 53{ 54 unsigned long flags; 55 56 spin_lock_irqsave(&clk_lock, flags); 57 __clk_enable(clk); 58 spin_unlock_irqrestore(&clk_lock, flags); 59 60 return 0; 61} 62EXPORT_SYMBOL(clk_enable); 63 64static void __clk_disable(struct clk *clk) 65{ 66 if (clk->users == 0) { 67 printk(KERN_ERR "%s: mismatched disable\n", clk->name); 68 WARN_ON(1); 69 return; 70 } 71 72 if (--clk->users == 0 && clk->mode) 73 clk->mode(clk, 0); 74 if (clk->parent) 75 __clk_disable(clk->parent); 76} 77 78void clk_disable(struct clk *clk) 79{ 80 unsigned long flags; 81 82 spin_lock_irqsave(&clk_lock, flags); 83 __clk_disable(clk); 84 spin_unlock_irqrestore(&clk_lock, flags); 85} 86EXPORT_SYMBOL(clk_disable); 87 88unsigned long clk_get_rate(struct clk *clk) 89{ 90 unsigned long flags; 91 unsigned long rate; 92 93 spin_lock_irqsave(&clk_lock, flags); 94 rate = clk->get_rate(clk); 95 spin_unlock_irqrestore(&clk_lock, flags); 96 97 return rate; 98} 99EXPORT_SYMBOL(clk_get_rate); 100 101long clk_round_rate(struct clk *clk, unsigned long rate) 102{ 103 unsigned long flags, actual_rate; 104 105 if (!clk->set_rate) 106 return -ENOSYS; 107 108 spin_lock_irqsave(&clk_lock, flags); 109 actual_rate = clk->set_rate(clk, rate, 0); 110 spin_unlock_irqrestore(&clk_lock, flags); 111 112 return actual_rate; 113} 114EXPORT_SYMBOL(clk_round_rate); 115 116int clk_set_rate(struct clk *clk, unsigned long rate) 117{ 118 unsigned long flags; 119 long ret; 120 121 if (!clk->set_rate) 122 return -ENOSYS; 123 124 spin_lock_irqsave(&clk_lock, flags); 125 ret = clk->set_rate(clk, rate, 1); 126 spin_unlock_irqrestore(&clk_lock, flags); 127 128 return (ret < 0) ? ret : 0; 129} 130EXPORT_SYMBOL(clk_set_rate); 131 132int clk_set_parent(struct clk *clk, struct clk *parent) 133{ 134 unsigned long flags; 135 int ret; 136 137 if (!clk->set_parent) 138 return -ENOSYS; 139 140 spin_lock_irqsave(&clk_lock, flags); 141 ret = clk->set_parent(clk, parent); 142 spin_unlock_irqrestore(&clk_lock, flags); 143 144 return ret; 145} 146EXPORT_SYMBOL(clk_set_parent); 147 148struct clk *clk_get_parent(struct clk *clk) 149{ 150 return clk->parent; 151} 152EXPORT_SYMBOL(clk_get_parent); 153