xref: /openbmc/linux/drivers/soc/ti/wkup_m3_ipc.c (revision b11403c9)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cdd5de50SDave Gerlach /*
3cdd5de50SDave Gerlach  * AMx3 Wkup M3 IPC driver
4cdd5de50SDave Gerlach  *
5cdd5de50SDave Gerlach  * Copyright (C) 2015 Texas Instruments, Inc.
6cdd5de50SDave Gerlach  *
7cdd5de50SDave Gerlach  * Dave Gerlach <d-gerlach@ti.com>
8cdd5de50SDave Gerlach  */
9cdd5de50SDave Gerlach 
102a21f9e6SDave Gerlach #include <linux/debugfs.h>
11cdd5de50SDave Gerlach #include <linux/err.h>
12ea082040SDave Gerlach #include <linux/firmware.h>
13cdd5de50SDave Gerlach #include <linux/kernel.h>
14cdd5de50SDave Gerlach #include <linux/kthread.h>
15cdd5de50SDave Gerlach #include <linux/interrupt.h>
16cdd5de50SDave Gerlach #include <linux/irq.h>
17cdd5de50SDave Gerlach #include <linux/module.h>
18cdd5de50SDave Gerlach #include <linux/of.h>
19cdd5de50SDave Gerlach #include <linux/omap-mailbox.h>
20cdd5de50SDave Gerlach #include <linux/platform_device.h>
21cdd5de50SDave Gerlach #include <linux/remoteproc.h>
22cdd5de50SDave Gerlach #include <linux/suspend.h>
23cdd5de50SDave Gerlach #include <linux/wkup_m3_ipc.h>
24cdd5de50SDave Gerlach 
25cdd5de50SDave Gerlach #define AM33XX_CTRL_IPC_REG_COUNT	0x8
26cdd5de50SDave Gerlach #define AM33XX_CTRL_IPC_REG_OFFSET(m)	(0x4 + 4 * (m))
27cdd5de50SDave Gerlach 
28cdd5de50SDave Gerlach /* AM33XX M3_TXEV_EOI register */
29cdd5de50SDave Gerlach #define AM33XX_CONTROL_M3_TXEV_EOI	0x00
30cdd5de50SDave Gerlach 
31cdd5de50SDave Gerlach #define AM33XX_M3_TXEV_ACK		(0x1 << 0)
32cdd5de50SDave Gerlach #define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
33cdd5de50SDave Gerlach 
34cdd5de50SDave Gerlach #define IPC_CMD_DS0			0x4
35cdd5de50SDave Gerlach #define IPC_CMD_STANDBY			0xc
36cdd5de50SDave Gerlach #define IPC_CMD_IDLE			0x10
37cdd5de50SDave Gerlach #define IPC_CMD_RESET			0xe
38cdd5de50SDave Gerlach #define DS_IPC_DEFAULT			0xffffffff
39cdd5de50SDave Gerlach #define M3_VERSION_UNKNOWN		0x0000ffff
40cdd5de50SDave Gerlach #define M3_BASELINE_VERSION		0x191
41cdd5de50SDave Gerlach #define M3_STATUS_RESP_MASK		(0xffff << 16)
42cdd5de50SDave Gerlach #define M3_FW_VERSION_MASK		0xffff
43ec93b62fSDave Gerlach #define M3_WAKE_SRC_MASK		0xff
44cdd5de50SDave Gerlach 
45f2260414SDave Gerlach #define IPC_MEM_TYPE_SHIFT		(0x0)
46f2260414SDave Gerlach #define IPC_MEM_TYPE_MASK		(0x7 << 0)
47f2260414SDave Gerlach #define IPC_VTT_STAT_SHIFT		(0x3)
48f2260414SDave Gerlach #define IPC_VTT_STAT_MASK		(0x1 << 3)
49f2260414SDave Gerlach #define IPC_VTT_GPIO_PIN_SHIFT		(0x4)
50f2260414SDave Gerlach #define IPC_VTT_GPIO_PIN_MASK		(0x3f << 4)
511dcbae86SDave Gerlach #define IPC_IO_ISOLATION_STAT_SHIFT	(10)
521dcbae86SDave Gerlach #define IPC_IO_ISOLATION_STAT_MASK	(0x1 << 10)
53f2260414SDave Gerlach 
542a21f9e6SDave Gerlach #define IPC_DBG_HALT_SHIFT		(11)
552a21f9e6SDave Gerlach #define IPC_DBG_HALT_MASK		(0x1 << 11)
562a21f9e6SDave Gerlach 
57cdd5de50SDave Gerlach #define M3_STATE_UNKNOWN		0
58cdd5de50SDave Gerlach #define M3_STATE_RESET			1
59cdd5de50SDave Gerlach #define M3_STATE_INITED			2
60cdd5de50SDave Gerlach #define M3_STATE_MSG_FOR_LP		3
61cdd5de50SDave Gerlach #define M3_STATE_MSG_FOR_RESET		4
62cdd5de50SDave Gerlach 
63ea082040SDave Gerlach #define WKUP_M3_SD_FW_MAGIC		0x570C
64ea082040SDave Gerlach 
65ea082040SDave Gerlach #define WKUP_M3_DMEM_START		0x80000
66ea082040SDave Gerlach #define WKUP_M3_AUXDATA_OFFSET		0x1000
67ea082040SDave Gerlach #define WKUP_M3_AUXDATA_SIZE		0xFF
68ea082040SDave Gerlach 
69cdd5de50SDave Gerlach static struct wkup_m3_ipc *m3_ipc_state;
70cdd5de50SDave Gerlach 
71ec93b62fSDave Gerlach static const struct wkup_m3_wakeup_src wakeups[] = {
7203b10fecSKeerthy 	{.irq_nr = 16,	.src = "PRCM"},
73ec93b62fSDave Gerlach 	{.irq_nr = 35,	.src = "USB0_PHY"},
74ec93b62fSDave Gerlach 	{.irq_nr = 36,	.src = "USB1_PHY"},
75ec93b62fSDave Gerlach 	{.irq_nr = 40,	.src = "I2C0"},
76ec93b62fSDave Gerlach 	{.irq_nr = 41,	.src = "RTC Timer"},
77ec93b62fSDave Gerlach 	{.irq_nr = 42,	.src = "RTC Alarm"},
78ec93b62fSDave Gerlach 	{.irq_nr = 43,	.src = "Timer0"},
79ec93b62fSDave Gerlach 	{.irq_nr = 44,	.src = "Timer1"},
80ec93b62fSDave Gerlach 	{.irq_nr = 45,	.src = "UART"},
81ec93b62fSDave Gerlach 	{.irq_nr = 46,	.src = "GPIO0"},
82ec93b62fSDave Gerlach 	{.irq_nr = 48,	.src = "MPU_WAKE"},
83ec93b62fSDave Gerlach 	{.irq_nr = 49,	.src = "WDT0"},
84ec93b62fSDave Gerlach 	{.irq_nr = 50,	.src = "WDT1"},
85ec93b62fSDave Gerlach 	{.irq_nr = 51,	.src = "ADC_TSC"},
86ec93b62fSDave Gerlach 	{.irq_nr = 0,	.src = "Unknown"},
87ec93b62fSDave Gerlach };
88ec93b62fSDave Gerlach 
89ea082040SDave Gerlach /**
90ea082040SDave Gerlach  * wkup_m3_copy_aux_data - Copy auxiliary data to special region of m3 dmem
91ea082040SDave Gerlach  * @data - pointer to data
92ea082040SDave Gerlach  * @sz - size of data to copy (limit 256 bytes)
93ea082040SDave Gerlach  *
94ea082040SDave Gerlach  * Copies any additional blob of data to the wkup_m3 dmem to be used by the
95ea082040SDave Gerlach  * firmware
96ea082040SDave Gerlach  */
wkup_m3_copy_aux_data(struct wkup_m3_ipc * m3_ipc,const void * data,int sz)97ea082040SDave Gerlach static unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc,
98ea082040SDave Gerlach 					   const void *data, int sz)
99ea082040SDave Gerlach {
100ea082040SDave Gerlach 	unsigned long aux_data_dev_addr;
101ea082040SDave Gerlach 	void *aux_data_addr;
102ea082040SDave Gerlach 
103ea082040SDave Gerlach 	aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET;
104ea082040SDave Gerlach 	aux_data_addr = rproc_da_to_va(m3_ipc->rproc,
105ea082040SDave Gerlach 				       aux_data_dev_addr,
106ea082040SDave Gerlach 				       WKUP_M3_AUXDATA_SIZE,
107ea082040SDave Gerlach 				       NULL);
108ea082040SDave Gerlach 	memcpy(aux_data_addr, data, sz);
109ea082040SDave Gerlach 
110ea082040SDave Gerlach 	return WKUP_M3_AUXDATA_OFFSET;
111ea082040SDave Gerlach }
112ea082040SDave Gerlach 
wkup_m3_scale_data_fw_cb(const struct firmware * fw,void * context)113ea082040SDave Gerlach static void wkup_m3_scale_data_fw_cb(const struct firmware *fw, void *context)
114ea082040SDave Gerlach {
115ea082040SDave Gerlach 	unsigned long val, aux_base;
116ea082040SDave Gerlach 	struct wkup_m3_scale_data_header hdr;
117ea082040SDave Gerlach 	struct wkup_m3_ipc *m3_ipc = context;
118ea082040SDave Gerlach 	struct device *dev = m3_ipc->dev;
119ea082040SDave Gerlach 
120ea082040SDave Gerlach 	if (!fw) {
121ea082040SDave Gerlach 		dev_err(dev, "Voltage scale fw name given but file missing.\n");
122ea082040SDave Gerlach 		return;
123ea082040SDave Gerlach 	}
124ea082040SDave Gerlach 
125ea082040SDave Gerlach 	memcpy(&hdr, fw->data, sizeof(hdr));
126ea082040SDave Gerlach 
127ea082040SDave Gerlach 	if (hdr.magic != WKUP_M3_SD_FW_MAGIC) {
128ea082040SDave Gerlach 		dev_err(dev, "PM: Voltage Scale Data binary does not appear valid.\n");
129ea082040SDave Gerlach 		goto release_sd_fw;
130ea082040SDave Gerlach 	}
131ea082040SDave Gerlach 
132ea082040SDave Gerlach 	aux_base = wkup_m3_copy_aux_data(m3_ipc, fw->data + sizeof(hdr),
133ea082040SDave Gerlach 					 fw->size - sizeof(hdr));
134ea082040SDave Gerlach 
135ea082040SDave Gerlach 	val = (aux_base + hdr.sleep_offset);
136ea082040SDave Gerlach 	val |= ((aux_base + hdr.wake_offset) << 16);
137ea082040SDave Gerlach 
138ea082040SDave Gerlach 	m3_ipc->volt_scale_offsets = val;
139ea082040SDave Gerlach 
140ea082040SDave Gerlach release_sd_fw:
141ea082040SDave Gerlach 	release_firmware(fw);
142ea082040SDave Gerlach };
143ea082040SDave Gerlach 
wkup_m3_init_scale_data(struct wkup_m3_ipc * m3_ipc,struct device * dev)144ea082040SDave Gerlach static int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc,
145ea082040SDave Gerlach 				   struct device *dev)
146ea082040SDave Gerlach {
147ea082040SDave Gerlach 	int ret = 0;
148ea082040SDave Gerlach 
149ea082040SDave Gerlach 	/*
150ea082040SDave Gerlach 	 * If no name is provided, user has already been warned, pm will
151ea082040SDave Gerlach 	 * still work so return 0
152ea082040SDave Gerlach 	 */
153ea082040SDave Gerlach 
154ea082040SDave Gerlach 	if (!m3_ipc->sd_fw_name)
155ea082040SDave Gerlach 		return ret;
156ea082040SDave Gerlach 
157ea082040SDave Gerlach 	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
158ea082040SDave Gerlach 				      m3_ipc->sd_fw_name, dev, GFP_ATOMIC,
159ea082040SDave Gerlach 				      m3_ipc, wkup_m3_scale_data_fw_cb);
160ea082040SDave Gerlach 
161ea082040SDave Gerlach 	return ret;
162ea082040SDave Gerlach }
163ea082040SDave Gerlach 
1642a21f9e6SDave Gerlach #ifdef CONFIG_DEBUG_FS
wkup_m3_set_halt_late(bool enabled)1652a21f9e6SDave Gerlach static void wkup_m3_set_halt_late(bool enabled)
1662a21f9e6SDave Gerlach {
1672a21f9e6SDave Gerlach 	if (enabled)
1682a21f9e6SDave Gerlach 		m3_ipc_state->halt = (1 << IPC_DBG_HALT_SHIFT);
1692a21f9e6SDave Gerlach 	else
1702a21f9e6SDave Gerlach 		m3_ipc_state->halt = 0;
1712a21f9e6SDave Gerlach }
1722a21f9e6SDave Gerlach 
option_get(void * data,u64 * val)1732a21f9e6SDave Gerlach static int option_get(void *data, u64 *val)
1742a21f9e6SDave Gerlach {
1752a21f9e6SDave Gerlach 	u32 *option = data;
1762a21f9e6SDave Gerlach 
1772a21f9e6SDave Gerlach 	*val = *option;
1782a21f9e6SDave Gerlach 
1792a21f9e6SDave Gerlach 	return 0;
1802a21f9e6SDave Gerlach }
1812a21f9e6SDave Gerlach 
option_set(void * data,u64 val)1822a21f9e6SDave Gerlach static int option_set(void *data, u64 val)
1832a21f9e6SDave Gerlach {
1842a21f9e6SDave Gerlach 	u32 *option = data;
1852a21f9e6SDave Gerlach 
1862a21f9e6SDave Gerlach 	*option = val;
1872a21f9e6SDave Gerlach 
1882a21f9e6SDave Gerlach 	if (option == &m3_ipc_state->halt) {
1892a21f9e6SDave Gerlach 		if (val)
1902a21f9e6SDave Gerlach 			wkup_m3_set_halt_late(true);
1912a21f9e6SDave Gerlach 		else
1922a21f9e6SDave Gerlach 			wkup_m3_set_halt_late(false);
1932a21f9e6SDave Gerlach 	}
1942a21f9e6SDave Gerlach 
1952a21f9e6SDave Gerlach 	return 0;
1962a21f9e6SDave Gerlach }
1972a21f9e6SDave Gerlach 
1982a21f9e6SDave Gerlach DEFINE_SIMPLE_ATTRIBUTE(wkup_m3_ipc_option_fops, option_get, option_set,
1992a21f9e6SDave Gerlach 			"%llu\n");
2002a21f9e6SDave Gerlach 
wkup_m3_ipc_dbg_init(struct wkup_m3_ipc * m3_ipc)2012a21f9e6SDave Gerlach static int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc)
2022a21f9e6SDave Gerlach {
2032a21f9e6SDave Gerlach 	m3_ipc->dbg_path = debugfs_create_dir("wkup_m3_ipc", NULL);
2042a21f9e6SDave Gerlach 
205*b11403c9SOsama Muhammad 	if (IS_ERR(m3_ipc->dbg_path))
2062a21f9e6SDave Gerlach 		return -EINVAL;
2072a21f9e6SDave Gerlach 
2082a21f9e6SDave Gerlach 	(void)debugfs_create_file("enable_late_halt", 0644,
2092a21f9e6SDave Gerlach 				  m3_ipc->dbg_path,
2102a21f9e6SDave Gerlach 				  &m3_ipc->halt,
2112a21f9e6SDave Gerlach 				  &wkup_m3_ipc_option_fops);
2122a21f9e6SDave Gerlach 
2132a21f9e6SDave Gerlach 	return 0;
2142a21f9e6SDave Gerlach }
2152a21f9e6SDave Gerlach 
wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc * m3_ipc)2162a21f9e6SDave Gerlach static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc)
2172a21f9e6SDave Gerlach {
2182a21f9e6SDave Gerlach 	debugfs_remove_recursive(m3_ipc->dbg_path);
2192a21f9e6SDave Gerlach }
2202a21f9e6SDave Gerlach #else
wkup_m3_ipc_dbg_init(struct wkup_m3_ipc * m3_ipc)2212a21f9e6SDave Gerlach static inline int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc)
2222a21f9e6SDave Gerlach {
2232a21f9e6SDave Gerlach 	return 0;
2242a21f9e6SDave Gerlach }
2252a21f9e6SDave Gerlach 
wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc * m3_ipc)2262a21f9e6SDave Gerlach static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc)
2272a21f9e6SDave Gerlach {
2282a21f9e6SDave Gerlach }
2292a21f9e6SDave Gerlach #endif /* CONFIG_DEBUG_FS */
2302a21f9e6SDave Gerlach 
am33xx_txev_eoi(struct wkup_m3_ipc * m3_ipc)231cdd5de50SDave Gerlach static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc)
232cdd5de50SDave Gerlach {
233cdd5de50SDave Gerlach 	writel(AM33XX_M3_TXEV_ACK,
234cdd5de50SDave Gerlach 	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
235cdd5de50SDave Gerlach }
236cdd5de50SDave Gerlach 
am33xx_txev_enable(struct wkup_m3_ipc * m3_ipc)237cdd5de50SDave Gerlach static void am33xx_txev_enable(struct wkup_m3_ipc *m3_ipc)
238cdd5de50SDave Gerlach {
239cdd5de50SDave Gerlach 	writel(AM33XX_M3_TXEV_ENABLE,
240cdd5de50SDave Gerlach 	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
241cdd5de50SDave Gerlach }
242cdd5de50SDave Gerlach 
wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc * m3_ipc,u32 val,int ipc_reg_num)243cdd5de50SDave Gerlach static void wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc *m3_ipc,
244cdd5de50SDave Gerlach 				   u32 val, int ipc_reg_num)
245cdd5de50SDave Gerlach {
246cdd5de50SDave Gerlach 	if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,
247cdd5de50SDave Gerlach 		 "ipc register operation out of range"))
248cdd5de50SDave Gerlach 		return;
249cdd5de50SDave Gerlach 
250cdd5de50SDave Gerlach 	writel(val, m3_ipc->ipc_mem_base +
251cdd5de50SDave Gerlach 	       AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
252cdd5de50SDave Gerlach }
253cdd5de50SDave Gerlach 
wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc * m3_ipc,int ipc_reg_num)254cdd5de50SDave Gerlach static unsigned int wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc *m3_ipc,
255cdd5de50SDave Gerlach 					  int ipc_reg_num)
256cdd5de50SDave Gerlach {
257cdd5de50SDave Gerlach 	if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,
258cdd5de50SDave Gerlach 		 "ipc register operation out of range"))
259cdd5de50SDave Gerlach 		return 0;
260cdd5de50SDave Gerlach 
261cdd5de50SDave Gerlach 	return readl(m3_ipc->ipc_mem_base +
262cdd5de50SDave Gerlach 		     AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
263cdd5de50SDave Gerlach }
264cdd5de50SDave Gerlach 
wkup_m3_fw_version_read(struct wkup_m3_ipc * m3_ipc)265cdd5de50SDave Gerlach static int wkup_m3_fw_version_read(struct wkup_m3_ipc *m3_ipc)
266cdd5de50SDave Gerlach {
267cdd5de50SDave Gerlach 	int val;
268cdd5de50SDave Gerlach 
269cdd5de50SDave Gerlach 	val = wkup_m3_ctrl_ipc_read(m3_ipc, 2);
270cdd5de50SDave Gerlach 
271cdd5de50SDave Gerlach 	return val & M3_FW_VERSION_MASK;
272cdd5de50SDave Gerlach }
273cdd5de50SDave Gerlach 
wkup_m3_txev_handler(int irq,void * ipc_data)274cdd5de50SDave Gerlach static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data)
275cdd5de50SDave Gerlach {
276cdd5de50SDave Gerlach 	struct wkup_m3_ipc *m3_ipc = ipc_data;
277cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
278cdd5de50SDave Gerlach 	int ver = 0;
279cdd5de50SDave Gerlach 
280cdd5de50SDave Gerlach 	am33xx_txev_eoi(m3_ipc);
281cdd5de50SDave Gerlach 
282cdd5de50SDave Gerlach 	switch (m3_ipc->state) {
283cdd5de50SDave Gerlach 	case M3_STATE_RESET:
284cdd5de50SDave Gerlach 		ver = wkup_m3_fw_version_read(m3_ipc);
285cdd5de50SDave Gerlach 
286cdd5de50SDave Gerlach 		if (ver == M3_VERSION_UNKNOWN ||
287cdd5de50SDave Gerlach 		    ver < M3_BASELINE_VERSION) {
288cdd5de50SDave Gerlach 			dev_warn(dev, "CM3 Firmware Version %x not supported\n",
289cdd5de50SDave Gerlach 				 ver);
290cdd5de50SDave Gerlach 		} else {
291cdd5de50SDave Gerlach 			dev_info(dev, "CM3 Firmware Version = 0x%x\n", ver);
292cdd5de50SDave Gerlach 		}
293cdd5de50SDave Gerlach 
294cdd5de50SDave Gerlach 		m3_ipc->state = M3_STATE_INITED;
295ea082040SDave Gerlach 		wkup_m3_init_scale_data(m3_ipc, dev);
296cdd5de50SDave Gerlach 		complete(&m3_ipc->sync_complete);
297cdd5de50SDave Gerlach 		break;
298cdd5de50SDave Gerlach 	case M3_STATE_MSG_FOR_RESET:
299cdd5de50SDave Gerlach 		m3_ipc->state = M3_STATE_INITED;
300cdd5de50SDave Gerlach 		complete(&m3_ipc->sync_complete);
301cdd5de50SDave Gerlach 		break;
302cdd5de50SDave Gerlach 	case M3_STATE_MSG_FOR_LP:
303cdd5de50SDave Gerlach 		complete(&m3_ipc->sync_complete);
304cdd5de50SDave Gerlach 		break;
305cdd5de50SDave Gerlach 	case M3_STATE_UNKNOWN:
306cdd5de50SDave Gerlach 		dev_warn(dev, "Unknown CM3 State\n");
307cdd5de50SDave Gerlach 	}
308cdd5de50SDave Gerlach 
309cdd5de50SDave Gerlach 	am33xx_txev_enable(m3_ipc);
310cdd5de50SDave Gerlach 
311cdd5de50SDave Gerlach 	return IRQ_HANDLED;
312cdd5de50SDave Gerlach }
313cdd5de50SDave Gerlach 
wkup_m3_ping(struct wkup_m3_ipc * m3_ipc)314cdd5de50SDave Gerlach static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc)
315cdd5de50SDave Gerlach {
316cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
317cdd5de50SDave Gerlach 	mbox_msg_t dummy_msg = 0;
318cdd5de50SDave Gerlach 	int ret;
319cdd5de50SDave Gerlach 
320cdd5de50SDave Gerlach 	if (!m3_ipc->mbox) {
321cdd5de50SDave Gerlach 		dev_err(dev,
322cdd5de50SDave Gerlach 			"No IPC channel to communicate with wkup_m3!\n");
323cdd5de50SDave Gerlach 		return -EIO;
324cdd5de50SDave Gerlach 	}
325cdd5de50SDave Gerlach 
326cdd5de50SDave Gerlach 	/*
327cdd5de50SDave Gerlach 	 * Write a dummy message to the mailbox in order to trigger the RX
328cdd5de50SDave Gerlach 	 * interrupt to alert the M3 that data is available in the IPC
329cdd5de50SDave Gerlach 	 * registers. We must enable the IRQ here and disable it after in
330cdd5de50SDave Gerlach 	 * the RX callback to avoid multiple interrupts being received
331cdd5de50SDave Gerlach 	 * by the CM3.
332cdd5de50SDave Gerlach 	 */
333cdd5de50SDave Gerlach 	ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
334cdd5de50SDave Gerlach 	if (ret < 0) {
335cdd5de50SDave Gerlach 		dev_err(dev, "%s: mbox_send_message() failed: %d\n",
336cdd5de50SDave Gerlach 			__func__, ret);
337cdd5de50SDave Gerlach 		return ret;
338cdd5de50SDave Gerlach 	}
339cdd5de50SDave Gerlach 
340cdd5de50SDave Gerlach 	ret = wait_for_completion_timeout(&m3_ipc->sync_complete,
341cdd5de50SDave Gerlach 					  msecs_to_jiffies(500));
342cdd5de50SDave Gerlach 	if (!ret) {
343cdd5de50SDave Gerlach 		dev_err(dev, "MPU<->CM3 sync failure\n");
344cdd5de50SDave Gerlach 		m3_ipc->state = M3_STATE_UNKNOWN;
345cdd5de50SDave Gerlach 		return -EIO;
346cdd5de50SDave Gerlach 	}
347cdd5de50SDave Gerlach 
348cdd5de50SDave Gerlach 	mbox_client_txdone(m3_ipc->mbox, 0);
349cdd5de50SDave Gerlach 	return 0;
350cdd5de50SDave Gerlach }
351cdd5de50SDave Gerlach 
wkup_m3_ping_noirq(struct wkup_m3_ipc * m3_ipc)352cdd5de50SDave Gerlach static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc)
353cdd5de50SDave Gerlach {
354cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
355cdd5de50SDave Gerlach 	mbox_msg_t dummy_msg = 0;
356cdd5de50SDave Gerlach 	int ret;
357cdd5de50SDave Gerlach 
358cdd5de50SDave Gerlach 	if (!m3_ipc->mbox) {
359cdd5de50SDave Gerlach 		dev_err(dev,
360cdd5de50SDave Gerlach 			"No IPC channel to communicate with wkup_m3!\n");
361cdd5de50SDave Gerlach 		return -EIO;
362cdd5de50SDave Gerlach 	}
363cdd5de50SDave Gerlach 
364cdd5de50SDave Gerlach 	ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
365cdd5de50SDave Gerlach 	if (ret < 0) {
366cdd5de50SDave Gerlach 		dev_err(dev, "%s: mbox_send_message() failed: %d\n",
367cdd5de50SDave Gerlach 			__func__, ret);
368cdd5de50SDave Gerlach 		return ret;
369cdd5de50SDave Gerlach 	}
370cdd5de50SDave Gerlach 
371cdd5de50SDave Gerlach 	mbox_client_txdone(m3_ipc->mbox, 0);
372cdd5de50SDave Gerlach 	return 0;
373cdd5de50SDave Gerlach }
374cdd5de50SDave Gerlach 
wkup_m3_is_available(struct wkup_m3_ipc * m3_ipc)375cdd5de50SDave Gerlach static int wkup_m3_is_available(struct wkup_m3_ipc *m3_ipc)
376cdd5de50SDave Gerlach {
377cdd5de50SDave Gerlach 	return ((m3_ipc->state != M3_STATE_RESET) &&
378cdd5de50SDave Gerlach 		(m3_ipc->state != M3_STATE_UNKNOWN));
379cdd5de50SDave Gerlach }
380cdd5de50SDave Gerlach 
wkup_m3_set_vtt_gpio(struct wkup_m3_ipc * m3_ipc,int gpio)381f2260414SDave Gerlach static void wkup_m3_set_vtt_gpio(struct wkup_m3_ipc *m3_ipc, int gpio)
382f2260414SDave Gerlach {
383f2260414SDave Gerlach 	m3_ipc->vtt_conf = (1 << IPC_VTT_STAT_SHIFT) |
384f2260414SDave Gerlach 			    (gpio << IPC_VTT_GPIO_PIN_SHIFT);
385f2260414SDave Gerlach }
386f2260414SDave Gerlach 
wkup_m3_set_io_isolation(struct wkup_m3_ipc * m3_ipc)3871dcbae86SDave Gerlach static void wkup_m3_set_io_isolation(struct wkup_m3_ipc *m3_ipc)
3881dcbae86SDave Gerlach {
3891dcbae86SDave Gerlach 	m3_ipc->isolation_conf = (1 << IPC_IO_ISOLATION_STAT_SHIFT);
3901dcbae86SDave Gerlach }
3911dcbae86SDave Gerlach 
392cdd5de50SDave Gerlach /* Public functions */
393cdd5de50SDave Gerlach /**
394cdd5de50SDave Gerlach  * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use
3957be1c9c1SLee Jones  * @m3_ipc: Pointer to wkup_m3_ipc context
396cdd5de50SDave Gerlach  * @mem_type: memory type value read directly from emif
397cdd5de50SDave Gerlach  *
398cdd5de50SDave Gerlach  * wkup_m3 must know what memory type is in use to properly suspend
399cdd5de50SDave Gerlach  * and resume.
400cdd5de50SDave Gerlach  */
wkup_m3_set_mem_type(struct wkup_m3_ipc * m3_ipc,int mem_type)401cdd5de50SDave Gerlach static void wkup_m3_set_mem_type(struct wkup_m3_ipc *m3_ipc, int mem_type)
402cdd5de50SDave Gerlach {
403cdd5de50SDave Gerlach 	m3_ipc->mem_type = mem_type;
404cdd5de50SDave Gerlach }
405cdd5de50SDave Gerlach 
406cdd5de50SDave Gerlach /**
407cdd5de50SDave Gerlach  * wkup_m3_set_resume_address - Pass wkup_m3 resume address
4087be1c9c1SLee Jones  * @m3_ipc: Pointer to wkup_m3_ipc context
409cdd5de50SDave Gerlach  * @addr: Physical address from which resume code should execute
410cdd5de50SDave Gerlach  */
wkup_m3_set_resume_address(struct wkup_m3_ipc * m3_ipc,void * addr)411cdd5de50SDave Gerlach static void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr)
412cdd5de50SDave Gerlach {
413cdd5de50SDave Gerlach 	m3_ipc->resume_addr = (unsigned long)addr;
414cdd5de50SDave Gerlach }
415cdd5de50SDave Gerlach 
416cdd5de50SDave Gerlach /**
417cdd5de50SDave Gerlach  * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend
4187be1c9c1SLee Jones  * @m3_ipc: Pointer to wkup_m3_ipc context
419cdd5de50SDave Gerlach  *
420cdd5de50SDave Gerlach  * Returns code representing the status of a low power mode transition.
421cdd5de50SDave Gerlach  *	0 - Successful transition
422cdd5de50SDave Gerlach  *	1 - Failure to transition to low power state
423cdd5de50SDave Gerlach  */
wkup_m3_request_pm_status(struct wkup_m3_ipc * m3_ipc)424cdd5de50SDave Gerlach static int wkup_m3_request_pm_status(struct wkup_m3_ipc *m3_ipc)
425cdd5de50SDave Gerlach {
426cdd5de50SDave Gerlach 	unsigned int i;
427cdd5de50SDave Gerlach 	int val;
428cdd5de50SDave Gerlach 
429cdd5de50SDave Gerlach 	val = wkup_m3_ctrl_ipc_read(m3_ipc, 1);
430cdd5de50SDave Gerlach 
431cdd5de50SDave Gerlach 	i = M3_STATUS_RESP_MASK & val;
432cdd5de50SDave Gerlach 	i >>= __ffs(M3_STATUS_RESP_MASK);
433cdd5de50SDave Gerlach 
434cdd5de50SDave Gerlach 	return i;
435cdd5de50SDave Gerlach }
436cdd5de50SDave Gerlach 
437cdd5de50SDave Gerlach /**
438cdd5de50SDave Gerlach  * wkup_m3_prepare_low_power - Request preparation for transition to
439cdd5de50SDave Gerlach  *			       low power state
4407be1c9c1SLee Jones  * @m3_ipc: Pointer to wkup_m3_ipc context
441cdd5de50SDave Gerlach  * @state: A kernel suspend state to enter, either MEM or STANDBY
442cdd5de50SDave Gerlach  *
443cdd5de50SDave Gerlach  * Returns 0 if preparation was successful, otherwise returns error code
444cdd5de50SDave Gerlach  */
wkup_m3_prepare_low_power(struct wkup_m3_ipc * m3_ipc,int state)445cdd5de50SDave Gerlach static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state)
446cdd5de50SDave Gerlach {
447cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
448cdd5de50SDave Gerlach 	int m3_power_state;
449cdd5de50SDave Gerlach 	int ret = 0;
450cdd5de50SDave Gerlach 
451cdd5de50SDave Gerlach 	if (!wkup_m3_is_available(m3_ipc))
452cdd5de50SDave Gerlach 		return -ENODEV;
453cdd5de50SDave Gerlach 
454cdd5de50SDave Gerlach 	switch (state) {
455cdd5de50SDave Gerlach 	case WKUP_M3_DEEPSLEEP:
456cdd5de50SDave Gerlach 		m3_power_state = IPC_CMD_DS0;
457ea082040SDave Gerlach 		wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->volt_scale_offsets, 5);
458cdd5de50SDave Gerlach 		break;
459cdd5de50SDave Gerlach 	case WKUP_M3_STANDBY:
460cdd5de50SDave Gerlach 		m3_power_state = IPC_CMD_STANDBY;
461ea082040SDave Gerlach 		wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
462cdd5de50SDave Gerlach 		break;
463cdd5de50SDave Gerlach 	case WKUP_M3_IDLE:
464cdd5de50SDave Gerlach 		m3_power_state = IPC_CMD_IDLE;
465ea082040SDave Gerlach 		wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
466cdd5de50SDave Gerlach 		break;
467cdd5de50SDave Gerlach 	default:
468cdd5de50SDave Gerlach 		return 1;
469cdd5de50SDave Gerlach 	}
470cdd5de50SDave Gerlach 
471cdd5de50SDave Gerlach 	/* Program each required IPC register then write defaults to others */
472cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0);
473cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1);
474f2260414SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type |
4751dcbae86SDave Gerlach 			       m3_ipc->vtt_conf |
4762a21f9e6SDave Gerlach 			       m3_ipc->isolation_conf |
4772a21f9e6SDave Gerlach 			       m3_ipc->halt, 4);
4782a21f9e6SDave Gerlach 
479cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);
480cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3);
481cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6);
482cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7);
483cdd5de50SDave Gerlach 
484cdd5de50SDave Gerlach 	m3_ipc->state = M3_STATE_MSG_FOR_LP;
485cdd5de50SDave Gerlach 
486cdd5de50SDave Gerlach 	if (state == WKUP_M3_IDLE)
487cdd5de50SDave Gerlach 		ret = wkup_m3_ping_noirq(m3_ipc);
488cdd5de50SDave Gerlach 	else
489cdd5de50SDave Gerlach 		ret = wkup_m3_ping(m3_ipc);
490cdd5de50SDave Gerlach 
491cdd5de50SDave Gerlach 	if (ret) {
492cdd5de50SDave Gerlach 		dev_err(dev, "Unable to ping CM3\n");
493cdd5de50SDave Gerlach 		return ret;
494cdd5de50SDave Gerlach 	}
495cdd5de50SDave Gerlach 
496cdd5de50SDave Gerlach 	return 0;
497cdd5de50SDave Gerlach }
498cdd5de50SDave Gerlach 
499cdd5de50SDave Gerlach /**
500cdd5de50SDave Gerlach  * wkup_m3_finish_low_power - Return m3 to reset state
5017be1c9c1SLee Jones  * @m3_ipc: Pointer to wkup_m3_ipc context
502cdd5de50SDave Gerlach  *
503cdd5de50SDave Gerlach  * Returns 0 if reset was successful, otherwise returns error code
504cdd5de50SDave Gerlach  */
wkup_m3_finish_low_power(struct wkup_m3_ipc * m3_ipc)505cdd5de50SDave Gerlach static int wkup_m3_finish_low_power(struct wkup_m3_ipc *m3_ipc)
506cdd5de50SDave Gerlach {
507cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
508cdd5de50SDave Gerlach 	int ret = 0;
509cdd5de50SDave Gerlach 
510cdd5de50SDave Gerlach 	if (!wkup_m3_is_available(m3_ipc))
511cdd5de50SDave Gerlach 		return -ENODEV;
512cdd5de50SDave Gerlach 
513cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, IPC_CMD_RESET, 1);
514cdd5de50SDave Gerlach 	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);
515cdd5de50SDave Gerlach 
516cdd5de50SDave Gerlach 	m3_ipc->state = M3_STATE_MSG_FOR_RESET;
517cdd5de50SDave Gerlach 
518cdd5de50SDave Gerlach 	ret = wkup_m3_ping(m3_ipc);
519cdd5de50SDave Gerlach 	if (ret) {
520cdd5de50SDave Gerlach 		dev_err(dev, "Unable to ping CM3\n");
521cdd5de50SDave Gerlach 		return ret;
522cdd5de50SDave Gerlach 	}
523cdd5de50SDave Gerlach 
524cdd5de50SDave Gerlach 	return 0;
525cdd5de50SDave Gerlach }
526cdd5de50SDave Gerlach 
5277a872b6fSKeerthy /**
528ec93b62fSDave Gerlach  * wkup_m3_request_wake_src - Get the wakeup source info passed from wkup_m3
529ec93b62fSDave Gerlach  * @m3_ipc: Pointer to wkup_m3_ipc context
530ec93b62fSDave Gerlach  */
wkup_m3_request_wake_src(struct wkup_m3_ipc * m3_ipc)531ec93b62fSDave Gerlach static const char *wkup_m3_request_wake_src(struct wkup_m3_ipc *m3_ipc)
532ec93b62fSDave Gerlach {
533ec93b62fSDave Gerlach 	unsigned int wakeup_src_idx;
534ec93b62fSDave Gerlach 	int j, val;
535ec93b62fSDave Gerlach 
536ec93b62fSDave Gerlach 	val = wkup_m3_ctrl_ipc_read(m3_ipc, 6);
537ec93b62fSDave Gerlach 
538ec93b62fSDave Gerlach 	wakeup_src_idx = val & M3_WAKE_SRC_MASK;
539ec93b62fSDave Gerlach 
540ec93b62fSDave Gerlach 	for (j = 0; j < ARRAY_SIZE(wakeups) - 1; j++) {
541ec93b62fSDave Gerlach 		if (wakeups[j].irq_nr == wakeup_src_idx)
542ec93b62fSDave Gerlach 			return wakeups[j].src;
543ec93b62fSDave Gerlach 	}
544ec93b62fSDave Gerlach 	return wakeups[j].src;
545ec93b62fSDave Gerlach }
546ec93b62fSDave Gerlach 
547ec93b62fSDave Gerlach /**
5487a872b6fSKeerthy  * wkup_m3_set_rtc_only - Set the rtc_only flag
5497be1c9c1SLee Jones  * @m3_ipc: Pointer to wkup_m3_ipc context
5507a872b6fSKeerthy  */
wkup_m3_set_rtc_only(struct wkup_m3_ipc * m3_ipc)5517a872b6fSKeerthy static void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc)
5527a872b6fSKeerthy {
5537a872b6fSKeerthy 	if (m3_ipc_state)
5547a872b6fSKeerthy 		m3_ipc_state->is_rtc_only = true;
5557a872b6fSKeerthy }
5567a872b6fSKeerthy 
557cdd5de50SDave Gerlach static struct wkup_m3_ipc_ops ipc_ops = {
558cdd5de50SDave Gerlach 	.set_mem_type = wkup_m3_set_mem_type,
559cdd5de50SDave Gerlach 	.set_resume_address = wkup_m3_set_resume_address,
560cdd5de50SDave Gerlach 	.prepare_low_power = wkup_m3_prepare_low_power,
561cdd5de50SDave Gerlach 	.finish_low_power = wkup_m3_finish_low_power,
562cdd5de50SDave Gerlach 	.request_pm_status = wkup_m3_request_pm_status,
563ec93b62fSDave Gerlach 	.request_wake_src = wkup_m3_request_wake_src,
5647a872b6fSKeerthy 	.set_rtc_only = wkup_m3_set_rtc_only,
565cdd5de50SDave Gerlach };
566cdd5de50SDave Gerlach 
567cdd5de50SDave Gerlach /**
568cdd5de50SDave Gerlach  * wkup_m3_ipc_get - Return handle to wkup_m3_ipc
569cdd5de50SDave Gerlach  *
570cdd5de50SDave Gerlach  * Returns NULL if the wkup_m3 is not yet available, otherwise returns
571cdd5de50SDave Gerlach  * pointer to wkup_m3_ipc struct.
572cdd5de50SDave Gerlach  */
wkup_m3_ipc_get(void)573cdd5de50SDave Gerlach struct wkup_m3_ipc *wkup_m3_ipc_get(void)
574cdd5de50SDave Gerlach {
575cdd5de50SDave Gerlach 	if (m3_ipc_state)
576cdd5de50SDave Gerlach 		get_device(m3_ipc_state->dev);
577cdd5de50SDave Gerlach 	else
578cdd5de50SDave Gerlach 		return NULL;
579cdd5de50SDave Gerlach 
580cdd5de50SDave Gerlach 	return m3_ipc_state;
581cdd5de50SDave Gerlach }
582cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_get);
583cdd5de50SDave Gerlach 
584cdd5de50SDave Gerlach /**
585cdd5de50SDave Gerlach  * wkup_m3_ipc_put - Free handle to wkup_m3_ipc returned from wkup_m3_ipc_get
586cdd5de50SDave Gerlach  * @m3_ipc: A pointer to wkup_m3_ipc struct returned by wkup_m3_ipc_get
587cdd5de50SDave Gerlach  */
wkup_m3_ipc_put(struct wkup_m3_ipc * m3_ipc)588cdd5de50SDave Gerlach void wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc)
589cdd5de50SDave Gerlach {
590cdd5de50SDave Gerlach 	if (m3_ipc_state)
591cdd5de50SDave Gerlach 		put_device(m3_ipc_state->dev);
592cdd5de50SDave Gerlach }
593cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_put);
594cdd5de50SDave Gerlach 
wkup_m3_rproc_boot_thread(void * arg)595f91140e4SArnd Bergmann static int wkup_m3_rproc_boot_thread(void *arg)
596cdd5de50SDave Gerlach {
597f91140e4SArnd Bergmann 	struct wkup_m3_ipc *m3_ipc = arg;
598cdd5de50SDave Gerlach 	struct device *dev = m3_ipc->dev;
599cdd5de50SDave Gerlach 	int ret;
600cdd5de50SDave Gerlach 
601cdd5de50SDave Gerlach 	init_completion(&m3_ipc->sync_complete);
602cdd5de50SDave Gerlach 
603cdd5de50SDave Gerlach 	ret = rproc_boot(m3_ipc->rproc);
604cdd5de50SDave Gerlach 	if (ret)
605cdd5de50SDave Gerlach 		dev_err(dev, "rproc_boot failed\n");
60603729cfaSDave Gerlach 	else
60703729cfaSDave Gerlach 		m3_ipc_state = m3_ipc;
608cdd5de50SDave Gerlach 
609111e7049SEric W. Biederman 	return 0;
610cdd5de50SDave Gerlach }
611cdd5de50SDave Gerlach 
wkup_m3_ipc_probe(struct platform_device * pdev)612cdd5de50SDave Gerlach static int wkup_m3_ipc_probe(struct platform_device *pdev)
613cdd5de50SDave Gerlach {
614cdd5de50SDave Gerlach 	struct device *dev = &pdev->dev;
615f2260414SDave Gerlach 	int irq, ret, temp;
616cdd5de50SDave Gerlach 	phandle rproc_phandle;
617cdd5de50SDave Gerlach 	struct rproc *m3_rproc;
618cdd5de50SDave Gerlach 	struct task_struct *task;
619cdd5de50SDave Gerlach 	struct wkup_m3_ipc *m3_ipc;
620f2260414SDave Gerlach 	struct device_node *np = dev->of_node;
621cdd5de50SDave Gerlach 
622cdd5de50SDave Gerlach 	m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL);
623cdd5de50SDave Gerlach 	if (!m3_ipc)
624cdd5de50SDave Gerlach 		return -ENOMEM;
625cdd5de50SDave Gerlach 
626a33bfafdSYang Li 	m3_ipc->ipc_mem_base = devm_platform_ioremap_resource(pdev, 0);
627536e23c6SZhen Lei 	if (IS_ERR(m3_ipc->ipc_mem_base))
628cdd5de50SDave Gerlach 		return PTR_ERR(m3_ipc->ipc_mem_base);
629cdd5de50SDave Gerlach 
630cdd5de50SDave Gerlach 	irq = platform_get_irq(pdev, 0);
631c2b03901SYihao Han 	if (irq < 0)
632c3d66a16SMiaoqian Lin 		return irq;
633cdd5de50SDave Gerlach 
634cdd5de50SDave Gerlach 	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
635cdd5de50SDave Gerlach 			       0, "wkup_m3_txev", m3_ipc);
636cdd5de50SDave Gerlach 	if (ret) {
637cdd5de50SDave Gerlach 		dev_err(dev, "request_irq failed\n");
638cdd5de50SDave Gerlach 		return ret;
639cdd5de50SDave Gerlach 	}
640cdd5de50SDave Gerlach 
641cdd5de50SDave Gerlach 	m3_ipc->mbox_client.dev = dev;
642cdd5de50SDave Gerlach 	m3_ipc->mbox_client.tx_done = NULL;
643cdd5de50SDave Gerlach 	m3_ipc->mbox_client.tx_prepare = NULL;
644cdd5de50SDave Gerlach 	m3_ipc->mbox_client.rx_callback = NULL;
645cdd5de50SDave Gerlach 	m3_ipc->mbox_client.tx_block = false;
646cdd5de50SDave Gerlach 	m3_ipc->mbox_client.knows_txdone = false;
647cdd5de50SDave Gerlach 
648cdd5de50SDave Gerlach 	m3_ipc->mbox = mbox_request_channel(&m3_ipc->mbox_client, 0);
649cdd5de50SDave Gerlach 
650cdd5de50SDave Gerlach 	if (IS_ERR(m3_ipc->mbox)) {
651cdd5de50SDave Gerlach 		dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n",
652cdd5de50SDave Gerlach 			PTR_ERR(m3_ipc->mbox));
653cdd5de50SDave Gerlach 		return PTR_ERR(m3_ipc->mbox);
654cdd5de50SDave Gerlach 	}
655cdd5de50SDave Gerlach 
656cdd5de50SDave Gerlach 	if (of_property_read_u32(dev->of_node, "ti,rproc", &rproc_phandle)) {
657cdd5de50SDave Gerlach 		dev_err(&pdev->dev, "could not get rproc phandle\n");
658cdd5de50SDave Gerlach 		ret = -ENODEV;
659cdd5de50SDave Gerlach 		goto err_free_mbox;
660cdd5de50SDave Gerlach 	}
661cdd5de50SDave Gerlach 
662cdd5de50SDave Gerlach 	m3_rproc = rproc_get_by_phandle(rproc_phandle);
663cdd5de50SDave Gerlach 	if (!m3_rproc) {
664cdd5de50SDave Gerlach 		dev_err(&pdev->dev, "could not get rproc handle\n");
665cdd5de50SDave Gerlach 		ret = -EPROBE_DEFER;
666cdd5de50SDave Gerlach 		goto err_free_mbox;
667cdd5de50SDave Gerlach 	}
668cdd5de50SDave Gerlach 
669cdd5de50SDave Gerlach 	m3_ipc->rproc = m3_rproc;
670cdd5de50SDave Gerlach 	m3_ipc->dev = dev;
671cdd5de50SDave Gerlach 	m3_ipc->state = M3_STATE_RESET;
672cdd5de50SDave Gerlach 
673cdd5de50SDave Gerlach 	m3_ipc->ops = &ipc_ops;
674cdd5de50SDave Gerlach 
675f2260414SDave Gerlach 	if (!of_property_read_u32(np, "ti,vtt-gpio-pin", &temp)) {
676f2260414SDave Gerlach 		if (temp >= 0 && temp <= 31)
677f2260414SDave Gerlach 			wkup_m3_set_vtt_gpio(m3_ipc, temp);
678f2260414SDave Gerlach 		else
679f2260414SDave Gerlach 			dev_warn(dev, "Invalid VTT GPIO(%d) pin\n", temp);
680f2260414SDave Gerlach 	}
681f2260414SDave Gerlach 
68282e46bf7SRob Herring 	if (of_property_read_bool(np, "ti,set-io-isolation"))
6831dcbae86SDave Gerlach 		wkup_m3_set_io_isolation(m3_ipc);
6841dcbae86SDave Gerlach 
685ea082040SDave Gerlach 	ret = of_property_read_string(np, "firmware-name",
686ea082040SDave Gerlach 				      &m3_ipc->sd_fw_name);
687ea082040SDave Gerlach 	if (ret) {
688ea082040SDave Gerlach 		dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n");
689b710673eSYang Li 	}
690ea082040SDave Gerlach 
691cdd5de50SDave Gerlach 	/*
692cdd5de50SDave Gerlach 	 * Wait for firmware loading completion in a thread so we
693cdd5de50SDave Gerlach 	 * can boot the wkup_m3 as soon as it's ready without holding
694cdd5de50SDave Gerlach 	 * up kernel boot
695cdd5de50SDave Gerlach 	 */
696f91140e4SArnd Bergmann 	task = kthread_run(wkup_m3_rproc_boot_thread, m3_ipc,
697cdd5de50SDave Gerlach 			   "wkup_m3_rproc_loader");
698cdd5de50SDave Gerlach 
699cdd5de50SDave Gerlach 	if (IS_ERR(task)) {
700cdd5de50SDave Gerlach 		dev_err(dev, "can't create rproc_boot thread\n");
70136b29eb3SWei Yongjun 		ret = PTR_ERR(task);
702cdd5de50SDave Gerlach 		goto err_put_rproc;
703cdd5de50SDave Gerlach 	}
704cdd5de50SDave Gerlach 
7052a21f9e6SDave Gerlach 	wkup_m3_ipc_dbg_init(m3_ipc);
7062a21f9e6SDave Gerlach 
707cdd5de50SDave Gerlach 	return 0;
708cdd5de50SDave Gerlach 
709cdd5de50SDave Gerlach err_put_rproc:
710cdd5de50SDave Gerlach 	rproc_put(m3_rproc);
711cdd5de50SDave Gerlach err_free_mbox:
712cdd5de50SDave Gerlach 	mbox_free_channel(m3_ipc->mbox);
713cdd5de50SDave Gerlach 	return ret;
714cdd5de50SDave Gerlach }
715cdd5de50SDave Gerlach 
wkup_m3_ipc_remove(struct platform_device * pdev)716cdd5de50SDave Gerlach static int wkup_m3_ipc_remove(struct platform_device *pdev)
717cdd5de50SDave Gerlach {
7182a21f9e6SDave Gerlach 	wkup_m3_ipc_dbg_destroy(m3_ipc_state);
7192a21f9e6SDave Gerlach 
720cdd5de50SDave Gerlach 	mbox_free_channel(m3_ipc_state->mbox);
721cdd5de50SDave Gerlach 
722cdd5de50SDave Gerlach 	rproc_shutdown(m3_ipc_state->rproc);
723cdd5de50SDave Gerlach 	rproc_put(m3_ipc_state->rproc);
724cdd5de50SDave Gerlach 
725cdd5de50SDave Gerlach 	m3_ipc_state = NULL;
726cdd5de50SDave Gerlach 
727cdd5de50SDave Gerlach 	return 0;
728cdd5de50SDave Gerlach }
729cdd5de50SDave Gerlach 
wkup_m3_ipc_suspend(struct device * dev)730990c1009SArnd Bergmann static int __maybe_unused wkup_m3_ipc_suspend(struct device *dev)
7317a872b6fSKeerthy {
7327a872b6fSKeerthy 	/*
7337a872b6fSKeerthy 	 * Nothing needs to be done on suspend even with rtc_only flag set
7347a872b6fSKeerthy 	 */
7357a872b6fSKeerthy 	return 0;
7367a872b6fSKeerthy }
7377a872b6fSKeerthy 
wkup_m3_ipc_resume(struct device * dev)738990c1009SArnd Bergmann static int __maybe_unused wkup_m3_ipc_resume(struct device *dev)
7397a872b6fSKeerthy {
7407a872b6fSKeerthy 	if (m3_ipc_state->is_rtc_only) {
7417a872b6fSKeerthy 		rproc_shutdown(m3_ipc_state->rproc);
7427a872b6fSKeerthy 		rproc_boot(m3_ipc_state->rproc);
7437a872b6fSKeerthy 	}
7447a872b6fSKeerthy 
7457a872b6fSKeerthy 	m3_ipc_state->is_rtc_only = false;
7467a872b6fSKeerthy 
7477a872b6fSKeerthy 	return 0;
7487a872b6fSKeerthy }
7497a872b6fSKeerthy 
7507a872b6fSKeerthy static const struct dev_pm_ops wkup_m3_ipc_pm_ops = {
7517a872b6fSKeerthy 	SET_SYSTEM_SLEEP_PM_OPS(wkup_m3_ipc_suspend, wkup_m3_ipc_resume)
7527a872b6fSKeerthy };
7537a872b6fSKeerthy 
754cdd5de50SDave Gerlach static const struct of_device_id wkup_m3_ipc_of_match[] = {
755cdd5de50SDave Gerlach 	{ .compatible = "ti,am3352-wkup-m3-ipc", },
756cdd5de50SDave Gerlach 	{ .compatible = "ti,am4372-wkup-m3-ipc", },
757cdd5de50SDave Gerlach 	{},
758cdd5de50SDave Gerlach };
759cdd5de50SDave Gerlach MODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match);
760cdd5de50SDave Gerlach 
761cdd5de50SDave Gerlach static struct platform_driver wkup_m3_ipc_driver = {
762cdd5de50SDave Gerlach 	.probe = wkup_m3_ipc_probe,
763cdd5de50SDave Gerlach 	.remove = wkup_m3_ipc_remove,
764cdd5de50SDave Gerlach 	.driver = {
765cdd5de50SDave Gerlach 		.name = "wkup_m3_ipc",
766cdd5de50SDave Gerlach 		.of_match_table = wkup_m3_ipc_of_match,
7677a872b6fSKeerthy 		.pm = &wkup_m3_ipc_pm_ops,
768cdd5de50SDave Gerlach 	},
769cdd5de50SDave Gerlach };
770cdd5de50SDave Gerlach 
771cdd5de50SDave Gerlach module_platform_driver(wkup_m3_ipc_driver);
772cdd5de50SDave Gerlach 
773cdd5de50SDave Gerlach MODULE_LICENSE("GPL v2");
774cdd5de50SDave Gerlach MODULE_DESCRIPTION("wkup m3 remote processor ipc driver");
775cdd5de50SDave Gerlach MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
776