Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[linux-block.git] / arch / sh / kernel / cpu / hwblk.c
CommitLineData
79714acb
MD
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
10static DEFINE_SPINLOCK(hwblk_lock);
11
0f8ee187
MD
12static void hwblk_area_mod_cnt(struct hwblk_info *info,
13 int area, int counter, int value, int goal)
79714acb
MD
14{
15 struct hwblk_area *hap = info->areas + area;
16
0f8ee187
MD
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);
79714acb
MD
24}
25
0f8ee187
MD
26
27static int __hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
28 int counter, int value, int goal)
79714acb 29{
0f8ee187
MD
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);
79714acb 35
0f8ee187 36 return hp->cnt[counter];
79714acb
MD
37}
38
0f8ee187
MD
39static 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
49void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter)
50{
51 hwblk_mod_cnt(info, hwblk, counter, 1, 1);
52}
53
54void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter)
55{
56 hwblk_mod_cnt(info, hwblk, counter, -1, 0);
57}
58
59void hwblk_enable(struct hwblk_info *info, int hwblk)
79714acb
MD
60{
61 struct hwblk *hp = info->hwblks + hwblk;
62 unsigned long tmp;
63 unsigned long flags;
0f8ee187 64 int ret;
79714acb
MD
65
66 spin_lock_irqsave(&hwblk_lock, flags);
67
0f8ee187
MD
68 ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1);
69 if (ret == 1) {
79714acb
MD
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
0f8ee187 78void hwblk_disable(struct hwblk_info *info, int hwblk)
79714acb
MD
79{
80 struct hwblk *hp = info->hwblks + hwblk;
81 unsigned long tmp;
82 unsigned long flags;
0f8ee187 83 int ret;
79714acb
MD
84
85 spin_lock_irqsave(&hwblk_lock, flags);
86
0f8ee187
MD
87 ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0);
88 if (ret == 0) {
79714acb
MD
89 tmp = __raw_readl(hp->mstp);
90 tmp |= 1 << hp->bit;
91 __raw_writel(tmp, hp->mstp);
92 }
79714acb
MD
93
94 spin_unlock_irqrestore(&hwblk_lock, flags);
95}
96
0f8ee187 97struct hwblk_info *hwblk_info;
79714acb
MD
98
99int __init hwblk_register(struct hwblk_info *info)
100{
101 hwblk_info = info;
102 return 0;
103}
104
105int __init __weak arch_hwblk_init(void)
106{
107 return 0;
108}
109
110int __weak arch_hwblk_sleep_mode(void)
111{
112 return SUSP_SH_SLEEP;
113}
114
115int __init hwblk_init(void)
116{
117 return arch_hwblk_init();
118}
119
120/* allow clocks to enable and disable hardware blocks */
121static 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
130static void sh_hwblk_clk_disable(struct clk *clk)
131{
132 if (hwblk_info)
133 hwblk_disable(hwblk_info, clk->arch_flags);
134}
135
136static 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
142int __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}