/* * rmobile power management support * * Copyright (C) 2012 Renesas Solutions Corp. * Copyright (C) 2012 Kuninori Morimoto * * 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 #include #include #include #include #include #include /* SYSC */ #define SPDCR IOMEM(0xe6180008) #define SWUCR IOMEM(0xe6180014) #define PSTR IOMEM(0xe6180080) #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; } static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) { 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); } 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]); } void rmobile_add_device_to_domain_td(const char *domain_name, struct platform_device *pdev, struct gpd_timing_data *td) { struct device *dev = &pdev->dev; __pm_genpd_name_add_device(domain_name, dev, td); if (pm_clk_no_clocks(dev)) pm_clk_add(dev, NULL); } void rmobile_add_devices_to_domains(struct pm_domain_device data[], int size) { 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); } #endif /* CONFIG_PM */