1 /* 2 * pm.c - Common OMAP2+ power management-related code 3 * 4 * Copyright (C) 2010 Texas Instruments, Inc. 5 * Copyright (C) 2010 Nokia Corporation 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/init.h> 14 #include <linux/io.h> 15 #include <linux/err.h> 16 #include <linux/pm_opp.h> 17 #include <linux/export.h> 18 #include <linux/suspend.h> 19 #include <linux/cpu.h> 20 21 #include <asm/system_misc.h> 22 23 #include "omap-pm.h" 24 #include "omap_device.h" 25 #include "common.h" 26 27 #include "soc.h" 28 #include "prcm-common.h" 29 #include "voltage.h" 30 #include "powerdomain.h" 31 #include "clockdomain.h" 32 #include "pm.h" 33 #include "twl-common.h" 34 35 #ifdef CONFIG_SUSPEND 36 /* 37 * omap_pm_suspend: points to a function that does the SoC-specific 38 * suspend work 39 */ 40 static int (*omap_pm_suspend)(void); 41 #endif 42 43 #ifdef CONFIG_PM 44 /** 45 * struct omap2_oscillator - Describe the board main oscillator latencies 46 * @startup_time: oscillator startup latency 47 * @shutdown_time: oscillator shutdown latency 48 */ 49 struct omap2_oscillator { 50 u32 startup_time; 51 u32 shutdown_time; 52 }; 53 54 static struct omap2_oscillator oscillator = { 55 .startup_time = ULONG_MAX, 56 .shutdown_time = ULONG_MAX, 57 }; 58 59 void omap_pm_setup_oscillator(u32 tstart, u32 tshut) 60 { 61 oscillator.startup_time = tstart; 62 oscillator.shutdown_time = tshut; 63 } 64 65 void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) 66 { 67 if (!tstart || !tshut) 68 return; 69 70 *tstart = oscillator.startup_time; 71 *tshut = oscillator.shutdown_time; 72 } 73 #endif 74 75 static int __init _init_omap_device(char *name) 76 { 77 struct omap_hwmod *oh; 78 struct platform_device *pdev; 79 80 oh = omap_hwmod_lookup(name); 81 if (WARN(!oh, "%s: could not find omap_hwmod for %s\n", 82 __func__, name)) 83 return -ENODEV; 84 85 pdev = omap_device_build(oh->name, 0, oh, NULL, 0); 86 if (WARN(IS_ERR(pdev), "%s: could not build omap_device for %s\n", 87 __func__, name)) 88 return -ENODEV; 89 90 return 0; 91 } 92 93 /* 94 * Build omap_devices for processors and bus. 95 */ 96 static void __init omap2_init_processor_devices(void) 97 { 98 _init_omap_device("mpu"); 99 if (omap3_has_iva()) 100 _init_omap_device("iva"); 101 102 if (cpu_is_omap44xx()) { 103 _init_omap_device("l3_main_1"); 104 _init_omap_device("dsp"); 105 _init_omap_device("iva"); 106 } else { 107 _init_omap_device("l3_main"); 108 } 109 } 110 111 int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) 112 { 113 /* XXX The usecount test is racy */ 114 if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) && 115 !(clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)) 116 clkdm_allow_idle(clkdm); 117 else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP && 118 clkdm->usecount == 0) 119 clkdm_sleep(clkdm); 120 return 0; 121 } 122 123 /* 124 * This API is to be called during init to set the various voltage 125 * domains to the voltage as per the opp table. Typically we boot up 126 * at the nominal voltage. So this function finds out the rate of 127 * the clock associated with the voltage domain, finds out the correct 128 * opp entry and sets the voltage domain to the voltage specified 129 * in the opp entry 130 */ 131 static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, 132 const char *oh_name) 133 { 134 struct voltagedomain *voltdm; 135 struct clk *clk; 136 struct dev_pm_opp *opp; 137 unsigned long freq, bootup_volt; 138 struct device *dev; 139 140 if (!vdd_name || !clk_name || !oh_name) { 141 pr_err("%s: invalid parameters\n", __func__); 142 goto exit; 143 } 144 145 if (!strncmp(oh_name, "mpu", 3)) 146 /* 147 * All current OMAPs share voltage rail and clock 148 * source, so CPU0 is used to represent the MPU-SS. 149 */ 150 dev = get_cpu_device(0); 151 else 152 dev = omap_device_get_by_hwmod_name(oh_name); 153 154 if (IS_ERR(dev)) { 155 pr_err("%s: Unable to get dev pointer for hwmod %s\n", 156 __func__, oh_name); 157 goto exit; 158 } 159 160 voltdm = voltdm_lookup(vdd_name); 161 if (!voltdm) { 162 pr_err("%s: unable to get vdd pointer for vdd_%s\n", 163 __func__, vdd_name); 164 goto exit; 165 } 166 167 clk = clk_get(NULL, clk_name); 168 if (IS_ERR(clk)) { 169 pr_err("%s: unable to get clk %s\n", __func__, clk_name); 170 goto exit; 171 } 172 173 freq = clk_get_rate(clk); 174 clk_put(clk); 175 176 rcu_read_lock(); 177 opp = dev_pm_opp_find_freq_ceil(dev, &freq); 178 if (IS_ERR(opp)) { 179 rcu_read_unlock(); 180 pr_err("%s: unable to find boot up OPP for vdd_%s\n", 181 __func__, vdd_name); 182 goto exit; 183 } 184 185 bootup_volt = dev_pm_opp_get_voltage(opp); 186 rcu_read_unlock(); 187 if (!bootup_volt) { 188 pr_err("%s: unable to find voltage corresponding to the bootup OPP for vdd_%s\n", 189 __func__, vdd_name); 190 goto exit; 191 } 192 193 voltdm_scale(voltdm, bootup_volt); 194 return 0; 195 196 exit: 197 pr_err("%s: unable to set vdd_%s\n", __func__, vdd_name); 198 return -EINVAL; 199 } 200 201 #ifdef CONFIG_SUSPEND 202 static int omap_pm_enter(suspend_state_t suspend_state) 203 { 204 int ret = 0; 205 206 if (!omap_pm_suspend) 207 return -ENOENT; /* XXX doublecheck */ 208 209 switch (suspend_state) { 210 case PM_SUSPEND_STANDBY: 211 case PM_SUSPEND_MEM: 212 ret = omap_pm_suspend(); 213 break; 214 default: 215 ret = -EINVAL; 216 } 217 218 return ret; 219 } 220 221 static int omap_pm_begin(suspend_state_t state) 222 { 223 cpu_idle_poll_ctrl(true); 224 if (cpu_is_omap34xx()) 225 omap_prcm_irq_prepare(); 226 return 0; 227 } 228 229 static void omap_pm_end(void) 230 { 231 cpu_idle_poll_ctrl(false); 232 } 233 234 static void omap_pm_finish(void) 235 { 236 if (cpu_is_omap34xx()) 237 omap_prcm_irq_complete(); 238 } 239 240 static const struct platform_suspend_ops omap_pm_ops = { 241 .begin = omap_pm_begin, 242 .end = omap_pm_end, 243 .enter = omap_pm_enter, 244 .finish = omap_pm_finish, 245 .valid = suspend_valid_only_mem, 246 }; 247 248 /** 249 * omap_common_suspend_init - Set common suspend routines for OMAP SoCs 250 * @pm_suspend: function pointer to SoC specific suspend function 251 */ 252 void omap_common_suspend_init(void *pm_suspend) 253 { 254 omap_pm_suspend = pm_suspend; 255 suspend_set_ops(&omap_pm_ops); 256 } 257 #endif /* CONFIG_SUSPEND */ 258 259 static void __init omap3_init_voltages(void) 260 { 261 if (!cpu_is_omap34xx()) 262 return; 263 264 omap2_set_init_voltage("mpu_iva", "dpll1_ck", "mpu"); 265 omap2_set_init_voltage("core", "l3_ick", "l3_main"); 266 } 267 268 static void __init omap4_init_voltages(void) 269 { 270 if (!cpu_is_omap44xx()) 271 return; 272 273 omap2_set_init_voltage("mpu", "dpll_mpu_ck", "mpu"); 274 omap2_set_init_voltage("core", "l3_div_ck", "l3_main_1"); 275 omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva"); 276 } 277 278 static inline void omap_init_cpufreq(void) 279 { 280 struct platform_device_info devinfo = { }; 281 282 if (!of_have_populated_dt()) 283 devinfo.name = "omap-cpufreq"; 284 else 285 devinfo.name = "cpufreq-cpu0"; 286 platform_device_register_full(&devinfo); 287 } 288 289 static int __init omap2_common_pm_init(void) 290 { 291 if (!of_have_populated_dt()) 292 omap2_init_processor_devices(); 293 omap_pm_if_init(); 294 295 return 0; 296 } 297 omap_postcore_initcall(omap2_common_pm_init); 298 299 int __init omap2_common_pm_late_init(void) 300 { 301 if (of_have_populated_dt()) { 302 omap3_twl_init(); 303 omap4_twl_init(); 304 } 305 306 /* Init the voltage layer */ 307 omap_pmic_late_init(); 308 omap_voltage_late_init(); 309 310 /* Initialize the voltages */ 311 omap3_init_voltages(); 312 omap4_init_voltages(); 313 314 /* Smartreflex device init */ 315 omap_devinit_smartreflex(); 316 317 /* cpufreq dummy device instantiation */ 318 omap_init_cpufreq(); 319 320 return 0; 321 } 322