Blame view

arch/arm/mach-shmobile/pm-sh7372.c 13.4 KB
97991657b   Magnus Damm   ARM: mach-shmobil...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * sh7372 Power management support
   *
   *  Copyright (C) 2011 Magnus Damm
   *
   * This file is subject to the terms and conditions of the GNU General Public
   * License.  See the file "COPYING" in the main directory of this archive
   * for more details.
   */
  
  #include <linux/pm.h>
  #include <linux/suspend.h>
082a8ca1d   Magnus Damm   ARM: mach-shmobil...
13
  #include <linux/cpuidle.h>
97991657b   Magnus Damm   ARM: mach-shmobil...
14
15
16
17
  #include <linux/module.h>
  #include <linux/list.h>
  #include <linux/err.h>
  #include <linux/slab.h>
b5e8d269d   Rafael J. Wysocki   PM: Move clock-re...
18
  #include <linux/pm_clock.h>
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
19
20
  #include <linux/platform_device.h>
  #include <linux/delay.h>
cf33835c5   Magnus Damm   ARM: mach-shmobil...
21
22
  #include <linux/irq.h>
  #include <linux/bitrev.h>
056879d2f   Magnus Damm   ARM: mach-shmobil...
23
  #include <linux/console.h>
97991657b   Magnus Damm   ARM: mach-shmobil...
24
25
26
  #include <asm/system.h>
  #include <asm/io.h>
  #include <asm/tlbflush.h>
06b841666   Magnus Damm   ARM: mach-shmobil...
27
  #include <asm/suspend.h>
97991657b   Magnus Damm   ARM: mach-shmobil...
28
  #include <mach/common.h>
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
29
  #include <mach/sh7372.h>
97991657b   Magnus Damm   ARM: mach-shmobil...
30

cf33835c5   Magnus Damm   ARM: mach-shmobil...
31
32
33
  /* DBG */
  #define DBGREG1 0xe6100020
  #define DBGREG9 0xe6100040
97991657b   Magnus Damm   ARM: mach-shmobil...
34

cf33835c5   Magnus Damm   ARM: mach-shmobil...
35
36
37
38
39
40
41
42
43
44
  /* CPGA */
  #define SYSTBCR 0xe6150024
  #define MSTPSR0 0xe6150030
  #define MSTPSR1 0xe6150038
  #define MSTPSR2 0xe6150040
  #define MSTPSR3 0xe6150048
  #define MSTPSR4 0xe615004c
  #define PLLC01STPCR 0xe61500c8
  
  /* SYSC */
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
45
46
  #define SPDCR 0xe6180008
  #define SWUCR 0xe6180014
cf33835c5   Magnus Damm   ARM: mach-shmobil...
47
  #define SBAR 0xe6180020
382414b93   Magnus Damm   ARM: mach-shmobil...
48
  #define WUPRMSK 0xe6180028
cf33835c5   Magnus Damm   ARM: mach-shmobil...
49
50
  #define WUPSMSK 0xe618002c
  #define WUPSMSK2 0xe6180048
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
51
  #define PSTR 0xe6180080
cf33835c5   Magnus Damm   ARM: mach-shmobil...
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  #define WUPSFAC 0xe6180098
  #define IRQCR 0xe618022c
  #define IRQCR2 0xe6180238
  #define IRQCR3 0xe6180244
  #define IRQCR4 0xe6180248
  #define PDNSEL 0xe6180254
  
  /* INTC */
  #define ICR1A 0xe6900000
  #define ICR2A 0xe6900004
  #define ICR3A 0xe6900008
  #define ICR4A 0xe690000c
  #define INTMSK00A 0xe6900040
  #define INTMSK10A 0xe6900044
  #define INTMSK20A 0xe6900048
  #define INTMSK30A 0xe690004c
  
  /* MFIS */
  #define SMFRAM 0xe6a70000
  
  /* AP-System Core */
  #define APARMBAREA 0xe6f10020
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
74
75
76
77
78
79
80
81
82
83
  
  #define PSTR_RETRIES 100
  #define PSTR_DELAY_US 10
  
  #ifdef CONFIG_PM
  
  static int pd_power_down(struct generic_pm_domain *genpd)
  {
  	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
  	unsigned int mask = 1 << sh7372_pd->bit_shift;
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
84
85
  	if (sh7372_pd->suspend) {
  		int ret = sh7372_pd->suspend();
382414b93   Magnus Damm   ARM: mach-shmobil...
86

767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
87
88
89
  		if (ret)
  			return ret;
  	}
382414b93   Magnus Damm   ARM: mach-shmobil...
90

e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
91
92
93
94
95
96
97
98
99
100
101
  	if (__raw_readl(PSTR) & mask) {
  		unsigned int retry_count;
  
  		__raw_writel(mask, SPDCR);
  
  		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
  			if (!(__raw_readl(SPDCR) & mask))
  				break;
  			cpu_relax();
  		}
  	}
