| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | #include <linux/init.h>
 | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | #include <linux/suspend.h>
 | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | #include <asm/time.h>
 | 
					
						
							|  |  |  | #include <asm/mpc52xx.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* defined in lite5200_sleep.S and only used here */ | 
					
						
							|  |  |  | extern void lite5200_low_power(void __iomem *sram, void __iomem *mbar); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct mpc52xx_cdm __iomem *cdm; | 
					
						
							|  |  |  | static struct mpc52xx_intr __iomem *pic; | 
					
						
							|  |  |  | static struct mpc52xx_sdma __iomem *bes; | 
					
						
							|  |  |  | static struct mpc52xx_xlb __iomem *xlb; | 
					
						
							|  |  |  | static struct mpc52xx_gpio __iomem *gps; | 
					
						
							|  |  |  | static struct mpc52xx_gpio_wkup __iomem *gpw; | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | static void __iomem *pci; | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | static void __iomem *sram; | 
					
						
							|  |  |  | static const int sram_size = 0x4000;	/* 16 kBytes */ | 
					
						
							|  |  |  | static void __iomem *mbar; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | static suspend_state_t lite5200_pm_target_state; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | static int lite5200_pm_valid(suspend_state_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (state) { | 
					
						
							|  |  |  | 	case PM_SUSPEND_STANDBY: | 
					
						
							|  |  |  | 	case PM_SUSPEND_MEM: | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-08 00:04:17 +01:00
										 |  |  | static int lite5200_pm_begin(suspend_state_t state) | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (lite5200_pm_valid(state)) { | 
					
						
							|  |  |  | 		lite5200_pm_target_state = state; | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lite5200_pm_prepare(void) | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-01-18 09:30:37 -07:00
										 |  |  | 	struct device_node *np; | 
					
						
							| 
									
										
										
										
											2008-01-24 22:25:31 -07:00
										 |  |  | 	const struct of_device_id immr_ids[] = { | 
					
						
							|  |  |  | 		{ .compatible = "fsl,mpc5200-immr", }, | 
					
						
							|  |  |  | 		{ .compatible = "fsl,mpc5200b-immr", }, | 
					
						
							|  |  |  | 		{ .type = "soc", .compatible = "mpc5200", }, /* lite5200 */ | 
					
						
							|  |  |  | 		{ .type = "builtin", .compatible = "mpc5200", }, /* efika */ | 
					
						
							|  |  |  | 		{} | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | 	u64 regaddr64 = 0; | 
					
						
							|  |  |  | 	const u32 *regaddr_p; | 
					
						
							| 
									
										
										
										
											2008-01-18 09:30:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 	/* deep sleep? let mpc52xx code handle that */ | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | 	if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) | 
					
						
							|  |  |  | 		return mpc52xx_pm_prepare(); | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | 	if (lite5200_pm_target_state != PM_SUSPEND_MEM) | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* map registers */ | 
					
						
							| 
									
										
										
										
											2008-01-24 22:25:31 -07:00
										 |  |  | 	np = of_find_matching_node(NULL, immr_ids); | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | 	regaddr_p = of_get_address(np, 0, NULL, NULL); | 
					
						
							|  |  |  | 	if (regaddr_p) | 
					
						
							|  |  |  | 		regaddr64 = of_translate_address(np, regaddr_p); | 
					
						
							| 
									
										
										
										
											2008-01-18 09:30:37 -07:00
										 |  |  | 	of_node_put(np); | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mbar = ioremap((u32) regaddr64, 0xC000); | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 	if (!mbar) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); | 
					
						
							|  |  |  | 		return -ENOSYS; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cdm = mbar + 0x200; | 
					
						
							|  |  |  | 	pic = mbar + 0x500; | 
					
						
							|  |  |  | 	gps = mbar + 0xb00; | 
					
						
							|  |  |  | 	gpw = mbar + 0xc00; | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | 	pci = mbar + 0xd00; | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 	bes = mbar + 0x1200; | 
					
						
							|  |  |  | 	xlb = mbar + 0x1f00; | 
					
						
							|  |  |  | 	sram = mbar + 0x8000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* save and restore registers not bound to any real devices */ | 
					
						
							|  |  |  | static struct mpc52xx_cdm scdm; | 
					
						
							|  |  |  | static struct mpc52xx_intr spic; | 
					
						
							|  |  |  | static struct mpc52xx_sdma sbes; | 
					
						
							|  |  |  | static struct mpc52xx_xlb sxlb; | 
					
						
							|  |  |  | static struct mpc52xx_gpio sgps; | 
					
						
							|  |  |  | static struct mpc52xx_gpio_wkup sgpw; | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | static char spci[0x200]; | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void lite5200_save_regs(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	_memcpy_fromio(&spic, pic, sizeof(*pic)); | 
					
						
							|  |  |  | 	_memcpy_fromio(&sbes, bes, sizeof(*bes)); | 
					
						
							|  |  |  | 	_memcpy_fromio(&scdm, cdm, sizeof(*cdm)); | 
					
						
							|  |  |  | 	_memcpy_fromio(&sxlb, xlb, sizeof(*xlb)); | 
					
						
							|  |  |  | 	_memcpy_fromio(&sgps, gps, sizeof(*gps)); | 
					
						
							|  |  |  | 	_memcpy_fromio(&sgpw, gpw, sizeof(*gpw)); | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | 	_memcpy_fromio(spci, pci, 0x200); | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_memcpy_fromio(saved_sram, sram, sram_size); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void lite5200_restore_regs(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	_memcpy_toio(sram, saved_sram, sram_size); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-17 09:33:14 +01:00
										 |  |  | 	/* PCI Configuration */ | 
					
						
							|  |  |  | 	_memcpy_toio(pci, spci, 0x200); | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * GPIOs. Interrupt Master Enable has higher address then other | 
					
						
							|  |  |  | 	 * registers, so just memcpy is ok. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	_memcpy_toio(gpw, &sgpw, sizeof(*gpw)); | 
					
						
							|  |  |  | 	_memcpy_toio(gps, &sgps, sizeof(*gps)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* XLB Arbitrer */ | 
					
						
							|  |  |  | 	out_be32(&xlb->snoop_window, sxlb.snoop_window); | 
					
						
							|  |  |  | 	out_be32(&xlb->master_priority, sxlb.master_priority); | 
					
						
							|  |  |  | 	out_be32(&xlb->master_pri_enable, sxlb.master_pri_enable); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* enable */ | 
					
						
							|  |  |  | 	out_be32(&xlb->int_enable, sxlb.int_enable); | 
					
						
							|  |  |  | 	out_be32(&xlb->config, sxlb.config); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* CDM - Clock Distribution Module */ | 
					
						
							|  |  |  | 	out_8(&cdm->ipb_clk_sel, scdm.ipb_clk_sel); | 
					
						
							|  |  |  | 	out_8(&cdm->pci_clk_sel, scdm.pci_clk_sel); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_8(&cdm->ext_48mhz_en, scdm.ext_48mhz_en); | 
					
						
							|  |  |  | 	out_8(&cdm->fd_enable, scdm.fd_enable); | 
					
						
							|  |  |  | 	out_be16(&cdm->fd_counters, scdm.fd_counters); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_be32(&cdm->clk_enables, scdm.clk_enables); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_8(&cdm->osc_disable, scdm.osc_disable); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_be16(&cdm->mclken_div_psc1, scdm.mclken_div_psc1); | 
					
						
							|  |  |  | 	out_be16(&cdm->mclken_div_psc2, scdm.mclken_div_psc2); | 
					
						
							|  |  |  | 	out_be16(&cdm->mclken_div_psc3, scdm.mclken_div_psc3); | 
					
						
							|  |  |  | 	out_be16(&cdm->mclken_div_psc6, scdm.mclken_div_psc6); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* BESTCOMM */ | 
					
						
							|  |  |  | 	out_be32(&bes->taskBar, sbes.taskBar); | 
					
						
							|  |  |  | 	out_be32(&bes->currentPointer, sbes.currentPointer); | 
					
						
							|  |  |  | 	out_be32(&bes->endPointer, sbes.endPointer); | 
					
						
							|  |  |  | 	out_be32(&bes->variablePointer, sbes.variablePointer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_8(&bes->IntVect1, sbes.IntVect1); | 
					
						
							|  |  |  | 	out_8(&bes->IntVect2, sbes.IntVect2); | 
					
						
							|  |  |  | 	out_be16(&bes->PtdCntrl, sbes.PtdCntrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i=0; i<32; i++) | 
					
						
							|  |  |  | 		out_8(&bes->ipr[i], sbes.ipr[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_be32(&bes->cReqSelect, sbes.cReqSelect); | 
					
						
							|  |  |  | 	out_be32(&bes->task_size0, sbes.task_size0); | 
					
						
							|  |  |  | 	out_be32(&bes->task_size1, sbes.task_size1); | 
					
						
							|  |  |  | 	out_be32(&bes->MDEDebug, sbes.MDEDebug); | 
					
						
							|  |  |  | 	out_be32(&bes->ADSDebug, sbes.ADSDebug); | 
					
						
							|  |  |  | 	out_be32(&bes->Value1, sbes.Value1); | 
					
						
							|  |  |  | 	out_be32(&bes->Value2, sbes.Value2); | 
					
						
							|  |  |  | 	out_be32(&bes->Control, sbes.Control); | 
					
						
							|  |  |  | 	out_be32(&bes->Status, sbes.Status); | 
					
						
							|  |  |  | 	out_be32(&bes->PTDDebug, sbes.PTDDebug); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* restore tasks */ | 
					
						
							|  |  |  | 	for (i=0; i<16; i++) | 
					
						
							|  |  |  | 		out_be16(&bes->tcr[i], sbes.tcr[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* enable interrupts */ | 
					
						
							|  |  |  | 	out_be32(&bes->IntPend, sbes.IntPend); | 
					
						
							|  |  |  | 	out_be32(&bes->IntMask, sbes.IntMask); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* PIC */ | 
					
						
							|  |  |  | 	out_be32(&pic->per_pri1, spic.per_pri1); | 
					
						
							|  |  |  | 	out_be32(&pic->per_pri2, spic.per_pri2); | 
					
						
							|  |  |  | 	out_be32(&pic->per_pri3, spic.per_pri3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_be32(&pic->main_pri1, spic.main_pri1); | 
					
						
							|  |  |  | 	out_be32(&pic->main_pri2, spic.main_pri2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_be32(&pic->enc_status, spic.enc_status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* unmask and enable interrupts */ | 
					
						
							|  |  |  | 	out_be32(&pic->per_mask, spic.per_mask); | 
					
						
							|  |  |  | 	out_be32(&pic->main_mask, spic.main_mask); | 
					
						
							|  |  |  | 	out_be32(&pic->ctrl, spic.ctrl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lite5200_pm_enter(suspend_state_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* deep sleep? let mpc52xx code handle that */ | 
					
						
							|  |  |  | 	if (state == PM_SUSPEND_STANDBY) { | 
					
						
							|  |  |  | 		return mpc52xx_pm_enter(state); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lite5200_save_regs(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* effectively save FP regs */ | 
					
						
							|  |  |  | 	enable_kernel_fp(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lite5200_low_power(sram, mbar); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lite5200_restore_regs(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iounmap(mbar); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | static void lite5200_pm_finish(void) | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* deep sleep? let mpc52xx code handle that */ | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | 	if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) | 
					
						
							|  |  |  | 		mpc52xx_pm_finish(); | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-08 00:04:17 +01:00
										 |  |  | static void lite5200_pm_end(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	lite5200_pm_target_state = PM_SUSPEND_ON; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-16 14:14:02 +01:00
										 |  |  | static const struct platform_suspend_ops lite5200_pm_ops = { | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 	.valid		= lite5200_pm_valid, | 
					
						
							| 
									
										
										
										
											2008-01-08 00:04:17 +01:00
										 |  |  | 	.begin		= lite5200_pm_begin, | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 	.prepare	= lite5200_pm_prepare, | 
					
						
							|  |  |  | 	.enter		= lite5200_pm_enter, | 
					
						
							|  |  |  | 	.finish		= lite5200_pm_finish, | 
					
						
							| 
									
										
										
										
											2008-01-08 00:04:17 +01:00
										 |  |  | 	.end		= lite5200_pm_end, | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __init lite5200_pm_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-10-18 03:04:41 -07:00
										 |  |  | 	suspend_set_ops(&lite5200_pm_ops); | 
					
						
							| 
									
										
										
										
											2007-07-18 06:32:31 +10:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } |