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