 45e5ca5756
			
		
	
	
	45e5ca5756
	
	
	
		
			
			Instead of giving a name to every r8a7779's PM domain object, put them all into a table and initialize them all together in a loop. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Magnus Damm <damm@opensource.se>
		
			
				
	
	
		
			249 lines
		
	
	
	
		
			5.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
	
		
			5.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * r8a7779 Power management support
 | |
|  *
 | |
|  * Copyright (C) 2011  Renesas Solutions Corp.
 | |
|  * 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>
 | |
| #include <linux/err.h>
 | |
| #include <linux/pm_clock.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/irq.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/console.h>
 | |
| #include <asm/io.h>
 | |
| #include <mach/common.h>
 | |
| #include <mach/r8a7779.h>
 | |
| 
 | |
| static void __iomem *r8a7779_sysc_base;
 | |
| 
 | |
| /* SYSC */
 | |
| #define SYSCSR 0x00
 | |
| #define SYSCISR 0x04
 | |
| #define SYSCISCR 0x08
 | |
| #define SYSCIER 0x0c
 | |
| #define SYSCIMR 0x10
 | |
| #define PWRSR0 0x40
 | |
| #define PWRSR1 0x80
 | |
| #define PWRSR2 0xc0
 | |
| #define PWRSR3 0x100
 | |
| #define PWRSR4 0x140
 | |
| 
 | |
| #define PWRSR_OFFS 0x00
 | |
| #define PWROFFCR_OFFS 0x04
 | |
| #define PWRONCR_OFFS 0x0c
 | |
| #define PWRER_OFFS 0x14
 | |
| 
 | |
| #define SYSCSR_RETRIES 100
 | |
| #define SYSCSR_DELAY_US 1
 | |
| 
 | |
| #define SYSCISR_RETRIES 1000
 | |
| #define SYSCISR_DELAY_US 1
 | |
| 
 | |
| #if defined(CONFIG_PM) || defined(CONFIG_SMP)
 | |
| 
 | |
| static DEFINE_SPINLOCK(r8a7779_sysc_lock); /* SMP CPUs + I/O devices */
 | |
| 
 | |
| static int r8a7779_sysc_pwr_on_off(struct r8a7779_pm_ch *r8a7779_ch,
 | |
| 				   int sr_bit, int reg_offs)
 | |