d93f5cdea   Magnus Damm   ARM: mach-shmobil...
102
  	if (!sh7372_pd->no_debug)
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
103
104
105
  		pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x
  ",
  			 genpd->name, mask, __raw_readl(PSTR));
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
106
107
108
  
  	return 0;
  }
bc9f54498   Rafael J. Wysocki   PM / shmobile: Av...
109
  static int __pd_power_up(struct sh7372_pm_domain *sh7372_pd, bool do_resume)
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
110
  {
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
111
112
113
114
115
116
117
118
119
120
121
  	unsigned int mask = 1 << sh7372_pd->bit_shift;
  	unsigned int retry_count;
  	int ret = 0;
  
  	if (__raw_readl(PSTR) & mask)
  		goto out;
  
  	__raw_writel(mask, SWUCR);
  
  	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
  		if (!(__raw_readl(SWUCR) & mask))
be2658ffe   Guennadi Liakhovetski   PM / shmobile: Do...
122
  			break;
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
123
124
125
126
127
  		if (retry_count > PSTR_RETRIES)
  			udelay(PSTR_DELAY_US);
  		else
  			cpu_relax();
  	}
be2658ffe   Guennadi Liakhovetski   PM / shmobile: Do...
128
  	if (!retry_count)
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
129
  		ret = -EIO;
d93f5cdea   Magnus Damm   ARM: mach-shmobil...
130
  	if (!sh7372_pd->no_debug)
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
131
132
133
  		pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x
  ",
  			 sh7372_pd->genpd.name, mask, __raw_readl(PSTR));
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
134

382414b93   Magnus Damm   ARM: mach-shmobil...
135
   out:
bc9f54498   Rafael J. Wysocki   PM / shmobile: Av...
136
  	if (ret == 0 && sh7372_pd->resume && do_resume)
382414b93   Magnus Damm   ARM: mach-shmobil...
137
  		sh7372_pd->resume();
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
138
139
  	return ret;
  }
bc9f54498   Rafael J. Wysocki   PM / shmobile: Av...
140
141
142
143
  static int pd_power_up(struct generic_pm_domain *genpd)
  {
  	 return __pd_power_up(to_sh7372_pd(genpd), true);
  }
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
144
  static int sh7372_a4r_suspend(void)
382414b93   Magnus Damm   ARM: mach-shmobil...
145
146
147
  {
  	sh7372_intcs_suspend();
  	__raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
148
  	return 0;
382414b93   Magnus Damm   ARM: mach-shmobil...
149
  }
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
150
151
  static bool pd_active_wakeup(struct device *dev)
  {
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
152
153
154
155
  	bool (*active_wakeup)(struct device *dev);
  
  	active_wakeup = dev_gpd_data(dev)->ops.active_wakeup;
  	return active_wakeup ? active_wakeup(dev) : true;
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
156
  }
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  static int sh7372_stop_dev(struct device *dev)
  {
  	int (*stop)(struct device *dev);
  
  	stop = dev_gpd_data(dev)->ops.stop;
  	if (stop) {
  		int ret = stop(dev);
  		if (ret)
  			return ret;
  	}
  	return pm_clk_suspend(dev);
  }
  
  static int sh7372_start_dev(struct device *dev)
  {
  	int (*start)(struct device *dev);
  	int ret;
  
  	ret = pm_clk_resume(dev);
  	if (ret)
  		return ret;
  
  	start = dev_gpd_data(dev)->ops.start;
  	if (start)
  		ret = start(dev);
  
  	return ret;
  }
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
185
186
187
  void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
  {
  	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
188
  	struct dev_power_governor *gov = sh7372_pd->gov;
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
189

b02c999ac   Rafael J. Wysocki   PM / Domains: Add...
190
  	pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
d5e4cbfe2   Rafael J. Wysocki   PM / Domains: Mak...
191
192
193
  	genpd->dev_ops.stop = sh7372_stop_dev;
  	genpd->dev_ops.start = sh7372_start_dev;
  	genpd->dev_ops.active_wakeup = pd_active_wakeup;
0aa2a2216   Rafael J. Wysocki   PM / Domains: Pre...
194
  	genpd->dev_irq_safe = true;
111058c3f   Rafael J. Wysocki   ARM / shmobile: M...
195
196
  	genpd->power_off = pd_power_down;
  	genpd->power_on = pd_power_up;
bc9f54498   Rafael J. Wysocki   PM / shmobile: Av...
197
  	__pd_power_up(sh7372_pd, false);
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
198
199
200
201
202
203
  }
  
  void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
  				 struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
