xref: /linux/drivers/soc/ti/wkup_m3_ipc.c (revision 03b10fecb92143aa2599180ee2efd3388fe6ecc0)
1cdd5de50SDave Gerlach /*
2cdd5de50SDave Gerlach  * AMx3 Wkup M3 IPC driver
3cdd5de50SDave Gerlach  *
4cdd5de50SDave Gerlach  * Copyright (C) 2015 Texas Instruments, Inc.
5cdd5de50SDave Gerlach  *
6cdd5de50SDave Gerlach  * Dave Gerlach <d-gerlach@ti.com>
7cdd5de50SDave Gerlach  *
8cdd5de50SDave Gerlach  * This program is free software; you can redistribute it and/or
9cdd5de50SDave Gerlach  * modify it under the terms of the GNU General Public License
10cdd5de50SDave Gerlach  * version 2 as published by the Free Software Foundation.
11cdd5de50SDave Gerlach  *
12cdd5de50SDave Gerlach  * This program is distributed in the hope that it will be useful,
13cdd5de50SDave Gerlach  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cdd5de50SDave Gerlach  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15cdd5de50SDave Gerlach  * GNU General Public License for more details.
16cdd5de50SDave Gerlach  */
17cdd5de50SDave Gerlach 
18cdd5de50SDave Gerlach #include <linux/err.h>
19cdd5de50SDave Gerlach #include <linux/kernel.h>
20cdd5de50SDave Gerlach #include <linux/kthread.h>
21cdd5de50SDave Gerlach #include <linux/interrupt.h>
22cdd5de50SDave Gerlach #include <linux/irq.h>
23cdd5de50SDave Gerlach #include <linux/module.h>
24cdd5de50SDave Gerlach #include <linux/of.h>
25cdd5de50SDave Gerlach #include <linux/omap-mailbox.h>
26cdd5de50SDave Gerlach #include <linux/platform_device.h>
27cdd5de50SDave Gerlach #include <linux/remoteproc.h>
28cdd5de50SDave Gerlach #include <linux/suspend.h>
29cdd5de50SDave Gerlach #include <linux/wkup_m3_ipc.h>
30cdd5de50SDave Gerlach 
31cdd5de50SDave Gerlach #define AM33XX_CTRL_IPC_REG_COUNT	0x8
32cdd5de50SDave Gerlach #define AM33XX_CTRL_IPC_REG_OFFSET(m)	(0x4 + 4 * (m))
33cdd5de50SDave Gerlach 
34cdd5de50SDave Gerlach /* AM33XX M3_TXEV_EOI register */
35cdd5de50SDave Gerlach #define AM33XX_CONTROL_M3_TXEV_EOI	0x00
36cdd5de50SDave Gerlach 
37cdd5de50SDave Gerlach #define AM33XX_M3_TXEV_ACK		(0x1 << 0)
38cdd5de50SDave Gerlach #define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
39cdd5de50SDave Gerlach 
40cdd5de50SDave Gerlach #define IPC_CMD_DS0			0x4
41cdd5de50SDave Gerlach #define IPC_CMD_STANDBY			0xc
42cdd5de50SDave Gerlach #define IPC_CMD_IDLE			0x10
43cdd5de50SDave Gerlach #define IPC_CMD_RESET			0xe
44cdd5de50SDave Gerlach #define DS_IPC_DEFAULT			0xffffffff
45cdd5de50SDave Gerlach #define M3_VERSION_UNKNOWN		0x0000ffff
46cdd5de50SDave Gerlach #define M3_BASELINE_VERSION		0x191
47cdd5de50SDave Gerlach #define M3_STATUS_RESP_MASK		(0xffff << 16)
48cdd5de50SDave Gerlach #define M3_FW_VERSION_MASK		0xffff
49ec93b62fSDave Gerlach #define M3_WAKE_SRC_MASK		0xff
50cdd5de50SDave Gerlach 
51cdd5de50SDave Gerlach #define M3_STATE_UNKNOWN		0
52cdd5de50SDave Gerlach #define M3_STATE_RESET			1
53cdd5de50SDave Gerlach #define M3_STATE_INITED			2
54cdd5de50SDave Gerlach #define M3_STATE_MSG_FOR_LP		3
55cdd5de50SDave Gerlach #define M3_STATE_MSG_FOR_RESET		4
56cdd5de50SDave Gerlach 
57cdd5de50SDave Gerlach static struct wkup_m3_ipc *m3_ipc_state;
58cdd5de50SDave Gerlach 
59ec93b62fSDave Gerlach static const struct wkup_m3_wakeup_src wakeups[] = {
60*03b10fecSKeerthy 	{.irq_nr = 16,	.src = "PRCM"},
61ec93b62fSDave Gerlach 	{.irq_nr = 35,	.src = "USB0_PHY"},
62ec93b62fSDave Gerlach 	{.irq_nr = 36,	.src = "USB1_PHY"},
63ec93b62fSDave Gerlach 	{.irq_nr = 40,	.src = "I2C0"},
64ec93b62fSDave Gerlach 	{.irq_nr = 41,	.src = "RTC Timer"},
65ec93b62fSDave Gerlach 	{.irq_nr = 42,	.src = "RTC Alarm"},
66ec93b62fSDave Gerlach 	{.irq_nr = 43,	.src = "Timer0"},
67ec93b62fSDave Gerlach 	{.irq_nr = 44,	.src = "Timer1"},
68ec93b62fSDave Gerlach 	{.irq_nr = 45,	.src = "UART"},
69ec93b62fSDave Gerlach 	{.irq_nr = 46,	.src = "GPIO0"},
70ec93b62fSDave Gerlach 	{.irq_nr = 48,	.src = "MPU_WAKE"},
71ec93b62fSDave Gerlach 	{.irq_nr = 49,	.src = "WDT0"},
72ec93b62fSDave Gerlach 	{.irq_nr = 50,	.src = "WDT1"},
73ec93b62fSDave Gerlach 	{.irq_nr = 51,	.src = "ADC_TSC"},
74ec93b62fSDave Gerlach 	{.irq_nr = 0,	.src = "Unknown"},
75ec93b62fSDave Gerlach };
76ec93b62fSDave Gerlach 
77cdd5de50SDave Gerlach static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc)
78cdd5de50SDave Gerlach {
79cdd5de50SDave Gerlach 	writel(AM33XX_M3_TXEV_ACK,
80cdd5de50SDave Gerlach 	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
81cdd5de50SDave Gerlach }
82cdd5de50SDave Gerlach 
83cdd5de50SDave Gerlach static void am33xx_txev_enable(struct wkup_m3_ipc *m3_ipc)
84cdd5de50SDave Gerlach {
85cdd5de50SDave Gerlach 	writel(AM33XX_M3_TXEV_ENABLE,
86cdd5de50SDave Gerlach 	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
87cdd5de50SDave Gerlach }
88cdd5de50SDave Gerlach 
89cdd5de50SDave Gerlach static void wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc *m3_ipc,
90cdd5de50SDave Gerlach 				   u32 val, int ipc_reg_num)
91cdd5de50SDave Gerlach {
92cdd5de50SDave Gerlach 	if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,
93cdd5de50SDave Gerlach 		 "ipc register operation out of range"))
94cdd5de50SDave Gerlach 		return;
95cdd5de50SDave Gerlach 
96cdd5de50SDave Gerlach 	writel(val, m3_ipc->ipc_mem_base +
97cdd5de50SDave Gerlach 	       AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
98cdd5de50SDave Gerlach }
99cdd5de50SDave Gerlach 
100cdd5de50SDave Gerlach static unsigned int wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc *m3_ipc,
101cdd5de50SDave Gerlach 					  int ipc_reg_num)
102cdd5de50SDave Gerlach {
103cdd5de50SDave Gerlach 	if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,
104cdd5de50SDave Gerlach 		 "ipc register operation out of range"))
105cdd5de50SDave Gerlach 		return 0;
106cdd5de50SDave Gerlach 
107cdd5de50SDave Gerlach 	return readl(m3_ipc->ipc_mem_base +
108cdd5de50SDave Gerlach 		     AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
109cdd5de50SDave Gerlach }
110cdd5de50SDave Gerlach 
111cdd5de50SDave Gerlach static int wkup_m3_fw_version_read(struct wkup_m3_ipc *m3_ipc)
112cdd5de50SDave Gerlach {
113cdd5de50SDave Gerlach 	int val;
114cdd5de50SDave Gerlach 
115cdd5de50SDave Gerlach 	val = wkup_m3_ctrl_ipc_read(m3_ipc, 2);
116cdd5de50SDave Gerlach 
117cdd5de50SDave Gerlach 	return val & M3_FW_VERSION_MASK;
118cdd5de50SDave Gerlach }
119cdd5de50SDave Gerlach 
120cdd5de50SDave Gerlach static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data)
121cdd5de50SDave Gerlach {
122cdd5de50SDave Gerlach 	struct wkup_m3_ipc *m3_ipc = ipc_data;
123cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
124cdd5de50SDave Gerlach 	int ver = 0;
125cdd5de50SDave Gerlach 
126cdd5de50SDave Gerlach 	am33xx_txev_eoi(m3_ipc);
127cdd5de50SDave Gerlach 
128cdd5de50SDave Gerlach 	switch (m3_ipc->state) {
129cdd5de50SDave Gerlach 	case M3_STATE_RESET:
130cdd5de50SDave Gerlach 		ver = wkup_m3_fw_version_read(m3_ipc);
131cdd5de50SDave Gerlach 
132cdd5de50SDave Gerlach 		if (ver == M3_VERSION_UNKNOWN ||
133cdd5de50SDave Gerlach 		    ver < M3_BASELINE_VERSION) {
134cdd5de50SDave Gerlach 			dev_warn(dev, "CM3 Firmware Version %x not supported\n",
135cdd5de50SDave Gerlach 				 ver);
136cdd5de50SDave Gerlach 		} else {
137cdd5de50SDave Gerlach 			dev_info(dev, "CM3 Firmware Version = 0x%x\n", ver);
138cdd5de50SDave Gerlach 		}
139cdd5de50SDave Gerlach 
140cdd5de50SDave Gerlach 		m3_ipc->state = M3_STATE_INITED;
141cdd5de50SDave Gerlach 		complete(&m3_ipc->sync_complete);
142cdd5de50SDave Gerlach 		break;
143cdd5de50SDave Gerlach 	case M3_STATE_MSG_FOR_RESET:
144cdd5de50SDave Gerlach 		m3_ipc->state = M3_STATE_INITED;
145cdd5de50SDave Gerlach 		complete(&m3_ipc->sync_complete);
146cdd5de50SDave Gerlach 		break;
147cdd5de50SDave Gerlach 	case M3_STATE_MSG_FOR_LP:
148cdd5de50SDave Gerlach 		complete(&m3_ipc->sync_complete);
149cdd5de50SDave Gerlach 		break;
150cdd5de50SDave Gerlach 	case M3_STATE_UNKNOWN:
151cdd5de50SDave Gerlach 		dev_warn(dev, "Unknown CM3 State\n");
152cdd5de50SDave Gerlach 	}
153cdd5de50SDave Gerlach 
154cdd5de50SDave Gerlach 	am33xx_txev_enable(m3_ipc);
155cdd5de50SDave Gerlach 
156cdd5de50SDave Gerlach 	return IRQ_HANDLED;
157cdd5de50SDave Gerlach }
158cdd5de50SDave Gerlach 
159cdd5de50SDave Gerlach static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc)
160cdd5de50SDave Gerlach {
161cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
162cdd5de50SDave Gerlach 	mbox_msg_t dummy_msg = 0;
163cdd5de50SDave Gerlach 	int ret;
164cdd5de50SDave Gerlach 
165cdd5de50SDave Gerlach 	if (!m3_ipc->mbox) {
166cdd5de50SDave Gerlach 		dev_err(dev,
167cdd5de50SDave Gerlach 			"No IPC channel to communicate with wkup_m3!\n");
168cdd5de50SDave Gerlach 		return -EIO;
169cdd5de50SDave Gerlach 	}
170cdd5de50SDave Gerlach 
171cdd5de50SDave Gerlach 	/*
172cdd5de50SDave Gerlach 	 * Write a dummy message to the mailbox in order to trigger the RX
173cdd5de50SDave Gerlach 	 * interrupt to alert the M3 that data is available in the IPC
174cdd5de50SDave Gerlach 	 * registers. We must enable the IRQ here and disable it after in
175cdd5de50SDave Gerlach 	 * the RX callback to avoid multiple interrupts being received
176cdd5de50SDave Gerlach 	 * by the CM3.
177cdd5de50SDave Gerlach 	 */
178cdd5de50SDave Gerlach 	ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
179cdd5de50SDave Gerlach 	if (ret < 0) {
180cdd5de50SDave Gerlach 		dev_err(dev, "%s: mbox_send_message() failed: %d\n",
181cdd5de50SDave Gerlach 			__func__, ret);
182cdd5de50SDave Gerlach 		return ret;
183cdd5de50SDave Gerlach 	}
184cdd5de50SDave Gerlach 
185cdd5de50SDave Gerlach 	ret = wait_for_completion_timeout(&m3_ipc->sync_complete,
186cdd5de50SDave Gerlach 					  msecs_to_jiffies(500));
187cdd5de50SDave Gerlach 	if (!ret) {
188cdd5de50SDave Gerlach 		dev_err(dev, "MPU<->CM3 sync failure\n");
189cdd5de50SDave Gerlach 		m3_ipc->state = M3_STATE_UNKNOWN;
190cdd5de50SDave Gerlach 		return -EIO;
191cdd5de50SDave Gerlach 	}
192cdd5de50SDave Gerlach 
193cdd5de50SDave Gerlach 	mbox_client_txdone(m3_ipc->mbox, 0);
194cdd5de50SDave Gerlach 	return 0;
195cdd5de50SDave Gerlach }
196cdd5de50SDave Gerlach 
197cdd5de50SDave Gerlach static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc)
198cdd5de50SDave Gerlach {
199cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
200cdd5de50SDave Gerlach 	mbox_msg_t dummy_msg = 0;
201cdd5de50SDave Gerlach 	int ret;
202cdd5de50SDave Gerlach 
203cdd5de50SDave Gerlach 	if (!m3_ipc->mbox) {
204cdd5de50SDave Gerlach 		dev_err(dev,
205cdd5de50SDave Gerlach 			"No IPC channel to communicate with wkup_m3!\n");
206cdd5de50SDave Gerlach 		return -EIO;
207cdd5de50SDave Gerlach 	}
208cdd5de50SDave Gerlach 
209cdd5de50SDave Gerlach 	ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
210cdd5de50SDave Gerlach 	if (ret < 0) {
211cdd5de50SDave Gerlach 		dev_err(dev, "%s: mbox_send_message() failed: %d\n",
212cdd5de50SDave Gerlach 			__func__, ret);
213cdd5de50SDave Gerlach 		return ret;
214cdd5de50SDave Gerlach 	}
215cdd5de50SDave Gerlach 
216cdd5de50SDave Gerlach 	mbox_client_txdone(m3_ipc->mbox, 0);
217cdd5de50SDave Gerlach 	return 0;
218cdd5de50SDave Gerlach }
219cdd5de50SDave Gerlach 
220cdd5de50SDave Gerlach static int wkup_m3_is_available(struct wkup_m3_ipc *m3_ipc)
221cdd5de50SDave Gerlach {
222cdd5de50SDave Gerlach 	return ((m3_ipc->state != M3_STATE_RESET) &&
223cdd5de50SDave Gerlach 		(m3_ipc->state != M3_STATE_UNKNOWN));
224cdd5de50SDave Gerlach }
225cdd5de50SDave Gerlach 
226cdd5de50SDave Gerlach /* Public functions */
227cdd5de50SDave Gerlach /**
228cdd5de50SDave Gerlach  * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use
229cdd5de50SDave Gerlach  * @mem_type: memory type value read directly from emif
230cdd5de50SDave Gerlach  *
231cdd5de50SDave Gerlach  * wkup_m3 must know what memory type is in use to properly suspend
232cdd5de50SDave Gerlach  * and resume.
233cdd5de50SDave Gerlach  */
234cdd5de50SDave Gerlach static void wkup_m3_set_mem_type(struct wkup_m3_ipc *m3_ipc, int mem_type)
235cdd5de50SDave Gerlach {
236cdd5de50SDave Gerlach 	m3_ipc->mem_type = mem_type;
237cdd5de50SDave Gerlach }
238cdd5de50SDave Gerlach 
239cdd5de50SDave Gerlach /**
240cdd5de50SDave Gerlach  * wkup_m3_set_resume_address - Pass wkup_m3 resume address
241cdd5de50SDave Gerlach  * @addr: Physical address from which resume code should execute
242cdd5de50SDave Gerlach  */
243cdd5de50SDave Gerlach static void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr)
244cdd5de50SDave Gerlach {
245cdd5de50SDave Gerlach 	m3_ipc->resume_addr = (unsigned long)addr;
246cdd5de50SDave Gerlach }
247cdd5de50SDave Gerlach 
248cdd5de50SDave Gerlach /**
249cdd5de50SDave Gerlach  * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend
250cdd5de50SDave Gerlach  *
251cdd5de50SDave Gerlach  * Returns code representing the status of a low power mode transition.
252cdd5de50SDave Gerlach  *	0 - Successful transition
253cdd5de50SDave Gerlach  *	1 - Failure to transition to low power state
254cdd5de50SDave Gerlach  */
255cdd5de50SDave Gerlach static int wkup_m3_request_pm_status(struct wkup_m3_ipc *m3_ipc)
256cdd5de50SDave Gerlach {
257cdd5de50SDave Gerlach 	unsigned int i;
258cdd5de50SDave Gerlach 	int val;
259cdd5de50SDave Gerlach 
260cdd5de50SDave Gerlach 	val = wkup_m3_ctrl_ipc_read(m3_ipc, 1);
261cdd5de50SDave Gerlach 
262cdd5de50SDave Gerlach 	i = M3_STATUS_RESP_MASK & val;
263cdd5de50SDave Gerlach 	i >>= __ffs(M3_STATUS_RESP_MASK);
264cdd5de50SDave Gerlach 
265cdd5de50SDave Gerlach 	return i;
266cdd5de50SDave Gerlach }
267cdd5de50SDave Gerlach 
268cdd5de50SDave Gerlach /**
269cdd5de50SDave Gerlach  * wkup_m3_prepare_low_power - Request preparation for transition to
270cdd5de50SDave Gerlach  *			       low power state
271cdd5de50SDave Gerlach  * @state: A kernel suspend state to enter, either MEM or STANDBY
272cdd5de50SDave Gerlach  *
273cdd5de50SDave Gerlach  * Returns 0 if preparation was successful, otherwise returns error code
274cdd5de50SDave Gerlach  */
275cdd5de50SDave Gerlach static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state)
276cdd5de50SDave Gerlach {
277cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
278cdd5de50SDave Gerlach 	int m3_power_state;
279cdd5de50SDave Gerlach 	int ret = 0;
280cdd5de50SDave Gerlach 
281cdd5de50SDave Gerlach 	if (!wkup_m3_is_available(m3_ipc))
282cdd5de50SDave Gerlach 		return -ENODEV;
283cdd5de50SDave Gerlach 
284cdd5de50SDave Gerlach 	switch (state) {
285cdd5de50SDave Gerlach 	case WKUP_M3_DEEPSLEEP:
286cdd5de50SDave Gerlach 		m3_power_state = IPC_CMD_DS0;
287cdd5de50SDave Gerlach 		break;
288cdd5de50SDave Gerlach 	case WKUP_M3_STANDBY:
289cdd5de50SDave Gerlach 		m3_power_state = IPC_CMD_STANDBY;
290cdd5de50SDave Gerlach 		break;
291cdd5de50SDave Gerlach 	case WKUP_M3_IDLE:
292cdd5de50SDave Gerlach 		m3_power_state = IPC_CMD_IDLE;
293cdd5de50SDave Gerlach 		break;
294cdd5de50SDave Gerlach 	default:
295cdd5de50SDave Gerlach 		return 1;
296cdd5de50SDave Gerlach 	}
297cdd5de50SDave Gerlach 
298cdd5de50SDave Gerlach 	/* Program each required IPC register then write defaults to others */
299cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0);
300cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1);
301cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type, 4);
302cdd5de50SDave Gerlach 
303cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);
304cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3);
305cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
306cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6);
307cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7);
308cdd5de50SDave Gerlach 
309cdd5de50SDave Gerlach 	m3_ipc->state = M3_STATE_MSG_FOR_LP;
310cdd5de50SDave Gerlach 
311cdd5de50SDave Gerlach 	if (state == WKUP_M3_IDLE)
312cdd5de50SDave Gerlach 		ret = wkup_m3_ping_noirq(m3_ipc);
313cdd5de50SDave Gerlach 	else
314cdd5de50SDave Gerlach 		ret = wkup_m3_ping(m3_ipc);
315cdd5de50SDave Gerlach 
316cdd5de50SDave Gerlach 	if (ret) {
317cdd5de50SDave Gerlach 		dev_err(dev, "Unable to ping CM3\n");
318cdd5de50SDave Gerlach 		return ret;
319cdd5de50SDave Gerlach 	}
320cdd5de50SDave Gerlach 
321cdd5de50SDave Gerlach 	return 0;
322cdd5de50SDave Gerlach }
323cdd5de50SDave Gerlach 
324cdd5de50SDave Gerlach /**
325cdd5de50SDave Gerlach  * wkup_m3_finish_low_power - Return m3 to reset state
326cdd5de50SDave Gerlach  *
327cdd5de50SDave Gerlach  * Returns 0 if reset was successful, otherwise returns error code
328cdd5de50SDave Gerlach  */
329cdd5de50SDave Gerlach static int wkup_m3_finish_low_power(struct wkup_m3_ipc *m3_ipc)
330cdd5de50SDave Gerlach {
331cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
332cdd5de50SDave Gerlach 	int ret = 0;
333cdd5de50SDave Gerlach 
334cdd5de50SDave Gerlach 	if (!wkup_m3_is_available(m3_ipc))
335cdd5de50SDave Gerlach 		return -ENODEV;
336cdd5de50SDave Gerlach 
337cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, IPC_CMD_RESET, 1);
338cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);
339cdd5de50SDave Gerlach 
340cdd5de50SDave Gerlach 	m3_ipc->state = M3_STATE_MSG_FOR_RESET;
341cdd5de50SDave Gerlach 
342cdd5de50SDave Gerlach 	ret = wkup_m3_ping(m3_ipc);
343cdd5de50SDave Gerlach 	if (ret) {
344cdd5de50SDave Gerlach 		dev_err(dev, "Unable to ping CM3\n");
345cdd5de50SDave Gerlach 		return ret;
346cdd5de50SDave Gerlach 	}
347cdd5de50SDave Gerlach 
348cdd5de50SDave Gerlach 	return 0;
349cdd5de50SDave Gerlach }
350cdd5de50SDave Gerlach 
3517a872b6fSKeerthy /**
352ec93b62fSDave Gerlach  * wkup_m3_request_wake_src - Get the wakeup source info passed from wkup_m3
353ec93b62fSDave Gerlach  * @m3_ipc: Pointer to wkup_m3_ipc context
354ec93b62fSDave Gerlach  */
355ec93b62fSDave Gerlach static const char *wkup_m3_request_wake_src(struct wkup_m3_ipc *m3_ipc)
356ec93b62fSDave Gerlach {
357ec93b62fSDave Gerlach 	unsigned int wakeup_src_idx;
358ec93b62fSDave Gerlach 	int j, val;
359ec93b62fSDave Gerlach 
360ec93b62fSDave Gerlach 	val = wkup_m3_ctrl_ipc_read(m3_ipc, 6);
361ec93b62fSDave Gerlach 
362ec93b62fSDave Gerlach 	wakeup_src_idx = val & M3_WAKE_SRC_MASK;
363ec93b62fSDave Gerlach 
364ec93b62fSDave Gerlach 	for (j = 0; j < ARRAY_SIZE(wakeups) - 1; j++) {
365ec93b62fSDave Gerlach 		if (wakeups[j].irq_nr == wakeup_src_idx)
366ec93b62fSDave Gerlach 			return wakeups[j].src;
367ec93b62fSDave Gerlach 	}
368ec93b62fSDave Gerlach 	return wakeups[j].src;
369ec93b62fSDave Gerlach }
370ec93b62fSDave Gerlach 
371ec93b62fSDave Gerlach /**
3727a872b6fSKeerthy  * wkup_m3_set_rtc_only - Set the rtc_only flag
3737a872b6fSKeerthy  * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the
3747a872b6fSKeerthy  *                  wakeup src value
3757a872b6fSKeerthy  */
3767a872b6fSKeerthy static void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc)
3777a872b6fSKeerthy {
3787a872b6fSKeerthy 	if (m3_ipc_state)
3797a872b6fSKeerthy 		m3_ipc_state->is_rtc_only = true;
3807a872b6fSKeerthy }
3817a872b6fSKeerthy 
382cdd5de50SDave Gerlach static struct wkup_m3_ipc_ops ipc_ops = {
383cdd5de50SDave Gerlach 	.set_mem_type = wkup_m3_set_mem_type,
384cdd5de50SDave Gerlach 	.set_resume_address = wkup_m3_set_resume_address,
385cdd5de50SDave Gerlach 	.prepare_low_power = wkup_m3_prepare_low_power,
386cdd5de50SDave Gerlach 	.finish_low_power = wkup_m3_finish_low_power,
387cdd5de50SDave Gerlach 	.request_pm_status = wkup_m3_request_pm_status,
388ec93b62fSDave Gerlach 	.request_wake_src = wkup_m3_request_wake_src,
3897a872b6fSKeerthy 	.set_rtc_only = wkup_m3_set_rtc_only,
390cdd5de50SDave Gerlach };
391cdd5de50SDave Gerlach 
392cdd5de50SDave Gerlach /**
393cdd5de50SDave Gerlach  * wkup_m3_ipc_get - Return handle to wkup_m3_ipc
394cdd5de50SDave Gerlach  *
395cdd5de50SDave Gerlach  * Returns NULL if the wkup_m3 is not yet available, otherwise returns
396cdd5de50SDave Gerlach  * pointer to wkup_m3_ipc struct.
397cdd5de50SDave Gerlach  */
398cdd5de50SDave Gerlach struct wkup_m3_ipc *wkup_m3_ipc_get(void)
399cdd5de50SDave Gerlach {
400cdd5de50SDave Gerlach 	if (m3_ipc_state)
401cdd5de50SDave Gerlach 		get_device(m3_ipc_state->dev);
402cdd5de50SDave Gerlach 	else
403cdd5de50SDave Gerlach 		return NULL;
404cdd5de50SDave Gerlach 
405cdd5de50SDave Gerlach 	return m3_ipc_state;
406cdd5de50SDave Gerlach }
407cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_get);
408cdd5de50SDave Gerlach 
409cdd5de50SDave Gerlach /**
410cdd5de50SDave Gerlach  * wkup_m3_ipc_put - Free handle to wkup_m3_ipc returned from wkup_m3_ipc_get
411cdd5de50SDave Gerlach  * @m3_ipc: A pointer to wkup_m3_ipc struct returned by wkup_m3_ipc_get
412cdd5de50SDave Gerlach  */
413cdd5de50SDave Gerlach void wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc)
414cdd5de50SDave Gerlach {
415cdd5de50SDave Gerlach 	if (m3_ipc_state)
416cdd5de50SDave Gerlach 		put_device(m3_ipc_state->dev);
417cdd5de50SDave Gerlach }
418cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_put);
419cdd5de50SDave Gerlach 
420cdd5de50SDave Gerlach static void wkup_m3_rproc_boot_thread(struct wkup_m3_ipc *m3_ipc)
421cdd5de50SDave Gerlach {
422cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
423cdd5de50SDave Gerlach 	int ret;
424cdd5de50SDave Gerlach 
425cdd5de50SDave Gerlach 	init_completion(&m3_ipc->sync_complete);
426cdd5de50SDave Gerlach 
427cdd5de50SDave Gerlach 	ret = rproc_boot(m3_ipc->rproc);
428cdd5de50SDave Gerlach 	if (ret)
429cdd5de50SDave Gerlach 		dev_err(dev, "rproc_boot failed\n");
430cdd5de50SDave Gerlach 
431cdd5de50SDave Gerlach 	do_exit(0);
432cdd5de50SDave Gerlach }
433cdd5de50SDave Gerlach 
434cdd5de50SDave Gerlach static int wkup_m3_ipc_probe(struct platform_device *pdev)
435cdd5de50SDave Gerlach {
436cdd5de50SDave Gerlach 	struct device *dev = &pdev->dev;
437cdd5de50SDave Gerlach 	int irq, ret;
438cdd5de50SDave Gerlach 	phandle rproc_phandle;
439cdd5de50SDave Gerlach 	struct rproc *m3_rproc;
440cdd5de50SDave Gerlach 	struct resource *res;
441cdd5de50SDave Gerlach 	struct task_struct *task;
442cdd5de50SDave Gerlach 	struct wkup_m3_ipc *m3_ipc;
443cdd5de50SDave Gerlach 
444cdd5de50SDave Gerlach 	m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL);
445cdd5de50SDave Gerlach 	if (!m3_ipc)
446cdd5de50SDave Gerlach 		return -ENOMEM;
447cdd5de50SDave Gerlach 
448cdd5de50SDave Gerlach 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
449cdd5de50SDave Gerlach 	m3_ipc->ipc_mem_base = devm_ioremap_resource(dev, res);
450cdd5de50SDave Gerlach 	if (IS_ERR(m3_ipc->ipc_mem_base)) {
451cdd5de50SDave Gerlach 		dev_err(dev, "could not ioremap ipc_mem\n");
452cdd5de50SDave Gerlach 		return PTR_ERR(m3_ipc->ipc_mem_base);
453cdd5de50SDave Gerlach 	}
454cdd5de50SDave Gerlach 
455cdd5de50SDave Gerlach 	irq = platform_get_irq(pdev, 0);
456cdd5de50SDave Gerlach 	if (!irq) {
457cdd5de50SDave Gerlach 		dev_err(&pdev->dev, "no irq resource\n");
458cdd5de50SDave Gerlach 		return -ENXIO;
459cdd5de50SDave Gerlach 	}
460cdd5de50SDave Gerlach 
461cdd5de50SDave Gerlach 	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
462cdd5de50SDave Gerlach 			       0, "wkup_m3_txev", m3_ipc);
463cdd5de50SDave Gerlach 	if (ret) {
464cdd5de50SDave Gerlach 		dev_err(dev, "request_irq failed\n");
465cdd5de50SDave Gerlach 		return ret;
466cdd5de50SDave Gerlach 	}
467cdd5de50SDave Gerlach 
468cdd5de50SDave Gerlach 	m3_ipc->mbox_client.dev = dev;
469cdd5de50SDave Gerlach 	m3_ipc->mbox_client.tx_done = NULL;
470cdd5de50SDave Gerlach 	m3_ipc->mbox_client.tx_prepare = NULL;
471cdd5de50SDave Gerlach 	m3_ipc->mbox_client.rx_callback = NULL;
472cdd5de50SDave Gerlach 	m3_ipc->mbox_client.tx_block = false;
473cdd5de50SDave Gerlach 	m3_ipc->mbox_client.knows_txdone = false;
474cdd5de50SDave Gerlach 
475cdd5de50SDave Gerlach 	m3_ipc->mbox = mbox_request_channel(&m3_ipc->mbox_client, 0);
476cdd5de50SDave Gerlach 
477cdd5de50SDave Gerlach 	if (IS_ERR(m3_ipc->mbox)) {
478cdd5de50SDave Gerlach 		dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n",
479cdd5de50SDave Gerlach 			PTR_ERR(m3_ipc->mbox));
480cdd5de50SDave Gerlach 		return PTR_ERR(m3_ipc->mbox);
481cdd5de50SDave Gerlach 	}
482cdd5de50SDave Gerlach 
483cdd5de50SDave Gerlach 	if (of_property_read_u32(dev->of_node, "ti,rproc", &rproc_phandle)) {
484cdd5de50SDave Gerlach 		dev_err(&pdev->dev, "could not get rproc phandle\n");
485cdd5de50SDave Gerlach 		ret = -ENODEV;
486cdd5de50SDave Gerlach 		goto err_free_mbox;
487cdd5de50SDave Gerlach 	}
488cdd5de50SDave Gerlach 
489cdd5de50SDave Gerlach 	m3_rproc = rproc_get_by_phandle(rproc_phandle);
490cdd5de50SDave Gerlach 	if (!m3_rproc) {
491cdd5de50SDave Gerlach 		dev_err(&pdev->dev, "could not get rproc handle\n");
492cdd5de50SDave Gerlach 		ret = -EPROBE_DEFER;
493cdd5de50SDave Gerlach 		goto err_free_mbox;
494cdd5de50SDave Gerlach 	}
495cdd5de50SDave Gerlach 
496cdd5de50SDave Gerlach 	m3_ipc->rproc = m3_rproc;
497cdd5de50SDave Gerlach 	m3_ipc->dev = dev;
498cdd5de50SDave Gerlach 	m3_ipc->state = M3_STATE_RESET;
499cdd5de50SDave Gerlach 
500cdd5de50SDave Gerlach 	m3_ipc->ops = &ipc_ops;
501cdd5de50SDave Gerlach 
502cdd5de50SDave Gerlach 	/*
503cdd5de50SDave Gerlach 	 * Wait for firmware loading completion in a thread so we
504cdd5de50SDave Gerlach 	 * can boot the wkup_m3 as soon as it's ready without holding
505cdd5de50SDave Gerlach 	 * up kernel boot
506cdd5de50SDave Gerlach 	 */
507cdd5de50SDave Gerlach 	task = kthread_run((void *)wkup_m3_rproc_boot_thread, m3_ipc,
508cdd5de50SDave Gerlach 			   "wkup_m3_rproc_loader");
509cdd5de50SDave Gerlach 
510cdd5de50SDave Gerlach 	if (IS_ERR(task)) {
511cdd5de50SDave Gerlach 		dev_err(dev, "can't create rproc_boot thread\n");
51236b29eb3SWei Yongjun 		ret = PTR_ERR(task);
513cdd5de50SDave Gerlach 		goto err_put_rproc;
514cdd5de50SDave Gerlach 	}
515cdd5de50SDave Gerlach 
516cdd5de50SDave Gerlach 	m3_ipc_state = m3_ipc;
517cdd5de50SDave Gerlach 
518cdd5de50SDave Gerlach 	return 0;
519cdd5de50SDave Gerlach 
520cdd5de50SDave Gerlach err_put_rproc:
521cdd5de50SDave Gerlach 	rproc_put(m3_rproc);
522cdd5de50SDave Gerlach err_free_mbox:
523cdd5de50SDave Gerlach 	mbox_free_channel(m3_ipc->mbox);
524cdd5de50SDave Gerlach 	return ret;
525cdd5de50SDave Gerlach }
526cdd5de50SDave Gerlach 
527cdd5de50SDave Gerlach static int wkup_m3_ipc_remove(struct platform_device *pdev)
528cdd5de50SDave Gerlach {
529cdd5de50SDave Gerlach 	mbox_free_channel(m3_ipc_state->mbox);
530cdd5de50SDave Gerlach 
531cdd5de50SDave Gerlach 	rproc_shutdown(m3_ipc_state->rproc);
532cdd5de50SDave Gerlach 	rproc_put(m3_ipc_state->rproc);
533cdd5de50SDave Gerlach 
534cdd5de50SDave Gerlach 	m3_ipc_state = NULL;
535cdd5de50SDave Gerlach 
536cdd5de50SDave Gerlach 	return 0;
537cdd5de50SDave Gerlach }
538cdd5de50SDave Gerlach 
539990c1009SArnd Bergmann static int __maybe_unused wkup_m3_ipc_suspend(struct device *dev)
5407a872b6fSKeerthy {
5417a872b6fSKeerthy 	/*
5427a872b6fSKeerthy 	 * Nothing needs to be done on suspend even with rtc_only flag set
5437a872b6fSKeerthy 	 */
5447a872b6fSKeerthy 	return 0;
5457a872b6fSKeerthy }
5467a872b6fSKeerthy 
547990c1009SArnd Bergmann static int __maybe_unused wkup_m3_ipc_resume(struct device *dev)
5487a872b6fSKeerthy {
5497a872b6fSKeerthy 	if (m3_ipc_state->is_rtc_only) {
5507a872b6fSKeerthy 		rproc_shutdown(m3_ipc_state->rproc);
5517a872b6fSKeerthy 		rproc_boot(m3_ipc_state->rproc);
5527a872b6fSKeerthy 	}
5537a872b6fSKeerthy 
5547a872b6fSKeerthy 	m3_ipc_state->is_rtc_only = false;
5557a872b6fSKeerthy 
5567a872b6fSKeerthy 	return 0;
5577a872b6fSKeerthy }
5587a872b6fSKeerthy 
5597a872b6fSKeerthy static const struct dev_pm_ops wkup_m3_ipc_pm_ops = {
5607a872b6fSKeerthy 	SET_SYSTEM_SLEEP_PM_OPS(wkup_m3_ipc_suspend, wkup_m3_ipc_resume)
5617a872b6fSKeerthy };
5627a872b6fSKeerthy 
563cdd5de50SDave Gerlach static const struct of_device_id wkup_m3_ipc_of_match[] = {
564cdd5de50SDave Gerlach 	{ .compatible = "ti,am3352-wkup-m3-ipc", },
565cdd5de50SDave Gerlach 	{ .compatible = "ti,am4372-wkup-m3-ipc", },
566cdd5de50SDave Gerlach 	{},
567cdd5de50SDave Gerlach };
568cdd5de50SDave Gerlach MODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match);
569cdd5de50SDave Gerlach 
570cdd5de50SDave Gerlach static struct platform_driver wkup_m3_ipc_driver = {
571cdd5de50SDave Gerlach 	.probe = wkup_m3_ipc_probe,
572cdd5de50SDave Gerlach 	.remove = wkup_m3_ipc_remove,
573cdd5de50SDave Gerlach 	.driver = {
574cdd5de50SDave Gerlach 		.name = "wkup_m3_ipc",
575cdd5de50SDave Gerlach 		.of_match_table = wkup_m3_ipc_of_match,
5767a872b6fSKeerthy 		.pm = &wkup_m3_ipc_pm_ops,
577cdd5de50SDave Gerlach 	},
578cdd5de50SDave Gerlach };
579cdd5de50SDave Gerlach 
580cdd5de50SDave Gerlach module_platform_driver(wkup_m3_ipc_driver);
581cdd5de50SDave Gerlach 
582cdd5de50SDave Gerlach MODULE_LICENSE("GPL v2");
583cdd5de50SDave Gerlach MODULE_DESCRIPTION("wkup m3 remote processor ipc driver");
584cdd5de50SDave Gerlach MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
585