| {
 | |
| 	int k;
 | |
| 
 | |
| 	for (k = 0; k < SYSCSR_RETRIES; k++) {
 | |
| 		if (ioread32(r8a7779_sysc_base + SYSCSR) & (1 << sr_bit))
 | |
| 			break;
 | |
| 		udelay(SYSCSR_DELAY_US);
 | |
| 	}
 | |
| 
 | |
| 	if (k == SYSCSR_RETRIES)
 | |
| 		return -EAGAIN;
 | |
| 
 | |
| 	iowrite32(1 << r8a7779_ch->chan_bit,
 | |
| 		  r8a7779_sysc_base + r8a7779_ch->chan_offs + reg_offs);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int r8a7779_sysc_pwr_off(struct r8a7779_pm_ch *r8a7779_ch)
 | |
| {
 | |
| 	return r8a7779_sysc_pwr_on_off(r8a7779_ch, 0, PWROFFCR_OFFS);
 | |
| }
 | |
| 
 | |
| static int r8a7779_sysc_pwr_on(struct r8a7779_pm_ch *r8a7779_ch)
 | |
| {
 | |
| 	return r8a7779_sysc_pwr_on_off(r8a7779_ch, 1, PWRONCR_OFFS);
 | |
| }
 | |
| 
 | |
| static int r8a7779_sysc_update(struct r8a7779_pm_ch *r8a7779_ch,
 | |
| 			       int (*on_off_fn)(struct r8a7779_pm_ch *))
 | |
| {
 | |
| 	unsigned int isr_mask = 1 << r8a7779_ch->isr_bit;
 | |
| 	unsigned int chan_mask = 1 << r8a7779_ch->chan_bit;
 | |
| 	unsigned int status;
 | |
| 	unsigned long flags;
 | |
| 	int ret = 0;
 | |
| 	int k;
 | |
| 
 | |
| 	spin_lock_irqsave(&r8a7779_sysc_lock, flags);
 | |
| 
 | |
| 	iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
 | |
| 
 | |
| 	do {
 | |
| 		ret = on_off_fn(r8a7779_ch);
 | |
| 		if (ret)
 | |
| 			goto out;
 | |
| 
 | |
| 		status = ioread32(r8a7779_sysc_base +
 | |
| 				  r8a7779_ch->chan_offs + PWRER_OFFS);
 | |
| 	} while (status & chan_mask);
 | |
| 
 | |
| 	for (k = 0; k < SYSCISR_RETRIES; k++) {
 | |
| 		if (ioread32(r8a7779_sysc_base + SYSCISR) & isr_mask)
 | |
| 			break;
 | |
| 		udelay(SYSCISR_DELAY_US);
 | |
| 	}
 | |
| 
 | |
| 	if (k == SYSCISR_RETRIES)
 | |
| 		ret = -EIO;
 | |
| 
 | |
| 	iowrite32(isr_mask, r8a7779_sysc_base + SYSCISCR);
 | |
| 
 | |
|  out:
 | |
| 	spin_unlock_irqrestore(&r8a7779_sysc_lock, flags);
 | |
| 
 | |
| 	pr_debug("r8a7779 power domain %d: %02x %02x %02x %02x %02x -> %d\n",
 | |
| 		 r8a7779_ch->isr_bit, ioread32(r8a7779_sysc_base + PWRSR0),
 | |
| 		 ioread32(r8a7779_sysc_base + PWRSR1),
 | |
| 		 ioread32(r8a7779_sysc_base + PWRSR2),
 | |
| 		 ioread32(r8a7779_sysc_base + PWRSR3),
 | |
| 		 ioread32(r8a7779_sysc_base + PWRSR4), ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int r8a7779_sysc_power_down(struct r8a7779_pm_ch *r8a7779_ch)
 | |
| {
 | |
| 	return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_off);
 | |
| }
 | |
| 
 | |
| int r8a7779_sysc_power_up(struct r8a7779_pm_ch *r8a7779_ch)
 | |
| {
 | |
| 	return r8a7779_sysc_update(r8a7779_ch, r8a7779_sysc_pwr_on);
 | |
| }
 | |
| 
 | |
| static void __init r8a7779_sysc_init(void)
 | |
| {
 | |
| 	r8a7779_sysc_base = ioremap_nocache(0xffd85000, PAGE_SIZE);
 | |
| 	if (!r8a7779_sysc_base)
 | |
| 		panic("unable to ioremap r8a7779 SYSC hardware block\n");
 | |
| 
 | |
| 	/* enable all interrupt sources, but do not use interrupt handler */
 | |
| 	iowrite32(0x0131000e, r8a7779_sysc_base + SYSCIER);
 | |
| 	iowrite32(0, r8a7779_sysc_base + SYSCIMR);
 | |
| }
 | |
| 
 | |
| #else /* CONFIG_PM || CONFIG_SMP */
 | |
| 
 | |
| static inline void r8a7779_sysc_init(void) {}
 | |
| 
 | |
| #endif /* CONFIG_PM || CONFIG_SMP */
 | |
| 
 | |
| #ifdef CONFIG_PM
 | |
| 
 | |
| static int pd_power_down(struct generic_pm_domain *genpd)
 | |
| {
 | |
| 	return r8a7779_sysc_power_down(to_r8a7779_ch(genpd));
 | |
| }
 | |
| 
 | |
| static int pd_power_up(struct generic_pm_domain *genpd)
 | |
| {
 | |
| 	return r8a7779_sysc_power_up(to_r8a7779_ch(genpd));
 | |
| }
 | |
| 
 | |
| static bool pd_is_off(struct generic_pm_domain *genpd)
 | |
| {
 | |
| 	struct r8a7779_pm_ch *r8a7779_ch = to_r8a7779_ch(genpd);
 | |
| 	unsigned int st;
 | |
| 
 | |
| 	st = ioread32(r8a7779_sysc_base + r8a7779_ch->chan_offs + PWRSR_OFFS);
 | |
| 	if (st & (1 << r8a7779_ch->chan_bit))
 | |
| 		return true;
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static bool pd_active_wakeup(struct device *dev)
 | |
| {
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd)
 | |
| {
 | |
| 	struct generic_pm_domain *genpd = &r8a7779_pd->genpd;
 | |
| 
 | |
| 	pm_genpd_init(genpd, NULL, false);
 | |
| 	genpd->dev_ops.stop = pm_clk_suspend;
 | |
| 	genpd->dev_ops.start = pm_clk_resume;
 | |
| 	genpd->dev_ops.active_wakeup = pd_active_wakeup;
 | |
| 	genpd->dev_irq_safe = true;
 | |
| 	genpd->power_off = pd_power_down;
 | |
| 	genpd->power_on = pd_power_up;
 | |
| 
 | |
| 	if (pd_is_off(&r8a7779_pd->genpd))
 | |
| 		pd_power_up(&r8a7779_pd->genpd);
 | |
| }
 | |
| 
 | |
| static struct r8a7779_pm_domain r8a7779_pm_domains[] = {
 | |
| 	{
 | |
| 		.genpd.name = "SH4A",
 | |
| 		.ch = {
 | |
| 			.chan_offs = 0x80, /* PWRSR1 .. PWRER1 */
 | |
| 			.isr_bit = 16, /* SH4A */
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		.genpd.name = "SGX",
 | |
| 		.ch = {
 | |
| 			.chan_offs = 0xc0, /* PWRSR2 .. PWRER2 */
 | |
| 			.isr_bit = 20, /* SGX */
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		.genpd.name = "VDP1",
 | |
| 		.ch = {
 | |
| 			.chan_offs = 0x100, /* PWRSR3 .. PWRER3 */
 | |
| 			.isr_bit = 21, /* VDP */
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		.genpd.name = "IMPX3",
 | |
| 		.ch = {
 | |
| 			.chan_offs = 0x140, /* PWRSR4 .. PWRER4 */
 | |
| 			.isr_bit = 24, /* IMP */
 | |
| 		},
 | |
| 	},
 | |
| };
 | |
| 
 | |
| void __init r8a7779_init_pm_domains(void)
 | |
| {
 | |
| 	int j;
 | |
| 
 | |
| 	for (j = 0; j < ARRAY_SIZE(r8a7779_pm_domains); j++)
 | |
| 		r8a7779_init_pm_domain(&r8a7779_pm_domains[j]);
 | |
| }
 | |
| 
 | |
| #endif /* CONFIG_PM */
 | |
| 
 | |
| void __init r8a7779_pm_init(void)
 | |
| {
 | |
| 	static int once;
 | |
| 
 | |
| 	if (!once++)
 | |
| 		r8a7779_sysc_init();
 | |
| }
 |