ARM: OMAP: AM33XX: Update the suspend code for DS1
[sitara-epos/sitara-epos-kernel.git] / arch / arm / mach-omap2 / pm33xx.c
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>
27 #include <plat/prcm.h>
28 #include <plat/mailbox.h>
29 #include <plat/sram.h>
30 #include <plat/omap_hwmod.h>
31 #include <plat/omap_device.h>
33 #include <asm/suspend.h>
34 #include <asm/proc-fns.h>
35 #include <asm/sizes.h>
37 #include "pm.h"
38 #include "pm33xx.h"
40 void (*am33xx_do_wfi_sram)(void);
42 #define DS_MODE         DS1_ID  /* DS0/1_ID */
44 #ifdef CONFIG_SUSPEND
45 static int m3_state;
46 struct omap_mbox *m3_mbox;
47 void __iomem *ipc_regs;
48 void __iomem *m3_eoi;
49 void __iomem *m3_code;
51 static struct device *mpu_dev;
52 bool enable_deep_sleep = true;
54 static suspend_state_t suspend_state = PM_SUSPEND_ON;
56 struct a8_wkup_m3_ipc_data {
57         int resume_addr;
58         int sleep_mode;
59         int ipc_data1;
60         int ipc_data2;
61 } am33xx_lp_ipc;
63 static int am33xx_set_low_power_state(struct a8_wkup_m3_ipc_data *);
65 static int am33xx_do_sram_idle(long unsigned int state)
66 {
67         am33xx_do_wfi_sram();
68         return 0;
69 }
71 static inline bool is_suspending(void)
72 {
73         return (suspend_state != PM_SUSPEND_ON) && console_suspend_enabled;
74 }
76 static int am33xx_pm_suspend(void)
77 {
78         int ret = 0;
80         /* Block console output in case it is on one of the OMAP UARTs */
81         if (!is_suspending()) {
82                 if (!console_trylock()) {
83                         ret = -EAGAIN;
84                         goto err;
85                 }
86         }
88         ret = cpu_suspend(0, am33xx_do_sram_idle);
89         if (ret)
90                 pr_err("Could not suspend\n");
91         else
92                 goto done;
94 err:
95         pr_err("Console still active\n");
96 done:
97         return ret;
98 }
100 static int am33xx_pm_enter(suspend_state_t unused)
102         int ret = 0;
104         switch (suspend_state) {
105         case PM_SUSPEND_STANDBY:
106         case PM_SUSPEND_MEM:
107                 ret = am33xx_pm_suspend();
108                 break;
109         default:
110                 ret = -EINVAL;
111         }
113         return ret;
116 static int am33xx_pm_begin(suspend_state_t state)
118         int ret = 0;
120         am33xx_lp_ipc.resume_addr = DS_RESUME_ADDR;
121         am33xx_lp_ipc.sleep_mode  = DS_MODE;
122         am33xx_lp_ipc.ipc_data1   = DS_IPC_DEFAULT;
123         am33xx_lp_ipc.ipc_data2   = DS_IPC_DEFAULT;
125         am33xx_set_low_power_state(&am33xx_lp_ipc);
127         ret = omap_mbox_msg_send(m3_mbox, 0xABCDABCD);
128         if (!ret) {
129                 pr_info("Message sent for entering %s\n",
130                         (DS_MODE == DS0_ID ? "DS0" : "DS1"));
131                 omap_mbox_msg_rx_flush(m3_mbox);
132         }
134         omap_mbox_disable_irq(m3_mbox, IRQ_RX);
136         disable_hlt();
137         suspend_state = state;
139         return ret;
142 static void am33xx_pm_end(void)
144         suspend_state = PM_SUSPEND_ON;
146         if (!is_suspending())
147                 console_unlock();
149         omap_mbox_enable_irq(m3_mbox, IRQ_RX);
150         return;
153 static const struct platform_suspend_ops am33xx_pm_ops = {
154         .begin          = am33xx_pm_begin,
155         .end            = am33xx_pm_end,
156         .enter          = am33xx_pm_enter,
157         .valid          = suspend_valid_only_mem,
158 };
160 int am33xx_set_low_power_state(struct a8_wkup_m3_ipc_data *data)
162         __raw_writel(data->resume_addr, ipc_regs);
163         __raw_writel(data->sleep_mode, ipc_regs + 0x4);
164         __raw_writel(data->ipc_data1, ipc_regs + 0x8);
165         __raw_writel(data->ipc_data2, ipc_regs + 0xc);
167         return 0;
170 /*
171  * Dummy notifier for the mailbox
172  * TODO: Can this be completely removed?
173  */
174 int wkup_m3_mbox_msg(struct notifier_block *self, unsigned long len, void *msg)
176         return 0;
179 static struct notifier_block wkup_m3_mbox_notifier = {
180         .notifier_call = wkup_m3_mbox_msg,
181 };
183 /* Interrupt from M3 to A8 */
184 static irqreturn_t wkup_m3_txev_handler(int irq, void *unused)
186         m3_state++;
188         if (m3_eoi) {
189                 __raw_writel(0x1, m3_eoi);
190                 __raw_writel(0x0, m3_eoi);
191                 return IRQ_HANDLED;
192         } else {
193                 pr_err("%s unexpected interrupt. "
194                 "Something is seriously wrong\n", __func__);
195                 return IRQ_NONE;
196         }
199 /* Initiliaze WKUP_M3, load the binary blob and let it run */
200 static int wkup_m3_init(void)
202         struct clk *m3_clk;
203         struct omap_hwmod *wkup_m3_oh;
204         const struct firmware *firmware;
205         int ret = 0;
207         wkup_m3_oh = omap_hwmod_lookup("wkup_m3");
209         if (!wkup_m3_oh) {
210                 pr_err("%s: could not find omap_hwmod\n", __func__);
211                 ret = -ENODEV;
212                 goto exit;
213         }
215         ipc_regs = ioremap(A8_M3_IPC_REGS, 0x4*8);
216         if (!ipc_regs) {
217                 pr_err("Could not ioremap the IPC area\b");
218                 ret = -ENOMEM;
219                 goto exit;
220         }
222         m3_eoi = ioremap(M3_TXEV_EOI, 0x4);
223         if (!m3_eoi) {
224                 pr_err("Could not ioremap the EOI register\n");
225                 ret = -ENOMEM;
226                 goto err1;
227         }
229         /* Reserve the MBOX for sending messages to M3 */
230         m3_mbox = omap_mbox_get("wkup_m3", &wkup_m3_mbox_notifier);
231         if (IS_ERR(m3_mbox)) {
232                 pr_err("Could not reserve mailbox for A8->M3 IPC\n");
233                 ret = -ENODEV;
234                 goto err2;
235         }
237         /* Enable access to the M3 code and data area from A8 */
238         m3_clk = clk_get(NULL, "wkup_m3_fck");
239         if (IS_ERR(m3_clk)) {
240                 pr_err("%s failed to enable WKUP_M3 clock\n", __func__);
241                 goto err3;
242         }
244         if (clk_enable(m3_clk)) {
245                 pr_err("%s WKUP_M3: clock enable Failed\n", __func__);
246                 goto err4;
247         }
249         m3_code = ioremap(M3_UMEM, SZ_16K);
250         if (!m3_code) {
251                 pr_err("%s Could not ioremap M3 code space\n", __func__);
252                 ret = -ENOMEM;
253                 goto err5;
254         }
256         /* Now try to load the firware */
257         ret = request_firmware(&firmware, "cm3-firmware.bin", mpu_dev);
258         if (ret < 0) {
259                 dev_err(mpu_dev, "request_firmware failed\n");
260                 goto err6;
261         } else {
262                 memcpy(m3_code, firmware->data, firmware->size);
263                 pr_info("Copied the M3 firmware to UMEM\n");
264         }
266         ret = omap_hwmod_deassert_hardreset(wkup_m3_oh, "wkup_m3");
267         if (ret) {
268                 pr_err("Could not deassert the reset for WKUP_M3\n");
269                 goto err6;
270         }
272         ret = request_irq(AM33XX_IRQ_M3_M3SP_TXEV, wkup_m3_txev_handler,
273                           IRQF_DISABLED, "wkup_m3_txev", NULL);
274         if (ret)
275                 pr_err("%s request_irq failed for 0x%x\n", __func__,
276                         AM33XX_IRQ_M3_M3SP_TXEV);
277         else
278                 return 0;
280 err6:
281         release_firmware(firmware);
282         iounmap(m3_code);
283 err5:
284         clk_disable(m3_clk);
285 err4:
286         clk_put(m3_clk);
287 err3:
288         omap_mbox_put(m3_mbox, &wkup_m3_mbox_notifier);
289 err2:
290         iounmap(m3_eoi);
291 err1:
292         iounmap(ipc_regs);
293 exit:
294         return ret;
296 #endif /* CONFIG_SUSPEND */
298 /*
299  * Push the minimal suspend-resume code to SRAM
300  */
301 void am33xx_push_sram_idle(void)
303         am33xx_do_wfi_sram = omap_sram_push(am33xx_do_wfi, am33xx_do_wfi_sz);
306 static int __init am33xx_pm_init(void)
308         int ret;
310         if (!cpu_is_am33xx())
311                 return -ENODEV;
313         pr_info("Power Management for AM33XX family\n");
315 #ifdef CONFIG_SUSPEND
316         mpu_dev = omap_device_get_by_hwmod_name("mpu");
318         if (!mpu_dev) {
319                 pr_warning("%s: unable to get the mpu device\n", __func__);
320                 return -EINVAL;
321         }
323         ret = wkup_m3_init();
325         if (ret) {
326                 pr_err("Could not initialise WKUP_M3. "
327                         "Power management will be compromised\n");
328                 enable_deep_sleep = false;
329         }
331         if (enable_deep_sleep)
332                 suspend_set_ops(&am33xx_pm_ops);
333 #endif /* CONFIG_SUSPEND */
335         return ret;
337 late_initcall(am33xx_pm_init);