204
  	pm_genpd_add_device(&sh7372_pd->genpd, dev);
4605ab653   Rafael J. Wysocki   PM / Domains: Use...
205
206
  	if (pm_clk_no_clocks(dev))
  		pm_clk_add(dev, NULL);
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
207
  }
111058c3f   Rafael J. Wysocki   ARM / shmobile: M...
208
209
210
211
212
  void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd,
  			     struct sh7372_pm_domain *sh7372_sd)
  {
  	pm_genpd_add_subdomain(&sh7372_pd->genpd, &sh7372_sd->genpd);
  }
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
213
  struct sh7372_pm_domain sh7372_a4lc = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
214
  	.genpd.name = "A4LC",
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
215
216
  	.bit_shift = 1,
  };
c1ba5bb56   Kuninori Morimoto   ARM: mach-shmobil...
217
  struct sh7372_pm_domain sh7372_a4mp = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
218
  	.genpd.name = "A4MP",
c1ba5bb56   Kuninori Morimoto   ARM: mach-shmobil...
219
220
  	.bit_shift = 2,
  };
d24771dec   Magnus Damm   ARM: mach-shmobil...
221
  struct sh7372_pm_domain sh7372_d4 = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
222
  	.genpd.name = "D4",
d24771dec   Magnus Damm   ARM: mach-shmobil...
223
224
  	.bit_shift = 3,
  };
382414b93   Magnus Damm   ARM: mach-shmobil...
225
  struct sh7372_pm_domain sh7372_a4r = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
226
  	.genpd.name = "A4R",
382414b93   Magnus Damm   ARM: mach-shmobil...
227
  	.bit_shift = 5,
382414b93   Magnus Damm   ARM: mach-shmobil...
228
229
  	.suspend = sh7372_a4r_suspend,
  	.resume = sh7372_intcs_resume,
382414b93   Magnus Damm   ARM: mach-shmobil...
230
  };
33afebf3d   Magnus Damm   ARM: mach-shmobil...
231
  struct sh7372_pm_domain sh7372_a3rv = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
232
  	.genpd.name = "A3RV",
33afebf3d   Magnus Damm   ARM: mach-shmobil...
233
234
  	.bit_shift = 6,
  };
082517aa2   Magnus Damm   ARM: mach-shmobil...
235
  struct sh7372_pm_domain sh7372_a3ri = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
236
  	.genpd.name = "A3RI",
082517aa2   Magnus Damm   ARM: mach-shmobil...
237
238
  	.bit_shift = 8,
  };
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
239
240
241
242
243
244
245
246
  static int sh7372_a4s_suspend(void)
  {
  	/*
  	 * The A4S domain contains the CPU core and therefore it should
  	 * only be turned off if the CPU is in use.
  	 */
  	return -EBUSY;
  }
