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