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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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