f7dadb379   Magnus Damm   PM / shmobile: Ad...
247
248
249
250
251
  struct sh7372_pm_domain sh7372_a4s = {
  	.genpd.name = "A4S",
  	.bit_shift = 10,
  	.gov = &pm_domain_always_on_gov,
  	.no_debug = true,
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
252
  	.suspend = sh7372_a4s_suspend,
f7dadb379   Magnus Damm   PM / shmobile: Ad...
253
  };
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
254
255
256
257
258
259
  static int sh7372_a3sp_suspend(void)
  {
  	/*
  	 * Serial consoles make use of SCIF hardware located in A3SP,
  	 * keep such power domain on if "no_console_suspend" is set.
  	 */
a029db439   Guennadi Liakhovetski   PM / shmobile: fi...
260
  	return console_suspend_enabled ? 0 : -EBUSY;
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
261
  }
d93f5cdea   Magnus Damm   ARM: mach-shmobil...
262
  struct sh7372_pm_domain sh7372_a3sp = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
263
  	.genpd.name = "A3SP",
d93f5cdea   Magnus Damm   ARM: mach-shmobil...
264
  	.bit_shift = 11,
a87dc8fdc   Mark Brown   PM / shmobile: Us...
265
  	.gov = &pm_domain_always_on_gov,
d93f5cdea   Magnus Damm   ARM: mach-shmobil...
266
  	.no_debug = true,
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
267
  	.suspend = sh7372_a3sp_suspend,
d93f5cdea   Magnus Damm   ARM: mach-shmobil...
268
  };
c47586b6d   Magnus Damm   ARM: mach-shmobil...
269
  struct sh7372_pm_domain sh7372_a3sg = {
e84b2c202   Rafael J. Wysocki   PM / Domains: Mak...
270
  	.genpd.name = "A3SG",
c47586b6d   Magnus Damm   ARM: mach-shmobil...
271
272
  	.bit_shift = 13,
  };
88995e809   Rafael J. Wysocki   PM / shmobile: Fi...
273
274
275
276
277
  #else /* !CONFIG_PM */
  
  static inline void sh7372_a3sp_init(void) {}
  
  #endif /* !CONFIG_PM */
e3e010913   Rafael J. Wysocki   ARM / shmobile: S...
278

a0089bd61   Magnus Damm   ARM: mach-shmobil...
279
  #if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
06b841666   Magnus Damm   ARM: mach-shmobil...
280
  static int sh7372_do_idle_core_standby(unsigned long unused)
97991657b   Magnus Damm   ARM: mach-shmobil...
281
  {
06b841666   Magnus Damm   ARM: mach-shmobil...
282
283
284
  	cpu_do_idle(); /* WFI when SYSTBCR == 0x10 -> Core Standby */
  	return 0;
  }
97991657b   Magnus Damm   ARM: mach-shmobil...
285

f7dadb379   Magnus Damm   PM / shmobile: Ad...
286
  static void sh7372_set_reset_vector(unsigned long address)
06b841666   Magnus Damm   ARM: mach-shmobil...
287
288
  {
  	/* set reset vector, translate 4k */
f7dadb379   Magnus Damm   PM / shmobile: Ad...
289
  	__raw_writel(address, SBAR);
06b841666   Magnus Damm   ARM: mach-shmobil...
290
  	__raw_writel(0, APARMBAREA);
f7dadb379   Magnus Damm   PM / shmobile: Ad...
291
292
293
294
295
  }
  
  static void sh7372_enter_core_standby(void)
  {
  	sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc));
97991657b   Magnus Damm   ARM: mach-shmobil...
296

06b841666   Magnus Damm   ARM: mach-shmobil...
297
298
299
300
  	/* enter sleep mode with SYSTBCR to 0x10 */
  	__raw_writel(0x10, SYSTBCR);
  	cpu_suspend(0, sh7372_do_idle_core_standby);
  	__raw_writel(0, SYSTBCR);
97991657b   Magnus Damm   ARM: mach-shmobil...
301

06b841666   Magnus Damm   ARM: mach-shmobil...
302
303
  	 /* disable reset vector translation */
  	__raw_writel(0, SBAR);
97991657b   Magnus Damm   ARM: mach-shmobil...
304
  }
a0089bd61   Magnus Damm   ARM: mach-shmobil...
305
  #endif
97991657b   Magnus Damm   ARM: mach-shmobil...
306

a0089bd61   Magnus Damm   ARM: mach-shmobil...
307
  #ifdef CONFIG_SUSPEND
