Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6.git] / arch / sh / kernel / cpu / hwblk.c
1 #include <linux/clk.h>
2 #include <linux/compiler.h>
3 #include <linux/slab.h>
4 #include <linux/io.h>
5 #include <linux/spinlock.h>
6 #include <asm/suspend.h>
7 #include <asm/hwblk.h>
8 #include <asm/clock.h>
9
10 static DEFINE_SPINLOCK(hwblk_lock);
11
12 static void hwblk_area_mod_cnt(struct hwblk_info *info,
13                                int area, int counter, int value, int goal)
14 {
15         struct hwblk_area *hap = info->areas + area;
16
17         hap->cnt[counter] += value;
18
19         if (hap->cnt[counter] != goal)
20                 return;
21
22         if (hap->flags & HWBLK_AREA_FLAG_PARENT)
23                 hwblk_area_mod_cnt(info, hap->parent, counter, value, goal);
24 }
25
26
27 static int __hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
28                           int counter, int value, int goal)
29 {
30         struct hwblk *hp = info->hwblks + hwblk;
31
32         hp->cnt[counter] += value;
33         if (hp->cnt[counter] == goal)
34                 hwblk_area_mod_cnt(info, hp->area, counter, value, goal);
35
36         return hp->cnt[counter];
37 }
38
39 static void hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
40                           int counter, int value, int goal)
41 {
42         unsigned long flags;
43
44         spin_lock_irqsave(&hwblk_lock, flags);
45         __hwblk_mod_cnt(info, hwblk, counter, value, goal);
46         spin_unlock_irqrestore(&hwblk_lock, flags);
47 }
48
49 void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter)
50 {
51         hwblk_mod_cnt(info, hwblk, counter, 1, 1);
52 }
53
54 void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter)
55 {
56         hwblk_mod_cnt(info, hwblk, counter, -1, 0);
57 }
58
59 void hwblk_enable(struct hwblk_info *info, int hwblk)
60 {
61         struct hwblk *hp = info->hwblks + hwblk;
62         unsigned long tmp;
63         unsigned long flags;
64         int ret;
65
66         spin_lock_irqsave(&hwblk_lock, flags);
67
68         ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1);
69         if (ret == 1) {
70                 tmp = __raw_readl(hp->mstp);
71                 tmp &= ~(1 << hp->bit);
72                 __raw_writel(tmp, hp->mstp);
73         }
74
75         spin_unlock_irqrestore(&hwblk_lock, flags);
76 }
77
78 void hwblk_disable(struct hwblk_info *info, int hwblk)
79 {
80         struct hwblk *hp = info->hwblks + hwblk;
81         unsigned long tmp;
82         unsigned long flags;
83         int ret;
84
85         spin_lock_irqsave(&hwblk_lock, flags);
86
87         ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0);
88         if (ret == 0) {
89                 tmp = __raw_readl(hp->mstp);
90                 tmp |= 1 << hp->bit;
91                 __raw_writel(tmp, hp->mstp);
92         }
93
94         spin_unlock_irqrestore(&hwblk_lock, flags);
95 }
96
97 struct hwblk_info *hwblk_info;
98
99 int __init hwblk_register(struct hwblk_info *info)
100 {
101         hwblk_info = info;
102         return 0;
103 }
104
105 int __init __weak arch_hwblk_init(void)
106 {
107         return 0;
108 }
109
110 int __weak arch_hwblk_sleep_mode(void)
111 {
112         return SUSP_SH_SLEEP;
113 }
114
115 int __init hwblk_init(void)
116 {
117         return arch_hwblk_init();
118 }
119
120 /* allow clocks to enable and disable hardware blocks */
121 static int sh_hwblk_clk_enable(struct clk *clk)
122 {
123         if (!hwblk_info)
124                 return -ENOENT;
125
126         hwblk_enable(hwblk_info, clk->arch_flags);
127         return 0;
128 }
129
130 static void sh_hwblk_clk_disable(struct clk *clk)
131 {
132         if (hwblk_info)
133                 hwblk_disable(hwblk_info, clk->arch_flags);
134 }
135
136 static struct clk_ops sh_hwblk_clk_ops = {
137         .enable         = sh_hwblk_clk_enable,
138         .disable        = sh_hwblk_clk_disable,
139         .recalc         = followparent_recalc,
140 };
141
142 int __init sh_hwblk_clk_register(struct clk *clks, int nr)
143 {
144         struct clk *clkp;
145         int ret = 0;
146         int k;
147
148         for (k = 0; !ret && (k < nr); k++) {
149                 clkp = clks + k;
150                 clkp->ops = &sh_hwblk_clk_ops;
151                 ret |= clk_register(clkp);
152         }
153
154         return ret;
155 }