• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * r8a7779 Power management support
3  *
4  * Copyright (C) 2011  Renesas Solutions Corp.
5  * Copyright (C) 2011  Magnus Damm
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file "COPYING" in the main directory of this archive
9  * for more details.
10  */
11 
12 #include <linux/pm.h>
13 #include <linux/suspend.h>
14 #include <linux/err.h>
15 #include <linux/pm_clock.h>
16 #include <linux/platform_device.h>
17 #include <linux/delay.h>
18 #include <linux/irq.h>
19 #include <linux/interrupt.h>
20 #include <linux/console.h>
21 #include <asm/io.h>
22 #include <mach/common.h>
23 #include <mach/r8a7779.h>
24 
25 static void __iomem *r8a7779_sysc_base;
26 
27 /* SYSC */
28 #define SYSCSR 0x00
29 #define SYSCISR 0x04
30 #define SYSCISCR 0x08
31 #define SYSCIER 0x0c
32 #define SYSCIMR 0x10
33 #define PWRSR0 0x40
34 #define PWRSR1 0x80
35 #define PWRSR2 0xc0
36 #define PWRSR3 0x100
37 #define PWRSR4 0x140
38 
39 #define PWRSR_OFFS 0x00
40 #define PWROFFCR_OFFS 0x04
41 #define PWRONCR_OFFS 0x0c
42 #define PWRER_OFFS 0x14
43 
44 #define SYSCSR_RETRIES 100
45 #define SYSCSR_DELAY_US 1
46 
47 #define SYSCISR_RETRIES 1000
48 #define SYSCISR_DELAY_US 1
49 
50 #if defined(CONFIG_PM) || defined(CONFIG_SMP)
51 
52 static DEFINE_SPINLOCK(r8a7779_sysc_lock); /* SMP CPUs + I/O devices */
53 
r8a7779_sysc_pwr_on_off(struct r8a7779_pm_ch * r8a7779_ch,int sr_bit,int reg_offs)54 static int r8a7779_sysc_pwr_on_off(struct r8a7779_pm_ch *r8a7779_ch,
55 				   int sr_bit, int reg_offs)
56 {
57 	int k;
58 
59 	for (k = 0; k < SYSCSR_RETRIES; k++) {
60 		if (ioread32(r8a7779_sysc_base + SYSCSR) & (1 << sr_bit))
61 			break;
62 		udelay(SYSCSR_DELAY_US);
63 	}
64 
65 	if (k == SYSCSR_RETRIES)
66 		return -EAGAIN;
67 
68 	iowrite32(1 << r8a7779_ch->chan_bit,
69 		  r8a7779_sysc_base + r8a7779_ch->chan_offs + reg_offs);
70 
71 	return 0;
72 }
73 
r8a7779_sysc_pwr_off(struct r8a7779_pm_ch * r8a7779_ch)74 static int r8a7779_sysc_pwr_off(struct r8a7779_pm_ch *r8a7779_ch)
75 {
76 	return r8a7779_sysc_pwr_on_off(r8a7779_ch, 0, PWROFFCR_OFFS);
77 }
78 
r8a7779_sysc_pwr_on(struct r8a7779_pm_ch * r8a7779_ch)79 static int r8a7779_sysc_pwr_on(struct r8a7779_pm_ch *r8a7779_ch)
80 {
81 	return r8a7779_sysc_pwr_on_off(r8a7779_ch, 1, PWRONCR_OFFS);
82 }
83 
r8a7779_sysc_update(struct r8a7779_pm_ch * r8a7779_ch,int (* on_off_fn)(struct r8a7779_pm_ch *))84 static int r8a7779_sysc_update(struct r8a7779_pm_ch *r8a7779_ch,
85 			       int (*on_off_fn)(struct r8a7779_pm_ch *))
86 {
87 	unsigned int isr_mask = 1 << r8a7779_ch->isr_bit;
88 	unsigned int chan_mask = 1 << r8a7779_ch->chan_bit;
89 	unsigned int status;
90 	unsigned long flags;
91 	int ret = 0;
92 	int k;
93 
94 	spin_lock_irqsave(&r8a7779_sysc_lock, flags);
95 
96 	iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
97 
98 	do {
99 		ret = on_off_fn(r8a7779_ch);
100 		if (ret)
101 			goto out;
102 
103 		status = ioread32(r8a7779_sysc_base +
104 				  r8a7779_ch->chan_offs + PWRER_OFFS);
105 	} while (status & chan_mask);
106 
107 	for (k = 0; k < SYSCISR_RETRIES; k++) {
108 		if (ioread32(r8a7779_sysc_base + SYSCISR) & isr_mask)
109 			break;
110 		udelay(SYSCISR_DELAY_US);
111 	}
112 
113 	if (k == SYSCISR_RETRIES)
114 		ret = -EIO;
115 
116 	iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
117 
118  out:
119 	spin_unlock_irqrestore(&r8a7779_sysc_lock, flags);
120 
121 	pr_debug("r8a7779 power domain %d: %02x %02x %02x %02x %02x -> %d\n",
122 		 r8a7779_ch->isr_bit, ioread32(r8a7779_sysc_base + PWRSR0),
123 		 ioread32(r8a7779_sysc_base + PWRSR1),
124 		 ioread32(r8a7779_sysc_base + PWRSR2),
125 		 ioread32(r8a7779_sysc_base + PWRSR3),
126 		 ioread32(r8a7779_sysc_base + PWRSR4), ret);
127 	return ret;
128 }
129 
r8a7779_sysc_power_down(struct r8a7779_pm_ch * r8a7779_ch)130 int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch)
131 {
132 	return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_off);
133 }
134 
r8a7779_sysc_power_up(struct r8a7779_pm_ch * r8a7779_ch)135 int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch)
136 {
137 	return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_on);
138 }
139 
r8a7779_sysc_init(void)140 static void __init r8a7779_sysc_init(void)
141 {
142 	r8a7779_sysc_base = ioremap_nocache(0xffd85000, PAGE_SIZE);
143 	if (!r8a7779_sysc_base)
144 		panic("unable to ioremap r8a7779 SYSC hardware block\n");
145 
146 	/* enable all interrupt sources, but do not use interrupt handler */
147 	iowrite32(0x0131000e, r8a7779_sysc_base + SYSCIER);
148 	iowrite32(0, r8a7779_sysc_base + SYSCIMR);
149 }
150 
151 #else /* CONFIG_PM || CONFIG_SMP */
152 
r8a7779_sysc_init(void)153 static inline void r8a7779_sysc_init(void) {}
154 
155 #endif /* CONFIG_PM || CONFIG_SMP */
156 
157 #ifdef CONFIG_PM
158 
pd_power_down(struct generic_pm_domain * genpd)159 static int pd_power_down(struct generic_pm_domain *genpd)
160 {
161 	return r8a7779_sysc_power_down(to_r8a7779_ch(genpd));
162 }
163 
pd_power_up(struct generic_pm_domain * genpd)164 static int pd_power_up(struct generic_pm_domain *genpd)
165 {
166 	return r8a7779_sysc_power_up(to_r8a7779_ch(genpd));
167 }
168 
pd_is_off(struct generic_pm_domain * genpd)169 static bool pd_is_off(struct generic_pm_domain *genpd)
170 {
171 	struct r8a7779_pm_ch *r8a7779_ch = to_r8a7779_ch(genpd);
172 	unsigned int st;
173 
174 	st = ioread32(r8a7779_sysc_base + r8a7779_ch->chan_offs + PWRSR_OFFS);
175 	if (st & (1 << r8a7779_ch->chan_bit))
176 		return true;
177 
178 	return false;
179 }
180 
pd_active_wakeup(struct device * dev)181 static bool pd_active_wakeup(struct device *dev)
182 {
183 	return true;
184 }
185 
r8a7779_init_pm_domain(struct r8a7779_pm_domain * r8a7779_pd)186 void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd)
187 {
188 	struct generic_pm_domain *genpd = &r8a7779_pd->genpd;
189 
190 	pm_genpd_init(genpd, NULL, false);
191 	genpd->dev_ops.stop = pm_clk_suspend;
192 	genpd->dev_ops.start = pm_clk_resume;
193 	genpd->dev_ops.active_wakeup = pd_active_wakeup;
194 	genpd->dev_irq_safe = true;
195 	genpd->power_off = pd_power_down;
196 	genpd->power_on = pd_power_up;
197 
198 	if (pd_is_off(&r8a7779_pd->genpd))
199 		pd_power_up(&r8a7779_pd->genpd);
200 }
201 
r8a7779_add_device_to_domain(struct r8a7779_pm_domain * r8a7779_pd,struct platform_device * pdev)202 void r8a7779_add_device_to_domain(struct r8a7779_pm_domain *r8a7779_pd,
203 				 struct platform_device *pdev)
204 {
205 	struct device *dev = &pdev->dev;
206 
207 	pm_genpd_add_device(&r8a7779_pd->genpd, dev);
208 	if (pm_clk_no_clocks(dev))
209 		pm_clk_add(dev, NULL);
210 }
211 
212 struct r8a7779_pm_domain r8a7779_sh4a = {
213 	.ch = {
214 		.chan_offs = 0x80, /* PWRSR1 .. PWRER1 */
215 		.isr_bit = 16, /* SH4A */
216 	}
217 };
218 
219 struct r8a7779_pm_domain r8a7779_sgx = {
220 	.ch = {
221 		.chan_offs = 0xc0, /* PWRSR2 .. PWRER2 */
222 		.isr_bit = 20, /* SGX */
223 	}
224 };
225 
226 struct r8a7779_pm_domain r8a7779_vdp1 = {
227 	.ch = {
228 		.chan_offs = 0x100, /* PWRSR3 .. PWRER3 */
229 		.isr_bit = 21, /* VDP */
230 	}
231 };
232 
233 struct r8a7779_pm_domain r8a7779_impx3 = {
234 	.ch = {
235 		.chan_offs = 0x140, /* PWRSR4 .. PWRER4 */
236 		.isr_bit = 24, /* IMP */
237 	}
238 };
239 
240 #endif /* CONFIG_PM */
241 
r8a7779_pm_init(void)242 void __init r8a7779_pm_init(void)
243 {
244 	static int once;
245 
246 	if (!once++)
247 		r8a7779_sysc_init();
248 }
249