f7dadb379   Magnus Damm   PM / shmobile: Ad...
308
  static void sh7372_enter_sysc(int pllc0_on, unsigned long sleep_mode)
cf33835c5   Magnus Damm   ARM: mach-shmobil...
309
  {
cf33835c5   Magnus Damm   ARM: mach-shmobil...
310
311
312
313
  	if (pllc0_on)
  		__raw_writel(0, PLLC01STPCR);
  	else
  		__raw_writel(1 << 28, PLLC01STPCR);
cf33835c5   Magnus Damm   ARM: mach-shmobil...
314
  	__raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */
f7dadb379   Magnus Damm   PM / shmobile: Ad...
315
  	cpu_suspend(sleep_mode, sh7372_do_idle_sysc);
cf33835c5   Magnus Damm   ARM: mach-shmobil...
316
317
318
319
320
  	__raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */
  
  	 /* disable reset vector translation */
  	__raw_writel(0, SBAR);
  }
f7dadb379   Magnus Damm   PM / shmobile: Ad...
321
  static int sh7372_sysc_valid(unsigned long *mskp, unsigned long *msk2p)
cf33835c5   Magnus Damm   ARM: mach-shmobil...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
  {
  	unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4;
  	unsigned long msk, msk2;
  
  	/* check active clocks to determine potential wakeup sources */
  
  	mstpsr0 = __raw_readl(MSTPSR0);
  	if ((mstpsr0 & 0x00000003) != 0x00000003) {
  		pr_debug("sh7372 mstpsr0 0x%08lx
  ", mstpsr0);
  		return 0;
  	}
  
  	mstpsr1 = __raw_readl(MSTPSR1);
  	if ((mstpsr1 & 0xff079b7f) != 0xff079b7f) {
  		pr_debug("sh7372 mstpsr1 0x%08lx
  ", mstpsr1);
  		return 0;
  	}
  
  	mstpsr2 = __raw_readl(MSTPSR2);
  	if ((mstpsr2 & 0x000741ff) != 0x000741ff) {
  		pr_debug("sh7372 mstpsr2 0x%08lx
  ", mstpsr2);
  		return 0;
  	}
  
  	mstpsr3 = __raw_readl(MSTPSR3);
  	if ((mstpsr3 & 0x1a60f010) != 0x1a60f010) {
  		pr_debug("sh7372 mstpsr3 0x%08lx
  ", mstpsr3);
  		return 0;
  	}
  
  	mstpsr4 = __raw_readl(MSTPSR4);
  	if ((mstpsr4 & 0x00008cf0) != 0x00008cf0) {
  		pr_debug("sh7372 mstpsr4 0x%08lx
  ", mstpsr4);
  		return 0;
  	}
  
  	msk = 0;
  	msk2 = 0;
  
  	/* make bitmaps of limited number of wakeup sources */
  
  	if ((mstpsr2 & (1 << 23)) == 0) /* SPU2 */
  		msk |= 1 << 31;
  
  	if ((mstpsr2 & (1 << 12)) == 0) /* MFI_MFIM */
  		msk |= 1 << 21;
  
  	if ((mstpsr4 & (1 << 3)) == 0) /* KEYSC */
  		msk |= 1 << 2;
  
  	if ((mstpsr1 & (1 << 24)) == 0) /* CMT0 */
  		msk |= 1 << 1;
  
  	if ((mstpsr3 & (1 << 29)) == 0) /* CMT1 */
  		msk |= 1 << 1;
  
  	if ((mstpsr4 & (1 << 0)) == 0) /* CMT2 */
  		msk |= 1 << 1;
  
  	if ((mstpsr2 & (1 << 13)) == 0) /* MFI_MFIS */
  		msk2 |= 1 << 17;
  
  	*mskp = msk;
  	*msk2p = msk2;
  
  	return 1;
  }
  
  static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
  {
  	u16 tmp, irqcr1, irqcr2;
  	int k;
  
  	irqcr1 = 0;
  	irqcr2 = 0;
  
  	/* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
  	for (k = 0; k <= 7; k++) {
  		tmp = (icr >> ((7 - k) * 4)) & 0xf;
  		irqcr1 |= (tmp & 0x03) << (k * 2);
  		irqcr2 |= (tmp >> 2) << (k * 2);
  	}
  
  	*irqcr1p = irqcr1;
  	*irqcr2p = irqcr2;
  }
f7dadb379   Magnus Damm   PM / shmobile: Ad...
413
  static void sh7372_setup_sysc(unsigned long msk, unsigned long msk2)
cf33835c5   Magnus Damm   ARM: mach-shmobil...
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
  {
  	u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
  	unsigned long tmp;
  
  	/* read IRQ0A -> IRQ15A mask */
  	tmp = bitrev8(__raw_readb(INTMSK00A));
  	tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
  
  	/* setup WUPSMSK from clocks and external IRQ mask */
  	msk = (~msk & 0xc030000f) | (tmp << 4);
  	__raw_writel(msk, WUPSMSK);
  
  	/* propage level/edge trigger for external IRQ 0->15 */
  	sh7372_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
  	sh7372_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
  	__raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
  	__raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
  
  	/* read IRQ16A -> IRQ31A mask */
  	tmp = bitrev8(__raw_readb(INTMSK20A));
  	tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
  
  	/* setup WUPSMSK2 from clocks and external IRQ mask */
  	msk2 = (~msk2 & 0x00030000) | tmp;
  	__raw_writel(msk2, WUPSMSK2);
  
  	/* propage level/edge trigger for external IRQ 16->31 */
  	sh7372_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
  	sh7372_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
  	__raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
  	__raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
  }
f7dadb379   Magnus Damm   PM / shmobile: Ad...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  
  static void sh7372_enter_a3sm_common(int pllc0_on)
  {
  	sh7372_set_reset_vector(__pa(sh7372_resume_core_standby_sysc));
  	sh7372_enter_sysc(pllc0_on, 1 << 12);
  }
  
  static void sh7372_enter_a4s_common(int pllc0_on)
  {
  	sh7372_intca_suspend();
  	memcpy((void *)SMFRAM, sh7372_resume_core_standby_sysc, 0x100);
  	sh7372_set_reset_vector(SMFRAM);
  	sh7372_enter_sysc(pllc0_on, 1 << 10);
  	sh7372_intca_resume();
  }
a0089bd61   Magnus Damm   ARM: mach-shmobil...
461
  #endif
cf33835c5   Magnus Damm   ARM: mach-shmobil...
462

082a8ca1d   Magnus Damm   ARM: mach-shmobil...
463
  #ifdef CONFIG_CPU_IDLE
cf33835c5   Magnus Damm   ARM: mach-shmobil...
464

b73b5c493   Magnus Damm   ARM: mach-shmobil...
465
  static void sh7372_cpuidle_setup(struct cpuidle_driver *drv)
082a8ca1d   Magnus Damm   ARM: mach-shmobil...
466
  {
b73b5c493   Magnus Damm   ARM: mach-shmobil...
467
  	struct cpuidle_state *state = &drv->states[drv->state_count];
082a8ca1d   Magnus Damm   ARM: mach-shmobil...
468

082a8ca1d   Magnus Damm   ARM: mach-shmobil...
469
470
471
472
  	snprintf(state->name, CPUIDLE_NAME_LEN, "C2");
  	strncpy(state->desc, "Core Standby Mode", CPUIDLE_DESC_LEN);
  	state->exit_latency = 10;
  	state->target_residency = 20 + 10;
b73b5c493   Magnus Damm   ARM: mach-shmobil...
473
474
  	state->flags = CPUIDLE_FLAG_TIME_VALID;
  	shmobile_cpuidle_modes[drv->state_count] = sh7372_enter_core_standby;
082a8ca1d   Magnus Damm   ARM: mach-shmobil...
475

b73b5c493   Magnus Damm   ARM: mach-shmobil...
476
  	drv->state_count++;
082a8ca1d   Magnus Damm   ARM: mach-shmobil...
477
478
479
480
481
482
483
484
485
486
487
  }
  
  static void sh7372_cpuidle_init(void)
  {
  	shmobile_cpuidle_setup = sh7372_cpuidle_setup;
  }
  #else
  static void sh7372_cpuidle_init(void) {}
  #endif
  
  #ifdef CONFIG_SUSPEND
cf33835c5   Magnus Damm   ARM: mach-shmobil...
488

97991657b   Magnus Damm   ARM: mach-shmobil...
489
490
  static int sh7372_enter_suspend(suspend_state_t suspend_state)
  {
cf33835c5   Magnus Damm   ARM: mach-shmobil...
491
492
493
  	unsigned long msk, msk2;
  
  	/* check active clocks to determine potential wakeup sources */
f7dadb379   Magnus Damm   PM / shmobile: Ad...
494
  	if (sh7372_sysc_valid(&msk, &msk2)) {
cf33835c5   Magnus Damm   ARM: mach-shmobil...
495
  		/* convert INTC mask and sense to SYSC mask and sense */
f7dadb379   Magnus Damm   PM / shmobile: Ad...
496
  		sh7372_setup_sysc(msk, msk2);
767c0f3ae   Rafael J. Wysocki   PM / shmobile: Re...
497
  		if (!console_suspend_enabled &&
f7dadb379   Magnus Damm   PM / shmobile: Ad...
498
499
500
501
502
503
504
505
506
507
508
  		    sh7372_a4s.genpd.status == GPD_STATE_POWER_OFF) {
  			/* enter A4S sleep with PLLC0 off */
  			pr_debug("entering A4S
  ");
  			sh7372_enter_a4s_common(0);
  		} else {
  			/* enter A3SM sleep with PLLC0 off */
  			pr_debug("entering A3SM
  ");
  			sh7372_enter_a3sm_common(0);
  		}
cf33835c5   Magnus Damm   ARM: mach-shmobil...
509
510
511
512
513
514
  	} else {
  		/* default to Core Standby that supports all wakeup sources */
  		pr_debug("entering Core Standby
  ");
  		sh7372_enter_core_standby();
  	}
97991657b   Magnus Damm   ARM: mach-shmobil...
515
516
  	return 0;
  }
a8cf27bee   Rafael J. Wysocki   PM / shmobile: Al...
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
  /**
   * sh7372_pm_notifier_fn - SH7372 PM notifier routine.
   * @notifier: Unused.
   * @pm_event: Event being handled.
   * @unused: Unused.
   */
  static int sh7372_pm_notifier_fn(struct notifier_block *notifier,
  				 unsigned long pm_event, void *unused)
  {
  	switch (pm_event) {
  	case PM_SUSPEND_PREPARE:
  		/*
  		 * This is necessary, because the A4R domain has to be "on"
  		 * when suspend_device_irqs() and resume_device_irqs() are
  		 * executed during system suspend and resume, respectively, so
  		 * that those functions don't crash while accessing the INTCS.
  		 */
  		pm_genpd_poweron(&sh7372_a4r.genpd);
  		break;
  	case PM_POST_SUSPEND:
  		pm_genpd_poweroff_unused();
  		break;
  	}
  
  	return NOTIFY_DONE;
  }
97991657b   Magnus Damm   ARM: mach-shmobil...
543
544
545
  static void sh7372_suspend_init(void)
  {
  	shmobile_suspend_ops.enter = sh7372_enter_suspend;
a8cf27bee   Rafael J. Wysocki   PM / shmobile: Al...
546
  	pm_notifier(sh7372_pm_notifier_fn, 0);
97991657b   Magnus Damm   ARM: mach-shmobil...
547
548
549
550
  }
  #else
  static void sh7372_suspend_init(void) {}
  #endif
97991657b   Magnus Damm   ARM: mach-shmobil...
551
552
553
554
555
556
  void __init sh7372_pm_init(void)
  {
  	/* enable DBG hardware block to kick SYSC */
  	__raw_writel(0x0000a500, DBGREG9);
  	__raw_writel(0x0000a501, DBGREG9);
  	__raw_writel(0x00000000, DBGREG1);
d93f5cdea   Magnus Damm   ARM: mach-shmobil...
557
558
  	/* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */
  	__raw_writel(0, PDNSEL);
97991657b   Magnus Damm   ARM: mach-shmobil...
559
  	sh7372_suspend_init();
082a8ca1d   Magnus Damm   ARM: mach-shmobil...
560
  	sh7372_cpuidle_init();
97991657b   Magnus Damm   ARM: mach-shmobil...
561
  }