aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sh/include/asm/hwblk.h61
-rw-r--r--arch/sh/kernel/cpu/Makefile2
-rw-r--r--arch/sh/kernel/cpu/hwblk.c130
-rw-r--r--arch/sh/kernel/time.c2
4 files changed, 194 insertions, 1 deletions
diff --git a/arch/sh/include/asm/hwblk.h b/arch/sh/include/asm/hwblk.h
new file mode 100644
index 00000000000..51a46f49663
--- /dev/null
+++ b/arch/sh/include/asm/hwblk.h
@@ -0,0 +1,61 @@
1#ifndef __ASM_SH_HWBLK_H
2#define __ASM_SH_HWBLK_H
3
4#include <asm/clock.h>
5#include <asm/io.h>
6
7#define HWBLK_AREA_FLAG_PARENT (1 << 0) /* valid parent */
8
9#define HWBLK_AREA(_flags, _parent) \
10{ \
11 .flags = _flags, \
12 .parent = _parent, \
13}
14
15struct hwblk_area {
16 unsigned long cnt;
17 unsigned char parent;
18 unsigned char flags;
19};
20
21#define HWBLK(_mstp, _bit, _area) \
22{ \
23 .mstp = (void __iomem *)_mstp, \
24 .bit = _bit, \
25 .area = _area, \
26}
27
28struct hwblk {
29 void __iomem *mstp;
30 unsigned char bit;
31 unsigned char area;
32 unsigned long cnt;
33};
34
35struct hwblk_info {
36 struct hwblk_area *areas;
37 int nr_areas;
38 struct hwblk *hwblks;
39 int nr_hwblks;
40};
41
42/* Should be defined by processor-specific code */
43int arch_hwblk_init(void);
44int arch_hwblk_sleep_mode(void);
45
46int hwblk_register(struct hwblk_info *info);
47int hwblk_init(void);
48
49/* allow clocks to enable and disable hardware blocks */
50#define SH_HWBLK_CLK(_name, _id, _parent, _hwblk, _flags) \
51{ \
52 .name = _name, \
53 .id = _id, \
54 .parent = _parent, \
55 .arch_flags = _hwblk, \
56 .flags = _flags, \
57}
58
59int sh_hwblk_clk_register(struct clk *clks, int nr);
60
61#endif /* __ASM_SH_HWBLK_H */
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index eecad7cbd61..3d6b9312dc4 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -19,4 +19,4 @@ obj-$(CONFIG_UBC_WAKEUP) += ubc.o
19obj-$(CONFIG_SH_ADC) += adc.o 19obj-$(CONFIG_SH_ADC) += adc.o
20obj-$(CONFIG_SH_CLK_CPG) += clock-cpg.o 20obj-$(CONFIG_SH_CLK_CPG) += clock-cpg.o
21 21
22obj-y += irq/ init.o clock.o 22obj-y += irq/ init.o clock.o hwblk.o
diff --git a/arch/sh/kernel/cpu/hwblk.c b/arch/sh/kernel/cpu/hwblk.c
new file mode 100644
index 00000000000..7c3a73deff2
--- /dev/null
+++ b/arch/sh/kernel/cpu/hwblk.c
@@ -0,0 +1,130 @@
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
12static 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
22static 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
32static 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
52static 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
72static struct hwblk_info *hwblk_info;
73
74int __init hwblk_register(struct hwblk_info *info)
75{
76 hwblk_info = info;
77 return 0;
78}
79
80int __init __weak arch_hwblk_init(void)
81{
82 return 0;
83}
84
85int __weak arch_hwblk_sleep_mode(void)
86{
87 return SUSP_SH_SLEEP;
88}
89
90int __init hwblk_init(void)
91{
92 return arch_hwblk_init();
93}
94
95/* allow clocks to enable and disable hardware blocks */
96static 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
105static void sh_hwblk_clk_disable(struct clk *clk)
106{
107 if (hwblk_info)
108 hwblk_disable(hwblk_info, clk->arch_flags);
109}
110
111static 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
117int __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}
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c
index 9b352a1e3fb..d2424b068b7 100644
--- a/arch/sh/kernel/time.c
+++ b/arch/sh/kernel/time.c
@@ -21,6 +21,7 @@
21#include <linux/smp.h> 21#include <linux/smp.h>
22#include <linux/rtc.h> 22#include <linux/rtc.h>
23#include <asm/clock.h> 23#include <asm/clock.h>
24#include <asm/hwblk.h>
24#include <asm/rtc.h> 25#include <asm/rtc.h>
25 26
26/* Dummy RTC ops */ 27/* Dummy RTC ops */
@@ -96,6 +97,7 @@ void __init time_init(void)
96 if (board_time_init) 97 if (board_time_init)
97 board_time_init(); 98 board_time_init();
98 99
100 hwblk_init();
99 clk_init(); 101 clk_init();
100 102
101 rtc_sh_get_time(&xtime); 103 rtc_sh_get_time(&xtime);