| 
									
										
										
										
											2012-07-05 01:24:46 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * rmobile power management support | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2012  Renesas Solutions Corp. | 
					
						
							|  |  |  |  * Copyright (C) 2012  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * based on pm-sh7372.c | 
					
						
							|  |  |  |  *  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/console.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/pm.h>
 | 
					
						
							|  |  |  | #include <linux/pm_clock.h>
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | #include <mach/pm-rmobile.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* SYSC */ | 
					
						
							| 
									
										
										
										
											2012-09-14 20:08:08 +00:00
										 |  |  | #define SPDCR		IOMEM(0xe6180008)
 | 
					
						
							|  |  |  | #define SWUCR		IOMEM(0xe6180014)
 | 
					
						
							|  |  |  | #define PSTR		IOMEM(0xe6180080)
 | 
					
						
							| 
									
										
										
										
											2012-07-05 01:24:46 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define PSTR_RETRIES	100
 | 
					
						
							|  |  |  | #define PSTR_DELAY_US	10
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | static int rmobile_pd_power_down(struct generic_pm_domain *genpd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); | 
					
						
							|  |  |  | 	unsigned int mask = 1 << rmobile_pd->bit_shift; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rmobile_pd->suspend) { | 
					
						
							|  |  |  | 		int ret = rmobile_pd->suspend(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!rmobile_pd->no_debug) | 
					
						
							|  |  |  | 		pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", | 
					
						
							|  |  |  | 			 genpd->name, mask, __raw_readl(PSTR)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd, | 
					
						
							|  |  |  | 				 bool do_resume) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int mask = 1 << rmobile_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)) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		if (retry_count > PSTR_RETRIES) | 
					
						
							|  |  |  | 			udelay(PSTR_DELAY_US); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			cpu_relax(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!retry_count) | 
					
						
							|  |  |  | 		ret = -EIO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!rmobile_pd->no_debug) | 
					
						
							|  |  |  | 		pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n", | 
					
						
							|  |  |  | 			 rmobile_pd->genpd.name, mask, __raw_readl(PSTR)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	if (ret == 0 && rmobile_pd->resume && do_resume) | 
					
						
							|  |  |  | 		rmobile_pd->resume(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rmobile_pd_power_up(struct generic_pm_domain *genpd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return __rmobile_pd_power_up(to_rmobile_pd(genpd), true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool rmobile_pd_active_wakeup(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	bool (*active_wakeup)(struct device *dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	active_wakeup = dev_gpd_data(dev)->ops.active_wakeup; | 
					
						
							|  |  |  | 	return active_wakeup ? active_wakeup(dev) : true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rmobile_pd_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 rmobile_pd_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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-07 01:15:02 +02:00
										 |  |  | static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) | 
					
						
							| 
									
										
										
										
											2012-07-05 01:24:46 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct generic_pm_domain *genpd = &rmobile_pd->genpd; | 
					
						
							|  |  |  | 	struct dev_power_governor *gov = rmobile_pd->gov; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); | 
					
						
							|  |  |  | 	genpd->dev_ops.stop		= rmobile_pd_stop_dev; | 
					
						
							|  |  |  | 	genpd->dev_ops.start		= rmobile_pd_start_dev; | 
					
						
							|  |  |  | 	genpd->dev_ops.active_wakeup	= rmobile_pd_active_wakeup; | 
					
						
							|  |  |  | 	genpd->dev_irq_safe		= true; | 
					
						
							|  |  |  | 	genpd->power_off		= rmobile_pd_power_down; | 
					
						
							|  |  |  | 	genpd->power_on			= rmobile_pd_power_up; | 
					
						
							|  |  |  | 	__rmobile_pd_power_up(rmobile_pd, false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-07 01:10:22 +02:00
										 |  |  | void rmobile_init_domains(struct rmobile_pm_domain domains[], int num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int j; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (j = 0; j < num; j++) | 
					
						
							|  |  |  | 		rmobile_init_pm_domain(&domains[j]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-08 00:27:52 +02:00
										 |  |  | void rmobile_add_device_to_domain_td(const char *domain_name, | 
					
						
							|  |  |  | 				     struct platform_device *pdev, | 
					
						
							|  |  |  | 				     struct gpd_timing_data *td) | 
					
						
							| 
									
										
										
										
											2012-07-05 01:24:46 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = &pdev->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-08 00:27:52 +02:00
										 |  |  | 	__pm_genpd_name_add_device(domain_name, dev, td); | 
					
						
							| 
									
										
										
										
											2012-07-05 01:24:46 -07:00
										 |  |  | 	if (pm_clk_no_clocks(dev)) | 
					
						
							|  |  |  | 		pm_clk_add(dev, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-15 20:56:26 +02:00
										 |  |  | void rmobile_add_devices_to_domains(struct pm_domain_device data[], | 
					
						
							|  |  |  | 				    int size) | 
					
						
							| 
									
										
										
										
											2012-07-05 01:24:46 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-08-15 20:56:26 +02:00
										 |  |  | 	struct gpd_timing_data latencies = { | 
					
						
							|  |  |  | 		.stop_latency_ns = DEFAULT_DEV_LATENCY_NS, | 
					
						
							|  |  |  | 		.start_latency_ns = DEFAULT_DEV_LATENCY_NS, | 
					
						
							|  |  |  | 		.save_state_latency_ns = DEFAULT_DEV_LATENCY_NS, | 
					
						
							|  |  |  | 		.restore_state_latency_ns = DEFAULT_DEV_LATENCY_NS, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	int j; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (j = 0; j < size; j++) | 
					
						
							|  |  |  | 		rmobile_add_device_to_domain_td(data[j].domain_name, | 
					
						
							|  |  |  | 						data[j].pdev, &latencies); | 
					
						
							| 
									
										
										
										
											2012-07-05 01:24:46 -07:00
										 |  |  | } | 
					
						
							|  |  |  | #endif /* CONFIG_PM */
 |