162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AMx3 Wkup M3 IPC driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Texas Instruments, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Dave Gerlach <d-gerlach@ti.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/debugfs.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/firmware.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/kthread.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/irq.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/omap-mailbox.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/remoteproc.h>
2262306a36Sopenharmony_ci#include <linux/suspend.h>
2362306a36Sopenharmony_ci#include <linux/wkup_m3_ipc.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define AM33XX_CTRL_IPC_REG_COUNT	0x8
2662306a36Sopenharmony_ci#define AM33XX_CTRL_IPC_REG_OFFSET(m)	(0x4 + 4 * (m))
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* AM33XX M3_TXEV_EOI register */
2962306a36Sopenharmony_ci#define AM33XX_CONTROL_M3_TXEV_EOI	0x00
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define AM33XX_M3_TXEV_ACK		(0x1 << 0)
3262306a36Sopenharmony_ci#define AM33XX_M3_TXEV_ENABLE		(0x0 << 0)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define IPC_CMD_DS0			0x4
3562306a36Sopenharmony_ci#define IPC_CMD_STANDBY			0xc
3662306a36Sopenharmony_ci#define IPC_CMD_IDLE			0x10
3762306a36Sopenharmony_ci#define IPC_CMD_RESET			0xe
3862306a36Sopenharmony_ci#define DS_IPC_DEFAULT			0xffffffff
3962306a36Sopenharmony_ci#define M3_VERSION_UNKNOWN		0x0000ffff
4062306a36Sopenharmony_ci#define M3_BASELINE_VERSION		0x191
4162306a36Sopenharmony_ci#define M3_STATUS_RESP_MASK		(0xffff << 16)
4262306a36Sopenharmony_ci#define M3_FW_VERSION_MASK		0xffff
4362306a36Sopenharmony_ci#define M3_WAKE_SRC_MASK		0xff
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define IPC_MEM_TYPE_SHIFT		(0x0)
4662306a36Sopenharmony_ci#define IPC_MEM_TYPE_MASK		(0x7 << 0)
4762306a36Sopenharmony_ci#define IPC_VTT_STAT_SHIFT		(0x3)
4862306a36Sopenharmony_ci#define IPC_VTT_STAT_MASK		(0x1 << 3)
4962306a36Sopenharmony_ci#define IPC_VTT_GPIO_PIN_SHIFT		(0x4)
5062306a36Sopenharmony_ci#define IPC_VTT_GPIO_PIN_MASK		(0x3f << 4)
5162306a36Sopenharmony_ci#define IPC_IO_ISOLATION_STAT_SHIFT	(10)
5262306a36Sopenharmony_ci#define IPC_IO_ISOLATION_STAT_MASK	(0x1 << 10)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define IPC_DBG_HALT_SHIFT		(11)
5562306a36Sopenharmony_ci#define IPC_DBG_HALT_MASK		(0x1 << 11)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define M3_STATE_UNKNOWN		0
5862306a36Sopenharmony_ci#define M3_STATE_RESET			1
5962306a36Sopenharmony_ci#define M3_STATE_INITED			2
6062306a36Sopenharmony_ci#define M3_STATE_MSG_FOR_LP		3
6162306a36Sopenharmony_ci#define M3_STATE_MSG_FOR_RESET		4
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define WKUP_M3_SD_FW_MAGIC		0x570C
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define WKUP_M3_DMEM_START		0x80000
6662306a36Sopenharmony_ci#define WKUP_M3_AUXDATA_OFFSET		0x1000
6762306a36Sopenharmony_ci#define WKUP_M3_AUXDATA_SIZE		0xFF
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct wkup_m3_ipc *m3_ipc_state;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct wkup_m3_wakeup_src wakeups[] = {
7262306a36Sopenharmony_ci	{.irq_nr = 16,	.src = "PRCM"},
7362306a36Sopenharmony_ci	{.irq_nr = 35,	.src = "USB0_PHY"},
7462306a36Sopenharmony_ci	{.irq_nr = 36,	.src = "USB1_PHY"},
7562306a36Sopenharmony_ci	{.irq_nr = 40,	.src = "I2C0"},
7662306a36Sopenharmony_ci	{.irq_nr = 41,	.src = "RTC Timer"},
7762306a36Sopenharmony_ci	{.irq_nr = 42,	.src = "RTC Alarm"},
7862306a36Sopenharmony_ci	{.irq_nr = 43,	.src = "Timer0"},
7962306a36Sopenharmony_ci	{.irq_nr = 44,	.src = "Timer1"},
8062306a36Sopenharmony_ci	{.irq_nr = 45,	.src = "UART"},
8162306a36Sopenharmony_ci	{.irq_nr = 46,	.src = "GPIO0"},
8262306a36Sopenharmony_ci	{.irq_nr = 48,	.src = "MPU_WAKE"},
8362306a36Sopenharmony_ci	{.irq_nr = 49,	.src = "WDT0"},
8462306a36Sopenharmony_ci	{.irq_nr = 50,	.src = "WDT1"},
8562306a36Sopenharmony_ci	{.irq_nr = 51,	.src = "ADC_TSC"},
8662306a36Sopenharmony_ci	{.irq_nr = 0,	.src = "Unknown"},
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/**
9062306a36Sopenharmony_ci * wkup_m3_copy_aux_data - Copy auxiliary data to special region of m3 dmem
9162306a36Sopenharmony_ci * @data - pointer to data
9262306a36Sopenharmony_ci * @sz - size of data to copy (limit 256 bytes)
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * Copies any additional blob of data to the wkup_m3 dmem to be used by the
9562306a36Sopenharmony_ci * firmware
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_cistatic unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc,
9862306a36Sopenharmony_ci					   const void *data, int sz)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned long aux_data_dev_addr;
10162306a36Sopenharmony_ci	void *aux_data_addr;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET;
10462306a36Sopenharmony_ci	aux_data_addr = rproc_da_to_va(m3_ipc->rproc,
10562306a36Sopenharmony_ci				       aux_data_dev_addr,
10662306a36Sopenharmony_ci				       WKUP_M3_AUXDATA_SIZE,
10762306a36Sopenharmony_ci				       NULL);
10862306a36Sopenharmony_ci	memcpy(aux_data_addr, data, sz);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return WKUP_M3_AUXDATA_OFFSET;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void wkup_m3_scale_data_fw_cb(const struct firmware *fw, void *context)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	unsigned long val, aux_base;
11662306a36Sopenharmony_ci	struct wkup_m3_scale_data_header hdr;
11762306a36Sopenharmony_ci	struct wkup_m3_ipc *m3_ipc = context;
11862306a36Sopenharmony_ci	struct device *dev = m3_ipc->dev;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (!fw) {
12162306a36Sopenharmony_ci		dev_err(dev, "Voltage scale fw name given but file missing.\n");
12262306a36Sopenharmony_ci		return;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	memcpy(&hdr, fw->data, sizeof(hdr));
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (hdr.magic != WKUP_M3_SD_FW_MAGIC) {
12862306a36Sopenharmony_ci		dev_err(dev, "PM: Voltage Scale Data binary does not appear valid.\n");
12962306a36Sopenharmony_ci		goto release_sd_fw;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	aux_base = wkup_m3_copy_aux_data(m3_ipc, fw->data + sizeof(hdr),
13362306a36Sopenharmony_ci					 fw->size - sizeof(hdr));
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	val = (aux_base + hdr.sleep_offset);
13662306a36Sopenharmony_ci	val |= ((aux_base + hdr.wake_offset) << 16);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	m3_ipc->volt_scale_offsets = val;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cirelease_sd_fw:
14162306a36Sopenharmony_ci	release_firmware(fw);
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc,
14562306a36Sopenharmony_ci				   struct device *dev)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int ret = 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * If no name is provided, user has already been warned, pm will
15162306a36Sopenharmony_ci	 * still work so return 0
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (!m3_ipc->sd_fw_name)
15562306a36Sopenharmony_ci		return ret;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
15862306a36Sopenharmony_ci				      m3_ipc->sd_fw_name, dev, GFP_ATOMIC,
15962306a36Sopenharmony_ci				      m3_ipc, wkup_m3_scale_data_fw_cb);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return ret;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
16562306a36Sopenharmony_cistatic void wkup_m3_set_halt_late(bool enabled)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	if (enabled)
16862306a36Sopenharmony_ci		m3_ipc_state->halt = (1 << IPC_DBG_HALT_SHIFT);
16962306a36Sopenharmony_ci	else
17062306a36Sopenharmony_ci		m3_ipc_state->halt = 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int option_get(void *data, u64 *val)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	u32 *option = data;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	*val = *option;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return 0;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int option_set(void *data, u64 val)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u32 *option = data;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	*option = val;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (option == &m3_ipc_state->halt) {
18962306a36Sopenharmony_ci		if (val)
19062306a36Sopenharmony_ci			wkup_m3_set_halt_late(true);
19162306a36Sopenharmony_ci		else
19262306a36Sopenharmony_ci			wkup_m3_set_halt_late(false);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(wkup_m3_ipc_option_fops, option_get, option_set,
19962306a36Sopenharmony_ci			"%llu\n");
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	m3_ipc->dbg_path = debugfs_create_dir("wkup_m3_ipc", NULL);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (IS_ERR(m3_ipc->dbg_path))
20662306a36Sopenharmony_ci		return -EINVAL;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	(void)debugfs_create_file("enable_late_halt", 0644,
20962306a36Sopenharmony_ci				  m3_ipc->dbg_path,
21062306a36Sopenharmony_ci				  &m3_ipc->halt,
21162306a36Sopenharmony_ci				  &wkup_m3_ipc_option_fops);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return 0;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	debugfs_remove_recursive(m3_ipc->dbg_path);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci#else
22162306a36Sopenharmony_cistatic inline int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	writel(AM33XX_M3_TXEV_ACK,
23462306a36Sopenharmony_ci	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void am33xx_txev_enable(struct wkup_m3_ipc *m3_ipc)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	writel(AM33XX_M3_TXEV_ENABLE,
24062306a36Sopenharmony_ci	       m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc *m3_ipc,
24462306a36Sopenharmony_ci				   u32 val, int ipc_reg_num)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,
24762306a36Sopenharmony_ci		 "ipc register operation out of range"))
24862306a36Sopenharmony_ci		return;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	writel(val, m3_ipc->ipc_mem_base +
25162306a36Sopenharmony_ci	       AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic unsigned int wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc *m3_ipc,
25562306a36Sopenharmony_ci					  int ipc_reg_num)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,
25862306a36Sopenharmony_ci		 "ipc register operation out of range"))
25962306a36Sopenharmony_ci		return 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return readl(m3_ipc->ipc_mem_base +
26262306a36Sopenharmony_ci		     AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int wkup_m3_fw_version_read(struct wkup_m3_ipc *m3_ipc)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	int val;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	val = wkup_m3_ctrl_ipc_read(m3_ipc, 2);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return val & M3_FW_VERSION_MASK;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct wkup_m3_ipc *m3_ipc = ipc_data;
27762306a36Sopenharmony_ci	struct device *dev = m3_ipc->dev;
27862306a36Sopenharmony_ci	int ver = 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	am33xx_txev_eoi(m3_ipc);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	switch (m3_ipc->state) {
28362306a36Sopenharmony_ci	case M3_STATE_RESET:
28462306a36Sopenharmony_ci		ver = wkup_m3_fw_version_read(m3_ipc);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		if (ver == M3_VERSION_UNKNOWN ||
28762306a36Sopenharmony_ci		    ver < M3_BASELINE_VERSION) {
28862306a36Sopenharmony_ci			dev_warn(dev, "CM3 Firmware Version %x not supported\n",
28962306a36Sopenharmony_ci				 ver);
29062306a36Sopenharmony_ci		} else {
29162306a36Sopenharmony_ci			dev_info(dev, "CM3 Firmware Version = 0x%x\n", ver);
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		m3_ipc->state = M3_STATE_INITED;
29562306a36Sopenharmony_ci		wkup_m3_init_scale_data(m3_ipc, dev);
29662306a36Sopenharmony_ci		complete(&m3_ipc->sync_complete);
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	case M3_STATE_MSG_FOR_RESET:
29962306a36Sopenharmony_ci		m3_ipc->state = M3_STATE_INITED;
30062306a36Sopenharmony_ci		complete(&m3_ipc->sync_complete);
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	case M3_STATE_MSG_FOR_LP:
30362306a36Sopenharmony_ci		complete(&m3_ipc->sync_complete);
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	case M3_STATE_UNKNOWN:
30662306a36Sopenharmony_ci		dev_warn(dev, "Unknown CM3 State\n");
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	am33xx_txev_enable(m3_ipc);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return IRQ_HANDLED;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct device *dev = m3_ipc->dev;
31762306a36Sopenharmony_ci	mbox_msg_t dummy_msg = 0;
31862306a36Sopenharmony_ci	int ret;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (!m3_ipc->mbox) {
32162306a36Sopenharmony_ci		dev_err(dev,
32262306a36Sopenharmony_ci			"No IPC channel to communicate with wkup_m3!\n");
32362306a36Sopenharmony_ci		return -EIO;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/*
32762306a36Sopenharmony_ci	 * Write a dummy message to the mailbox in order to trigger the RX
32862306a36Sopenharmony_ci	 * interrupt to alert the M3 that data is available in the IPC
32962306a36Sopenharmony_ci	 * registers. We must enable the IRQ here and disable it after in
33062306a36Sopenharmony_ci	 * the RX callback to avoid multiple interrupts being received
33162306a36Sopenharmony_ci	 * by the CM3.
33262306a36Sopenharmony_ci	 */
33362306a36Sopenharmony_ci	ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
33462306a36Sopenharmony_ci	if (ret < 0) {
33562306a36Sopenharmony_ci		dev_err(dev, "%s: mbox_send_message() failed: %d\n",
33662306a36Sopenharmony_ci			__func__, ret);
33762306a36Sopenharmony_ci		return ret;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&m3_ipc->sync_complete,
34162306a36Sopenharmony_ci					  msecs_to_jiffies(500));
34262306a36Sopenharmony_ci	if (!ret) {
34362306a36Sopenharmony_ci		dev_err(dev, "MPU<->CM3 sync failure\n");
34462306a36Sopenharmony_ci		m3_ipc->state = M3_STATE_UNKNOWN;
34562306a36Sopenharmony_ci		return -EIO;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	mbox_client_txdone(m3_ipc->mbox, 0);
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct device *dev = m3_ipc->dev;
35562306a36Sopenharmony_ci	mbox_msg_t dummy_msg = 0;
35662306a36Sopenharmony_ci	int ret;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!m3_ipc->mbox) {
35962306a36Sopenharmony_ci		dev_err(dev,
36062306a36Sopenharmony_ci			"No IPC channel to communicate with wkup_m3!\n");
36162306a36Sopenharmony_ci		return -EIO;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	ret = mbox_send_message(m3_ipc->mbox, &dummy_msg);
36562306a36Sopenharmony_ci	if (ret < 0) {
36662306a36Sopenharmony_ci		dev_err(dev, "%s: mbox_send_message() failed: %d\n",
36762306a36Sopenharmony_ci			__func__, ret);
36862306a36Sopenharmony_ci		return ret;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	mbox_client_txdone(m3_ipc->mbox, 0);
37262306a36Sopenharmony_ci	return 0;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic int wkup_m3_is_available(struct wkup_m3_ipc *m3_ipc)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	return ((m3_ipc->state != M3_STATE_RESET) &&
37862306a36Sopenharmony_ci		(m3_ipc->state != M3_STATE_UNKNOWN));
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void wkup_m3_set_vtt_gpio(struct wkup_m3_ipc *m3_ipc, int gpio)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	m3_ipc->vtt_conf = (1 << IPC_VTT_STAT_SHIFT) |
38462306a36Sopenharmony_ci			    (gpio << IPC_VTT_GPIO_PIN_SHIFT);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void wkup_m3_set_io_isolation(struct wkup_m3_ipc *m3_ipc)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	m3_ipc->isolation_conf = (1 << IPC_IO_ISOLATION_STAT_SHIFT);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/* Public functions */
39362306a36Sopenharmony_ci/**
39462306a36Sopenharmony_ci * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use
39562306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context
39662306a36Sopenharmony_ci * @mem_type: memory type value read directly from emif
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * wkup_m3 must know what memory type is in use to properly suspend
39962306a36Sopenharmony_ci * and resume.
40062306a36Sopenharmony_ci */
40162306a36Sopenharmony_cistatic void wkup_m3_set_mem_type(struct wkup_m3_ipc *m3_ipc, int mem_type)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	m3_ipc->mem_type = mem_type;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci/**
40762306a36Sopenharmony_ci * wkup_m3_set_resume_address - Pass wkup_m3 resume address
40862306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context
40962306a36Sopenharmony_ci * @addr: Physical address from which resume code should execute
41062306a36Sopenharmony_ci */
41162306a36Sopenharmony_cistatic void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	m3_ipc->resume_addr = (unsigned long)addr;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/**
41762306a36Sopenharmony_ci * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend
41862306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * Returns code representing the status of a low power mode transition.
42162306a36Sopenharmony_ci *	0 - Successful transition
42262306a36Sopenharmony_ci *	1 - Failure to transition to low power state
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_cistatic int wkup_m3_request_pm_status(struct wkup_m3_ipc *m3_ipc)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	unsigned int i;
42762306a36Sopenharmony_ci	int val;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	val = wkup_m3_ctrl_ipc_read(m3_ipc, 1);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	i = M3_STATUS_RESP_MASK & val;
43262306a36Sopenharmony_ci	i >>= __ffs(M3_STATUS_RESP_MASK);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return i;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci/**
43862306a36Sopenharmony_ci * wkup_m3_prepare_low_power - Request preparation for transition to
43962306a36Sopenharmony_ci *			       low power state
44062306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context
44162306a36Sopenharmony_ci * @state: A kernel suspend state to enter, either MEM or STANDBY
44262306a36Sopenharmony_ci *
44362306a36Sopenharmony_ci * Returns 0 if preparation was successful, otherwise returns error code
44462306a36Sopenharmony_ci */
44562306a36Sopenharmony_cistatic int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct device *dev = m3_ipc->dev;
44862306a36Sopenharmony_ci	int m3_power_state;
44962306a36Sopenharmony_ci	int ret = 0;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!wkup_m3_is_available(m3_ipc))
45262306a36Sopenharmony_ci		return -ENODEV;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	switch (state) {
45562306a36Sopenharmony_ci	case WKUP_M3_DEEPSLEEP:
45662306a36Sopenharmony_ci		m3_power_state = IPC_CMD_DS0;
45762306a36Sopenharmony_ci		wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->volt_scale_offsets, 5);
45862306a36Sopenharmony_ci		break;
45962306a36Sopenharmony_ci	case WKUP_M3_STANDBY:
46062306a36Sopenharmony_ci		m3_power_state = IPC_CMD_STANDBY;
46162306a36Sopenharmony_ci		wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	case WKUP_M3_IDLE:
46462306a36Sopenharmony_ci		m3_power_state = IPC_CMD_IDLE;
46562306a36Sopenharmony_ci		wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
46662306a36Sopenharmony_ci		break;
46762306a36Sopenharmony_ci	default:
46862306a36Sopenharmony_ci		return 1;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* Program each required IPC register then write defaults to others */
47262306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0);
47362306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1);
47462306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type |
47562306a36Sopenharmony_ci			       m3_ipc->vtt_conf |
47662306a36Sopenharmony_ci			       m3_ipc->isolation_conf |
47762306a36Sopenharmony_ci			       m3_ipc->halt, 4);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);
48062306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3);
48162306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6);
48262306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	m3_ipc->state = M3_STATE_MSG_FOR_LP;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (state == WKUP_M3_IDLE)
48762306a36Sopenharmony_ci		ret = wkup_m3_ping_noirq(m3_ipc);
48862306a36Sopenharmony_ci	else
48962306a36Sopenharmony_ci		ret = wkup_m3_ping(m3_ipc);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (ret) {
49262306a36Sopenharmony_ci		dev_err(dev, "Unable to ping CM3\n");
49362306a36Sopenharmony_ci		return ret;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci/**
50062306a36Sopenharmony_ci * wkup_m3_finish_low_power - Return m3 to reset state
50162306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context
50262306a36Sopenharmony_ci *
50362306a36Sopenharmony_ci * Returns 0 if reset was successful, otherwise returns error code
50462306a36Sopenharmony_ci */
50562306a36Sopenharmony_cistatic int wkup_m3_finish_low_power(struct wkup_m3_ipc *m3_ipc)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct device *dev = m3_ipc->dev;
50862306a36Sopenharmony_ci	int ret = 0;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (!wkup_m3_is_available(m3_ipc))
51162306a36Sopenharmony_ci		return -ENODEV;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, IPC_CMD_RESET, 1);
51462306a36Sopenharmony_ci	wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	m3_ipc->state = M3_STATE_MSG_FOR_RESET;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	ret = wkup_m3_ping(m3_ipc);
51962306a36Sopenharmony_ci	if (ret) {
52062306a36Sopenharmony_ci		dev_err(dev, "Unable to ping CM3\n");
52162306a36Sopenharmony_ci		return ret;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci/**
52862306a36Sopenharmony_ci * wkup_m3_request_wake_src - Get the wakeup source info passed from wkup_m3
52962306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context
53062306a36Sopenharmony_ci */
53162306a36Sopenharmony_cistatic const char *wkup_m3_request_wake_src(struct wkup_m3_ipc *m3_ipc)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	unsigned int wakeup_src_idx;
53462306a36Sopenharmony_ci	int j, val;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	val = wkup_m3_ctrl_ipc_read(m3_ipc, 6);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	wakeup_src_idx = val & M3_WAKE_SRC_MASK;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	for (j = 0; j < ARRAY_SIZE(wakeups) - 1; j++) {
54162306a36Sopenharmony_ci		if (wakeups[j].irq_nr == wakeup_src_idx)
54262306a36Sopenharmony_ci			return wakeups[j].src;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci	return wakeups[j].src;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/**
54862306a36Sopenharmony_ci * wkup_m3_set_rtc_only - Set the rtc_only flag
54962306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context
55062306a36Sopenharmony_ci */
55162306a36Sopenharmony_cistatic void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	if (m3_ipc_state)
55462306a36Sopenharmony_ci		m3_ipc_state->is_rtc_only = true;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic struct wkup_m3_ipc_ops ipc_ops = {
55862306a36Sopenharmony_ci	.set_mem_type = wkup_m3_set_mem_type,
55962306a36Sopenharmony_ci	.set_resume_address = wkup_m3_set_resume_address,
56062306a36Sopenharmony_ci	.prepare_low_power = wkup_m3_prepare_low_power,
56162306a36Sopenharmony_ci	.finish_low_power = wkup_m3_finish_low_power,
56262306a36Sopenharmony_ci	.request_pm_status = wkup_m3_request_pm_status,
56362306a36Sopenharmony_ci	.request_wake_src = wkup_m3_request_wake_src,
56462306a36Sopenharmony_ci	.set_rtc_only = wkup_m3_set_rtc_only,
56562306a36Sopenharmony_ci};
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci/**
56862306a36Sopenharmony_ci * wkup_m3_ipc_get - Return handle to wkup_m3_ipc
56962306a36Sopenharmony_ci *
57062306a36Sopenharmony_ci * Returns NULL if the wkup_m3 is not yet available, otherwise returns
57162306a36Sopenharmony_ci * pointer to wkup_m3_ipc struct.
57262306a36Sopenharmony_ci */
57362306a36Sopenharmony_cistruct wkup_m3_ipc *wkup_m3_ipc_get(void)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	if (m3_ipc_state)
57662306a36Sopenharmony_ci		get_device(m3_ipc_state->dev);
57762306a36Sopenharmony_ci	else
57862306a36Sopenharmony_ci		return NULL;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return m3_ipc_state;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wkup_m3_ipc_get);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci/**
58562306a36Sopenharmony_ci * wkup_m3_ipc_put - Free handle to wkup_m3_ipc returned from wkup_m3_ipc_get
58662306a36Sopenharmony_ci * @m3_ipc: A pointer to wkup_m3_ipc struct returned by wkup_m3_ipc_get
58762306a36Sopenharmony_ci */
58862306a36Sopenharmony_civoid wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	if (m3_ipc_state)
59162306a36Sopenharmony_ci		put_device(m3_ipc_state->dev);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wkup_m3_ipc_put);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic int wkup_m3_rproc_boot_thread(void *arg)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct wkup_m3_ipc *m3_ipc = arg;
59862306a36Sopenharmony_ci	struct device *dev = m3_ipc->dev;
59962306a36Sopenharmony_ci	int ret;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	init_completion(&m3_ipc->sync_complete);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	ret = rproc_boot(m3_ipc->rproc);
60462306a36Sopenharmony_ci	if (ret)
60562306a36Sopenharmony_ci		dev_err(dev, "rproc_boot failed\n");
60662306a36Sopenharmony_ci	else
60762306a36Sopenharmony_ci		m3_ipc_state = m3_ipc;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return 0;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int wkup_m3_ipc_probe(struct platform_device *pdev)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
61562306a36Sopenharmony_ci	int irq, ret, temp;
61662306a36Sopenharmony_ci	phandle rproc_phandle;
61762306a36Sopenharmony_ci	struct rproc *m3_rproc;
61862306a36Sopenharmony_ci	struct task_struct *task;
61962306a36Sopenharmony_ci	struct wkup_m3_ipc *m3_ipc;
62062306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL);
62362306a36Sopenharmony_ci	if (!m3_ipc)
62462306a36Sopenharmony_ci		return -ENOMEM;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	m3_ipc->ipc_mem_base = devm_platform_ioremap_resource(pdev, 0);
62762306a36Sopenharmony_ci	if (IS_ERR(m3_ipc->ipc_mem_base))
62862306a36Sopenharmony_ci		return PTR_ERR(m3_ipc->ipc_mem_base);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
63162306a36Sopenharmony_ci	if (irq < 0)
63262306a36Sopenharmony_ci		return irq;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
63562306a36Sopenharmony_ci			       0, "wkup_m3_txev", m3_ipc);
63662306a36Sopenharmony_ci	if (ret) {
63762306a36Sopenharmony_ci		dev_err(dev, "request_irq failed\n");
63862306a36Sopenharmony_ci		return ret;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	m3_ipc->mbox_client.dev = dev;
64262306a36Sopenharmony_ci	m3_ipc->mbox_client.tx_done = NULL;
64362306a36Sopenharmony_ci	m3_ipc->mbox_client.tx_prepare = NULL;
64462306a36Sopenharmony_ci	m3_ipc->mbox_client.rx_callback = NULL;
64562306a36Sopenharmony_ci	m3_ipc->mbox_client.tx_block = false;
64662306a36Sopenharmony_ci	m3_ipc->mbox_client.knows_txdone = false;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	m3_ipc->mbox = mbox_request_channel(&m3_ipc->mbox_client, 0);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (IS_ERR(m3_ipc->mbox)) {
65162306a36Sopenharmony_ci		dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n",
65262306a36Sopenharmony_ci			PTR_ERR(m3_ipc->mbox));
65362306a36Sopenharmony_ci		return PTR_ERR(m3_ipc->mbox);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (of_property_read_u32(dev->of_node, "ti,rproc", &rproc_phandle)) {
65762306a36Sopenharmony_ci		dev_err(&pdev->dev, "could not get rproc phandle\n");
65862306a36Sopenharmony_ci		ret = -ENODEV;
65962306a36Sopenharmony_ci		goto err_free_mbox;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	m3_rproc = rproc_get_by_phandle(rproc_phandle);
66362306a36Sopenharmony_ci	if (!m3_rproc) {
66462306a36Sopenharmony_ci		dev_err(&pdev->dev, "could not get rproc handle\n");
66562306a36Sopenharmony_ci		ret = -EPROBE_DEFER;
66662306a36Sopenharmony_ci		goto err_free_mbox;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	m3_ipc->rproc = m3_rproc;
67062306a36Sopenharmony_ci	m3_ipc->dev = dev;
67162306a36Sopenharmony_ci	m3_ipc->state = M3_STATE_RESET;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	m3_ipc->ops = &ipc_ops;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (!of_property_read_u32(np, "ti,vtt-gpio-pin", &temp)) {
67662306a36Sopenharmony_ci		if (temp >= 0 && temp <= 31)
67762306a36Sopenharmony_ci			wkup_m3_set_vtt_gpio(m3_ipc, temp);
67862306a36Sopenharmony_ci		else
67962306a36Sopenharmony_ci			dev_warn(dev, "Invalid VTT GPIO(%d) pin\n", temp);
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (of_property_read_bool(np, "ti,set-io-isolation"))
68362306a36Sopenharmony_ci		wkup_m3_set_io_isolation(m3_ipc);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	ret = of_property_read_string(np, "firmware-name",
68662306a36Sopenharmony_ci				      &m3_ipc->sd_fw_name);
68762306a36Sopenharmony_ci	if (ret) {
68862306a36Sopenharmony_ci		dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n");
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/*
69262306a36Sopenharmony_ci	 * Wait for firmware loading completion in a thread so we
69362306a36Sopenharmony_ci	 * can boot the wkup_m3 as soon as it's ready without holding
69462306a36Sopenharmony_ci	 * up kernel boot
69562306a36Sopenharmony_ci	 */
69662306a36Sopenharmony_ci	task = kthread_run(wkup_m3_rproc_boot_thread, m3_ipc,
69762306a36Sopenharmony_ci			   "wkup_m3_rproc_loader");
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (IS_ERR(task)) {
70062306a36Sopenharmony_ci		dev_err(dev, "can't create rproc_boot thread\n");
70162306a36Sopenharmony_ci		ret = PTR_ERR(task);
70262306a36Sopenharmony_ci		goto err_put_rproc;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	wkup_m3_ipc_dbg_init(m3_ipc);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cierr_put_rproc:
71062306a36Sopenharmony_ci	rproc_put(m3_rproc);
71162306a36Sopenharmony_cierr_free_mbox:
71262306a36Sopenharmony_ci	mbox_free_channel(m3_ipc->mbox);
71362306a36Sopenharmony_ci	return ret;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic int wkup_m3_ipc_remove(struct platform_device *pdev)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	wkup_m3_ipc_dbg_destroy(m3_ipc_state);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	mbox_free_channel(m3_ipc_state->mbox);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	rproc_shutdown(m3_ipc_state->rproc);
72362306a36Sopenharmony_ci	rproc_put(m3_ipc_state->rproc);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	m3_ipc_state = NULL;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	return 0;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic int __maybe_unused wkup_m3_ipc_suspend(struct device *dev)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	/*
73362306a36Sopenharmony_ci	 * Nothing needs to be done on suspend even with rtc_only flag set
73462306a36Sopenharmony_ci	 */
73562306a36Sopenharmony_ci	return 0;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int __maybe_unused wkup_m3_ipc_resume(struct device *dev)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	if (m3_ipc_state->is_rtc_only) {
74162306a36Sopenharmony_ci		rproc_shutdown(m3_ipc_state->rproc);
74262306a36Sopenharmony_ci		rproc_boot(m3_ipc_state->rproc);
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	m3_ipc_state->is_rtc_only = false;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return 0;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic const struct dev_pm_ops wkup_m3_ipc_pm_ops = {
75162306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(wkup_m3_ipc_suspend, wkup_m3_ipc_resume)
75262306a36Sopenharmony_ci};
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic const struct of_device_id wkup_m3_ipc_of_match[] = {
75562306a36Sopenharmony_ci	{ .compatible = "ti,am3352-wkup-m3-ipc", },
75662306a36Sopenharmony_ci	{ .compatible = "ti,am4372-wkup-m3-ipc", },
75762306a36Sopenharmony_ci	{},
75862306a36Sopenharmony_ci};
75962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic struct platform_driver wkup_m3_ipc_driver = {
76262306a36Sopenharmony_ci	.probe = wkup_m3_ipc_probe,
76362306a36Sopenharmony_ci	.remove = wkup_m3_ipc_remove,
76462306a36Sopenharmony_ci	.driver = {
76562306a36Sopenharmony_ci		.name = "wkup_m3_ipc",
76662306a36Sopenharmony_ci		.of_match_table = wkup_m3_ipc_of_match,
76762306a36Sopenharmony_ci		.pm = &wkup_m3_ipc_pm_ops,
76862306a36Sopenharmony_ci	},
76962306a36Sopenharmony_ci};
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cimodule_platform_driver(wkup_m3_ipc_driver);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
77462306a36Sopenharmony_ciMODULE_DESCRIPTION("wkup m3 remote processor ipc driver");
77562306a36Sopenharmony_ciMODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
776