Merge branches 'sh/ftrace' and 'sh/stable-updates'
[pandora-kernel.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_inc(struct hwblk_info *info, int area)
13 {
14         struct hwblk_area *hap = info->areas + area;
15
16         hap->cnt++;
17         if (hap->cnt == 1)
18                 if (hap->flags & HWBLK_AREA_FLAG_PARENT)
19                         hwblk_area_inc(info, hap->parent);
20 }
21
22 static void hwblk_area_dec(struct hwblk_info *info, int area)
23 {
24         struct hwblk_area *hap = info->areas + area;
25
26         if (hap->cnt == 1)
27                 if (hap->flags & HWBLK_AREA_FLAG_PARENT)
28                         hwblk_area_dec(info, hap->parent);
29         hap->cnt--;
30 }
31
32 static void hwblk_enable(struct hwblk_info *info, int hwblk)
33 {
34         struct hwblk *hp = info->hwblks + hwblk;
35         unsigned long tmp;
36         unsigned long flags;
37
38         spin_lock_irqsave(&hwblk_lock, flags);
39
40         hp->cnt++;
41         if (hp->cnt == 1) {
42                 hwblk_area_inc(info, hp->area);
43
44                 tmp = __raw_readl(hp->mstp);
45                 tmp &= ~(1 << hp->bit);
46                 __raw_writel(tmp, hp->mstp);
47         }
48
49         spin_unlock_irqrestore(&hwblk_lock, flags);
50 }
51
52 static void hwblk_disable(struct hwblk_info *info, int hwblk)
53 {
54         struct hwblk *hp = info->hwblks + hwblk;
55         unsigned long tmp;
56         unsigned long flags;
57
58         spin_lock_irqsave(&hwblk_lock, flags);
59
60         if (hp->cnt == 1) {
61                 hwblk_area_dec(info, hp->area);
62
63                 tmp = __raw_readl(hp->mstp);
64                 tmp |= 1 << hp->bit;
65                 __raw_writel(tmp, hp->mstp);
66         }
67         hp->cnt--;
68
69         spin_unlock_irqrestore(&hwblk_lock, flags);
70 }
71
72 static struct hwblk_info *hwblk_info;
73
74 int __init hwblk_register(struct hwblk_info *info)
75 {
76         hwblk_info = info;
77         return 0;
78 }
79
80 int __init __weak arch_hwblk_init(void)
81 {
82         return 0;
83 }
84
85 int __weak arch_hwblk_sleep_mode(void)
86 {
87         return SUSP_SH_SLEEP;
88 }
89
90 int __init hwblk_init(void)
91 {
92         return arch_hwblk_init();
93 }
94
95 /* allow clocks to enable and disable hardware blocks */
96 static int sh_hwblk_clk_enable(struct clk *clk)
97 {
98         if (!hwblk_info)
99                 return -ENOENT;
100
101         hwblk_enable(hwblk_info, clk->arch_flags);
102         return 0;
103 }
104
105 static void sh_hwblk_clk_disable(struct clk *clk)
106 {
107         if (hwblk_info)
108                 hwblk_disable(hwblk_info, clk->arch_flags);
109 }
110
111 static struct clk_ops sh_hwblk_clk_ops = {
112         .enable         = sh_hwblk_clk_enable,
113         .disable        = sh_hwblk_clk_disable,
114         .recalc         = followparent_recalc,
115 };
116
117 int __init sh_hwblk_clk_register(struct clk *clks, int nr)
118 {
119         struct clk *clkp;
120         int ret = 0;
121         int k;
122
123         for (k = 0; !ret && (k < nr); k++) {
124                 clkp = clks + k;
125                 clkp->ops = &sh_hwblk_clk_ops;
126                 ret |= clk_register(clkp);
127         }
128
129         return ret;
130 }