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