| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  linux/arch/unicore32/kernel/puv3-core.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Code specific to PKUnity SoC and UniCore ISA | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> | 
					
						
							|  |  |  |  *	Copyright (C) 2001-2010 Guan Xuetao | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/amba/bus.h>
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/cnt32_to_63.h>
 | 
					
						
							|  |  |  | #include <linux/usb/musb.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/irq.h>
 | 
					
						
							|  |  |  | #include <mach/hardware.h>
 | 
					
						
							|  |  |  | #include <mach/pm.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * This is the PKUnity sched_clock implementation.  This has | 
					
						
							|  |  |  |  * a resolution of 271ns, and a maximum value of 32025597s (370 days). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The return value is guaranteed to be monotonic in that range as | 
					
						
							|  |  |  |  * long as there is always less than 582 seconds between successive | 
					
						
							|  |  |  |  * calls to this function. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  ( * 1E9 / CLOCK_TICK_RATE ) -> about 2235/32 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | unsigned long long sched_clock(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-02-26 21:21:18 +08:00
										 |  |  | 	unsigned long long v = cnt32_to_63(readl(OST_OSCR)); | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* original conservative method, but overflow frequently
 | 
					
						
							|  |  |  | 	 * v *= NSEC_PER_SEC >> 12; | 
					
						
							|  |  |  | 	 * do_div(v, CLOCK_TICK_RATE >> 12); | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	v = ((v & 0x7fffffffffffffffULL) * 2235) >> 5; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return v; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct resource puv3_usb_resources[] = { | 
					
						
							|  |  |  | 	/* order is significant! */ | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start		= io_v2p(PKUNITY_USB_BASE), | 
					
						
							|  |  |  | 		.end		= io_v2p(PKUNITY_USB_BASE) + 0x3ff, | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 		.flags		= IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, { | 
					
						
							|  |  |  | 		.start		= IRQ_USB, | 
					
						
							|  |  |  | 		.flags		= IORESOURCE_IRQ, | 
					
						
							|  |  |  | 	}, { | 
					
						
							|  |  |  | 		.start		= IRQ_USB, | 
					
						
							|  |  |  | 		.flags		= IORESOURCE_IRQ, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct musb_hdrc_config	puv3_usb_config[] = { | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.num_eps = 16, | 
					
						
							|  |  |  | 		.multipoint = 1, | 
					
						
							|  |  |  | #ifdef CONFIG_USB_INVENTRA_DMA
 | 
					
						
							|  |  |  | 		.dma = 1, | 
					
						
							|  |  |  | 		.dma_channels = 8, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct musb_hdrc_platform_data puv3_usb_plat = { | 
					
						
							|  |  |  | 	.mode		= MUSB_HOST, | 
					
						
							|  |  |  | 	.min_power	= 100, | 
					
						
							|  |  |  | 	.clock		= 0, | 
					
						
							|  |  |  | 	.config		= puv3_usb_config, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct resource puv3_mmc_resources[] = { | 
					
						
							|  |  |  | 	[0] = { | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start	= io_v2p(PKUNITY_SDC_BASE), | 
					
						
							|  |  |  | 		.end	= io_v2p(PKUNITY_SDC_BASE) + 0xfff, | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 		.flags	= IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[1] = { | 
					
						
							|  |  |  | 		.start	= IRQ_SDC, | 
					
						
							|  |  |  | 		.end	= IRQ_SDC, | 
					
						
							|  |  |  | 		.flags	= IORESOURCE_IRQ, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-26 21:39:10 +08:00
										 |  |  | static struct resource puv3_unigfx_resources[] = { | 
					
						
							|  |  |  | 	[0] = { | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start	= io_v2p(PKUNITY_UNIGFX_BASE), | 
					
						
							|  |  |  | 		.end	= io_v2p(PKUNITY_UNIGFX_BASE) + 0xfff, | 
					
						
							| 
									
										
										
										
											2011-02-26 21:39:10 +08:00
										 |  |  | 		.flags	= IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | static struct resource puv3_rtc_resources[] = { | 
					
						
							|  |  |  | 	[0] = { | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start = io_v2p(PKUNITY_RTC_BASE), | 
					
						
							|  |  |  | 		.end   = io_v2p(PKUNITY_RTC_BASE) + 0xff, | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 		.flags = IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[1] = { | 
					
						
							|  |  |  | 		.start = IRQ_RTCAlarm, | 
					
						
							|  |  |  | 		.end   = IRQ_RTCAlarm, | 
					
						
							|  |  |  | 		.flags = IORESOURCE_IRQ, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[2] = { | 
					
						
							|  |  |  | 		.start = IRQ_RTC, | 
					
						
							|  |  |  | 		.end   = IRQ_RTC, | 
					
						
							|  |  |  | 		.flags = IORESOURCE_IRQ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct resource puv3_pwm_resources[] = { | 
					
						
							|  |  |  | 	[0] = { | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start	= io_v2p(PKUNITY_OST_BASE) + 0x80, | 
					
						
							|  |  |  | 		.end	= io_v2p(PKUNITY_OST_BASE) + 0xff, | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 		.flags	= IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct resource puv3_uart0_resources[] = { | 
					
						
							|  |  |  | 	[0] = { | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start = io_v2p(PKUNITY_UART0_BASE), | 
					
						
							|  |  |  | 		.end   = io_v2p(PKUNITY_UART0_BASE) + 0xff, | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 		.flags = IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[1] = { | 
					
						
							|  |  |  | 		.start = IRQ_UART0, | 
					
						
							|  |  |  | 		.end   = IRQ_UART0, | 
					
						
							|  |  |  | 		.flags = IORESOURCE_IRQ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct resource puv3_uart1_resources[] = { | 
					
						
							|  |  |  | 	[0] = { | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start = io_v2p(PKUNITY_UART1_BASE), | 
					
						
							|  |  |  | 		.end   = io_v2p(PKUNITY_UART1_BASE) + 0xff, | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 		.flags = IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[1] = { | 
					
						
							|  |  |  | 		.start = IRQ_UART1, | 
					
						
							|  |  |  | 		.end   = IRQ_UART1, | 
					
						
							|  |  |  | 		.flags = IORESOURCE_IRQ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct resource puv3_umal_resources[] = { | 
					
						
							|  |  |  | 	[0] = { | 
					
						
							| 
									
										
										
										
											2011-03-04 18:07:48 +08:00
										 |  |  | 		.start = io_v2p(PKUNITY_UMAL_BASE), | 
					
						
							|  |  |  | 		.end   = io_v2p(PKUNITY_UMAL_BASE) + 0x1fff, | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 		.flags = IORESOURCE_MEM, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	[1] = { | 
					
						
							|  |  |  | 		.start = IRQ_UMAL, | 
					
						
							|  |  |  | 		.end   = IRQ_UMAL, | 
					
						
							|  |  |  | 		.flags = IORESOURCE_IRQ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PUV3_PM
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
 | 
					
						
							|  |  |  | #define RESTORE(x)	x = sleep_save[SLEEP_SAVE_##x]
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * List of global PXA peripheral registers to preserve. | 
					
						
							|  |  |  |  * More ones like CP and general purpose register values are preserved | 
					
						
							|  |  |  |  * with the stack pointer in sleep.S. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  | 	SLEEP_SAVE_PM_PLLDDRCFG, | 
					
						
							|  |  |  | 	SLEEP_SAVE_COUNT | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void puv3_cpu_pm_save(unsigned long *sleep_save) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | /*	SAVE(PM_PLLDDRCFG); */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void puv3_cpu_pm_restore(unsigned long *sleep_save) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | /*	RESTORE(PM_PLLDDRCFG); */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int puv3_cpu_pm_prepare(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* set resume return address */ | 
					
						
							| 
									
										
										
										
											2011-02-26 21:21:18 +08:00
										 |  |  | 	writel(virt_to_phys(puv3_cpu_resume), PM_DIVCFG); | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void puv3_cpu_pm_enter(suspend_state_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Clear reset status */ | 
					
						
							| 
									
										
										
										
											2011-02-26 21:21:18 +08:00
										 |  |  | 	writel(RESETC_RSSR_HWR | RESETC_RSSR_WDR | 
					
						
							|  |  |  | 			| RESETC_RSSR_SMR | RESETC_RSSR_SWR, RESETC_RSSR); | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (state) { | 
					
						
							|  |  |  | /*	case PM_SUSPEND_ON:
 | 
					
						
							|  |  |  | 		puv3_cpu_idle(); | 
					
						
							|  |  |  | 		break; */ | 
					
						
							|  |  |  | 	case PM_SUSPEND_MEM: | 
					
						
							|  |  |  | 		puv3_cpu_pm_prepare(); | 
					
						
							|  |  |  | 		puv3_cpu_suspend(PM_PMCR_SFB); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int puv3_cpu_pm_valid(suspend_state_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return state == PM_SUSPEND_MEM; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void puv3_cpu_pm_finish(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* ensure not to come back here if it wasn't intended */ | 
					
						
							|  |  |  | 	/* PSPR = 0; */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct puv3_cpu_pm_fns puv3_cpu_pm_fnss = { | 
					
						
							|  |  |  | 	.save_count	= SLEEP_SAVE_COUNT, | 
					
						
							|  |  |  | 	.valid		= puv3_cpu_pm_valid, | 
					
						
							|  |  |  | 	.save		= puv3_cpu_pm_save, | 
					
						
							|  |  |  | 	.restore	= puv3_cpu_pm_restore, | 
					
						
							|  |  |  | 	.enter		= puv3_cpu_pm_enter, | 
					
						
							|  |  |  | 	.prepare	= puv3_cpu_pm_prepare, | 
					
						
							|  |  |  | 	.finish		= puv3_cpu_pm_finish, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init puv3_init_pm(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	puv3_cpu_pm_fns = &puv3_cpu_pm_fnss; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | static inline void puv3_init_pm(void) {} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void puv3_ps2_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct clk *bclk32; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bclk32 = clk_get(NULL, "BUS32_CLK"); | 
					
						
							| 
									
										
										
										
											2011-02-26 21:21:18 +08:00
										 |  |  | 	writel(clk_get_rate(bclk32) / 200000, PS2_CNT); /* should > 5us */ | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __init puv3_core_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	puv3_init_pm(); | 
					
						
							|  |  |  | 	puv3_ps2_init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	platform_device_register_simple("PKUnity-v3-RTC", -1, | 
					
						
							|  |  |  | 			puv3_rtc_resources, ARRAY_SIZE(puv3_rtc_resources)); | 
					
						
							|  |  |  | 	platform_device_register_simple("PKUnity-v3-UMAL", -1, | 
					
						
							|  |  |  | 			puv3_umal_resources, ARRAY_SIZE(puv3_umal_resources)); | 
					
						
							|  |  |  | 	platform_device_register_simple("PKUnity-v3-MMC", -1, | 
					
						
							|  |  |  | 			puv3_mmc_resources, ARRAY_SIZE(puv3_mmc_resources)); | 
					
						
							| 
									
										
										
										
											2011-02-26 21:39:10 +08:00
										 |  |  | 	platform_device_register_simple("PKUnity-v3-UNIGFX", -1, | 
					
						
							|  |  |  | 			puv3_unigfx_resources, ARRAY_SIZE(puv3_unigfx_resources)); | 
					
						
							| 
									
										
										
										
											2011-02-26 19:51:18 +08:00
										 |  |  | 	platform_device_register_simple("PKUnity-v3-PWM", -1, | 
					
						
							|  |  |  | 			puv3_pwm_resources, ARRAY_SIZE(puv3_pwm_resources)); | 
					
						
							|  |  |  | 	platform_device_register_simple("PKUnity-v3-UART", 0, | 
					
						
							|  |  |  | 			puv3_uart0_resources, ARRAY_SIZE(puv3_uart0_resources)); | 
					
						
							|  |  |  | 	platform_device_register_simple("PKUnity-v3-UART", 1, | 
					
						
							|  |  |  | 			puv3_uart1_resources, ARRAY_SIZE(puv3_uart1_resources)); | 
					
						
							|  |  |  | 	platform_device_register_simple("PKUnity-v3-AC97", -1, NULL, 0); | 
					
						
							|  |  |  | 	platform_device_register_resndata(&platform_bus, "musb_hdrc", -1, | 
					
						
							|  |  |  | 			puv3_usb_resources, ARRAY_SIZE(puv3_usb_resources), | 
					
						
							|  |  |  | 			&puv3_usb_plat, sizeof(puv3_usb_plat)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 |