1 /*
2 * AM33XX Power Management Routines
3 *
4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation version 2.
9 *
10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11 * kind, whether express or implied; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
16 #include <linux/kernel.h>
17 #include <linux/init.h>
18 #include <linux/clk.h>
19 #include <linux/console.h>
20 #include <linux/err.h>
21 #include <linux/firmware.h>
22 #include <linux/io.h>
23 #include <linux/platform_device.h>
24 #include <linux/sched.h>
25 #include <linux/suspend.h>
26 #include <linux/completion.h>
28 #include <plat/prcm.h>
29 #include <plat/mailbox.h>
30 #include <plat/sram.h>
31 #include <plat/omap_hwmod.h>
32 #include <plat/omap_device.h>
34 #include <asm/suspend.h>
35 #include <asm/proc-fns.h>
36 #include <asm/sizes.h>
38 #include "pm.h"
39 #include "pm33xx.h"
40 #include "clockdomain.h"
41 #include "powerdomain.h"
43 void (*am33xx_do_wfi_sram)(void);
45 #define DS_MODE DS0_ID /* DS0/1_ID */
47 #ifdef CONFIG_SUSPEND
49 void __iomem *ipc_regs;
50 void __iomem *m3_eoi;
51 void __iomem *m3_code;
53 bool enable_deep_sleep = true;
55 static struct device *mpu_dev;
56 static struct omap_mbox *m3_mbox;
58 static int core_suspend_stat = -1;
59 static int m3_state = M3_STATE_UNKNOWN;
61 static suspend_state_t suspend_state = PM_SUSPEND_ON;
63 struct a8_wkup_m3_ipc_data {
64 int resume_addr;
65 int sleep_mode;
66 int ipc_data1;
67 int ipc_data2;
68 } am33xx_lp_ipc;
70 static int am33xx_ipc_cmd(struct a8_wkup_m3_ipc_data *);
71 static int am33xx_verify_lp_state(void);
72 static void am33xx_m3_state_machine_reset(void);
73 static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm;
74 static struct clockdomain *gfx_l3_clkdm, *gfx_l4ls_clkdm;
76 static DECLARE_COMPLETION(a8_m3_sync);
78 static int am33xx_do_sram_idle(long unsigned int state)
79 {
80 am33xx_do_wfi_sram();
81 return 0;
82 }
84 static int am33xx_pm_suspend(void)
85 {
86 int state, ret = 0;
88 struct omap_hwmod *cpgmac_oh, *gpmc_oh, *usb_oh;
90 cpgmac_oh = omap_hwmod_lookup("cpgmac0");
91 usb_oh = omap_hwmod_lookup("usb_otg_hs");
92 gpmc_oh = omap_hwmod_lookup("gpmc");
94 omap_hwmod_enable(cpgmac_oh);
95 omap_hwmod_enable(usb_oh);
96 omap_hwmod_enable(gpmc_oh);
98 omap_hwmod_idle(cpgmac_oh);
99 omap_hwmod_idle(usb_oh);
100 omap_hwmod_idle(gpmc_oh);
102 if(gfx_l3_clkdm && gfx_l4ls_clkdm) {
103 clkdm_sleep(gfx_l3_clkdm);
104 clkdm_sleep(gfx_l4ls_clkdm);
105 }
107 /* Try to put GFX to sleep */
108 if (gfx_pwrdm)
109 pwrdm_set_next_pwrst(gfx_pwrdm, PWRDM_POWER_OFF);
110 else
111 pr_err("Could not program GFX to low power state\n");
113 ret = cpu_suspend(0, am33xx_do_sram_idle);
115 if (gfx_pwrdm) {
116 state = pwrdm_read_pwrst(gfx_pwrdm);
117 if (state != PWRDM_POWER_OFF)
118 pr_err("GFX domain did not transition to low power state\n");
119 else
120 pr_info("GFX domain entered low power state\n");
121 }
123 /* XXX: Why do we need to wakeup the clockdomains? */
124 if(gfx_l3_clkdm && gfx_l4ls_clkdm) {
125 clkdm_wakeup(gfx_l3_clkdm);
126 clkdm_wakeup(gfx_l4ls_clkdm);
127 }
129 core_suspend_stat = ret;
131 return ret;
132 }
134 static int am33xx_pm_enter(suspend_state_t unused)
135 {
136 int ret = 0;
138 switch (suspend_state) {
139 case PM_SUSPEND_STANDBY:
140 case PM_SUSPEND_MEM:
141 ret = am33xx_pm_suspend();
142 break;
143 default:
144 ret = -EINVAL;
145 }
147 return ret;
148 }
150 static int am33xx_pm_begin(suspend_state_t state)
151 {
152 int ret = 0;
154 disable_hlt();
156 am33xx_lp_ipc.resume_addr = DS_RESUME_ADDR;
157 am33xx_lp_ipc.sleep_mode = DS_MODE;
158 am33xx_lp_ipc.ipc_data1 = DS_IPC_DEFAULT;
159 am33xx_lp_ipc.ipc_data2 = DS_IPC_DEFAULT;
161 am33xx_ipc_cmd(&am33xx_lp_ipc);
163 m3_state = M3_STATE_MSG_FOR_LP;
165 omap_mbox_enable_irq(m3_mbox, IRQ_RX);
167 ret = omap_mbox_msg_send(m3_mbox, 0xABCDABCD);
168 if (ret) {
169 pr_err("A8<->CM3 MSG for LP failed\n");
170 am33xx_m3_state_machine_reset();
171 ret = -1;
172 }
174 if (!wait_for_completion_timeout(&a8_m3_sync, msecs_to_jiffies(5000))) {
175 pr_err("A8<->CM3 sync failure\n");
176 am33xx_m3_state_machine_reset();
177 ret = -1;
178 } else {
179 pr_debug("Message sent for entering %s\n",
180 (DS_MODE == DS0_ID ? "DS0" : "DS1"));
181 omap_mbox_disable_irq(m3_mbox, IRQ_RX);
182 }
184 suspend_state = state;
185 return ret;
186 }
188 static void am33xx_m3_state_machine_reset(void)
189 {
190 int ret = 0;
192 am33xx_lp_ipc.resume_addr = 0x0;
193 am33xx_lp_ipc.sleep_mode = 0xe;
194 am33xx_lp_ipc.ipc_data1 = DS_IPC_DEFAULT;
195 am33xx_lp_ipc.ipc_data2 = DS_IPC_DEFAULT;
197 am33xx_ipc_cmd(&am33xx_lp_ipc);
199 m3_state = M3_STATE_MSG_FOR_RESET;
201 ret = omap_mbox_msg_send(m3_mbox, 0xABCDABCD);
202 if (!ret) {
203 pr_debug("Message sent for resetting M3 state machine\n");
204 } else {
205 pr_debug("Could not reset M3 state machine!!!\n");
206 m3_state = M3_STATE_UNKNOWN;
207 }
208 }
210 static void am33xx_pm_end(void)
211 {
212 int ret;
214 suspend_state = PM_SUSPEND_ON;
216 ret = am33xx_verify_lp_state();
218 omap_mbox_enable_irq(m3_mbox, IRQ_RX);
220 am33xx_m3_state_machine_reset();
222 enable_hlt();
224 return;
225 }
227 static const struct platform_suspend_ops am33xx_pm_ops = {
228 .begin = am33xx_pm_begin,
229 .end = am33xx_pm_end,
230 .enter = am33xx_pm_enter,
231 .valid = suspend_valid_only_mem,
232 };
234 int am33xx_ipc_cmd(struct a8_wkup_m3_ipc_data *data)
235 {
236 writel(data->resume_addr, ipc_regs);
237 writel(data->sleep_mode, ipc_regs + 0x4);
238 writel(data->ipc_data1, ipc_regs + 0x8);
239 writel(data->ipc_data2, ipc_regs + 0xc);
241 return 0;
242 }
244 /* return 0 if no reset M3 needed, 1 otherwise */
245 static int am33xx_verify_lp_state(void)
246 {
247 int status, ret = 0;
249 if (core_suspend_stat) {
250 pr_err("Kernel core reported suspend failure\n");
251 ret = -1;
252 goto clear_old_status;
253 }
255 status = readl(ipc_regs + 0x4);
256 status &= 0xffff0000;
258 if (status == 0x0) {
259 pr_info("Successfully transitioned all domains to low power state\n");
260 goto clear_old_status;
261 } else if (status == 0x10000) {
262 pr_info("Could enter low power state\n"
263 "Please check for active clocks in PER domain\n");
264 ret = -1;
265 goto clear_old_status;
266 } else {
267 pr_info("Status = %0x\n", status);
268 ret = -1;
269 }
271 clear_old_status:
272 /* After decoding write back the bad status */
273 status = readl(ipc_regs + 0x4);
274 status &= 0xffff0000;
275 status |= 0x10000;
276 writel(status, ipc_regs + 0x4);
278 return ret;
279 }
281 /*
282 * Dummy notifier for the mailbox
283 * TODO: Can this be completely removed?
284 */
285 int wkup_m3_mbox_msg(struct notifier_block *self, unsigned long len, void *msg)
286 {
287 return 0;
288 }
290 static struct notifier_block wkup_m3_mbox_notifier = {
291 .notifier_call = wkup_m3_mbox_msg,
292 };
294 /* Interrupt from M3 to A8 */
295 static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
296 {
297 writel(0x1, m3_eoi);
299 if (m3_state == M3_STATE_RESET) {
300 m3_state = M3_STATE_INITED;
301 } else if (m3_state == M3_STATE_MSG_FOR_RESET) {
302 m3_state = M3_STATE_INITED;
303 omap_mbox_msg_rx_flush(m3_mbox);
304 if (m3_mbox->ops->ack_irq)
305 m3_mbox->ops->ack_irq(m3_mbox, IRQ_RX);
306 } else if (m3_state == M3_STATE_MSG_FOR_LP) {
307 /* Read back the MBOX and disable the interrupt to M3 since we are going down */
308 omap_mbox_msg_rx_flush(m3_mbox);
309 if (m3_mbox->ops->ack_irq)
310 m3_mbox->ops->ack_irq(m3_mbox, IRQ_RX);
311 complete(&a8_m3_sync);
312 } else if (m3_state == M3_STATE_UNKNOWN) {
313 pr_err("IRQ %d with CM3 in unknown state\n", irq);
314 omap_mbox_msg_rx_flush(m3_mbox);
315 if (m3_mbox->ops->ack_irq)
316 m3_mbox->ops->ack_irq(m3_mbox, IRQ_RX);
317 return IRQ_NONE;
318 }
320 writel(0x0, m3_eoi);
322 return IRQ_HANDLED;
323 }
325 /* Initiliaze WKUP_M3, load the binary blob and let it run */
326 static int wkup_m3_init(void)
327 {
328 struct clk *m3_clk;
329 struct omap_hwmod *wkup_m3_oh;
330 const struct firmware *firmware;
331 int ret = 0;
333 wkup_m3_oh = omap_hwmod_lookup("wkup_m3");
335 if (!wkup_m3_oh) {
336 pr_err("%s: could not find omap_hwmod\n", __func__);
337 ret = -ENODEV;
338 goto exit;
339 }
341 ipc_regs = ioremap(A8_M3_IPC_REGS, 0x4*8);
342 if (!ipc_regs) {
343 pr_err("Could not ioremap the IPC area\b");
344 ret = -ENOMEM;
345 goto exit;
346 }
348 m3_eoi = ioremap(M3_TXEV_EOI, 0x4);
349 if (!m3_eoi) {
350 pr_err("Could not ioremap the EOI register\n");
351 ret = -ENOMEM;
352 goto err1;
353 }
355 /* Reserve the MBOX for sending messages to M3 */
356 m3_mbox = omap_mbox_get("wkup_m3", &wkup_m3_mbox_notifier);
357 if (IS_ERR(m3_mbox)) {
358 pr_err("Could not reserve mailbox for A8->M3 IPC\n");
359 ret = -ENODEV;
360 goto err2;
361 }
363 /* Enable access to the M3 code and data area from A8 */
364 m3_clk = clk_get(NULL, "wkup_m3_fck");
365 if (IS_ERR(m3_clk)) {
366 pr_err("%s failed to enable WKUP_M3 clock\n", __func__);
367 goto err3;
368 }
370 if (clk_enable(m3_clk)) {
371 pr_err("%s WKUP_M3: clock enable Failed\n", __func__);
372 goto err4;
373 }
375 m3_code = ioremap(M3_UMEM, SZ_16K);
376 if (!m3_code) {
377 pr_err("%s Could not ioremap M3 code space\n", __func__);
378 ret = -ENOMEM;
379 goto err5;
380 }
382 /* Now try to load the firware */
383 ret = request_firmware(&firmware, "cm3-firmware.bin", mpu_dev);
384 if (ret < 0) {
385 dev_err(mpu_dev, "request_firmware failed\n");
386 goto err6;
387 } else {
388 memcpy(m3_code, firmware->data, firmware->size);
389 pr_info("Copied the M3 firmware to UMEM\n");
390 }
392 /* Request the IRQ before M3 is released from reset */
393 ret = request_irq(AM33XX_IRQ_M3_M3SP_TXEV, wkup_m3_txev_handler,
394 IRQF_DISABLED, "wkup_m3_txev", NULL);
395 if (ret) {
396 pr_err("%s request_irq failed for 0x%x\n", __func__,
397 AM33XX_IRQ_M3_M3SP_TXEV);
398 goto err6;
399 }
401 m3_state = M3_STATE_RESET;
403 ret = omap_hwmod_deassert_hardreset(wkup_m3_oh, "wkup_m3");
404 if (ret) {
405 pr_err("Could not deassert the reset for WKUP_M3\n");
406 goto err6;
407 } else {
408 return 0;
409 }
411 err6:
412 release_firmware(firmware);
413 iounmap(m3_code);
414 err5:
415 clk_disable(m3_clk);
416 err4:
417 clk_put(m3_clk);
418 err3:
419 omap_mbox_put(m3_mbox, &wkup_m3_mbox_notifier);
420 err2:
421 iounmap(m3_eoi);
422 err1:
423 iounmap(ipc_regs);
424 exit:
425 return ret;
426 }
427 #endif /* CONFIG_SUSPEND */
429 /*
430 * Push the minimal suspend-resume code to SRAM
431 */
432 void am33xx_push_sram_idle(void)
433 {
434 am33xx_do_wfi_sram = omap_sram_push(am33xx_do_wfi, am33xx_do_wfi_sz);
435 }
437 /*
438 * Enable hardware supervised mode for all clockdomains if it's
439 * supported. Initiate sleep transition for other clockdomains, if
440 * they are not used
441 */
442 static int __init clkdms_setup(struct clockdomain *clkdm, void *unused)
443 {
444 if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
445 atomic_read(&clkdm->usecount) == 0)
446 clkdm_sleep(clkdm);
447 return 0;
448 }
450 static int __init am33xx_pm_init(void)
451 {
452 int ret;
454 if (!cpu_is_am33xx())
455 return -ENODEV;
457 pr_info("Power Management for AM33XX family\n");
459 (void) clkdm_for_each(clkdms_setup, NULL);
461 /* CEFUSE domain should be turned off post bootup */
462 cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm");
463 if (cefuse_pwrdm == NULL)
464 printk(KERN_ERR "Failed to get cefuse_pwrdm\n");
465 else
466 pwrdm_set_next_pwrst(cefuse_pwrdm, PWRDM_POWER_OFF);
468 gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
469 if (gfx_pwrdm == NULL)
470 printk(KERN_ERR "Failed to get gfx_pwrdm\n");
472 gfx_l3_clkdm = clkdm_lookup("gfx_l3_clkdm");
473 if (gfx_l3_clkdm == NULL)
474 printk(KERN_ERR "Failed to get gfx_l3_clkdm\n");
476 gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
477 if (gfx_l4ls_clkdm == NULL)
478 printk(KERN_ERR "Failed to get gfx_l4ls_gfx_clkdm\n");
480 #ifdef CONFIG_SUSPEND
481 mpu_dev = omap_device_get_by_hwmod_name("mpu");
483 if (!mpu_dev) {
484 pr_warning("%s: unable to get the mpu device\n", __func__);
485 return -EINVAL;
486 }
488 ret = wkup_m3_init();
490 if (ret) {
491 pr_err("Could not initialise WKUP_M3. "
492 "Power management will be compromised\n");
493 enable_deep_sleep = false;
494 }
496 if (enable_deep_sleep)
497 suspend_set_ops(&am33xx_pm_ops);
498 #endif /* CONFIG_SUSPEND */
500 return ret;
501 }
502 late_initcall(am33xx_pm_init);