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 221cdd5de50SDave Gerlach * @mem_type: memory type value read directly from emif 222cdd5de50SDave Gerlach * 223cdd5de50SDave Gerlach * wkup_m3 must know what memory type is in use to properly suspend 224cdd5de50SDave Gerlach * and resume. 225cdd5de50SDave Gerlach */ 226cdd5de50SDave Gerlach static void wkup_m3_set_mem_type(struct wkup_m3_ipc *m3_ipc, int mem_type) 227cdd5de50SDave Gerlach { 228cdd5de50SDave Gerlach m3_ipc->mem_type = mem_type; 229cdd5de50SDave Gerlach } 230cdd5de50SDave Gerlach 231cdd5de50SDave Gerlach /** 232cdd5de50SDave Gerlach * wkup_m3_set_resume_address - Pass wkup_m3 resume address 233cdd5de50SDave Gerlach * @addr: Physical address from which resume code should execute 234cdd5de50SDave Gerlach */ 235cdd5de50SDave Gerlach static void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr) 236cdd5de50SDave Gerlach { 237cdd5de50SDave Gerlach m3_ipc->resume_addr = (unsigned long)addr; 238cdd5de50SDave Gerlach } 239cdd5de50SDave Gerlach 240cdd5de50SDave Gerlach /** 241cdd5de50SDave Gerlach * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend 242cdd5de50SDave Gerlach * 243cdd5de50SDave Gerlach * Returns code representing the status of a low power mode transition. 244cdd5de50SDave Gerlach * 0 - Successful transition 245cdd5de50SDave Gerlach * 1 - Failure to transition to low power state 246cdd5de50SDave Gerlach */ 247cdd5de50SDave Gerlach static int wkup_m3_request_pm_status(struct wkup_m3_ipc *m3_ipc) 248cdd5de50SDave Gerlach { 249cdd5de50SDave Gerlach unsigned int i; 250cdd5de50SDave Gerlach int val; 251cdd5de50SDave Gerlach 252cdd5de50SDave Gerlach val = wkup_m3_ctrl_ipc_read(m3_ipc, 1); 253cdd5de50SDave Gerlach 254cdd5de50SDave Gerlach i = M3_STATUS_RESP_MASK & val; 255cdd5de50SDave Gerlach i >>= __ffs(M3_STATUS_RESP_MASK); 256cdd5de50SDave Gerlach 257cdd5de50SDave Gerlach return i; 258cdd5de50SDave Gerlach } 259cdd5de50SDave Gerlach 260cdd5de50SDave Gerlach /** 261cdd5de50SDave Gerlach * wkup_m3_prepare_low_power - Request preparation for transition to 262cdd5de50SDave Gerlach * low power state 263cdd5de50SDave Gerlach * @state: A kernel suspend state to enter, either MEM or STANDBY 264cdd5de50SDave Gerlach * 265cdd5de50SDave Gerlach * Returns 0 if preparation was successful, otherwise returns error code 266cdd5de50SDave Gerlach */ 267cdd5de50SDave Gerlach static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) 268cdd5de50SDave Gerlach { 269cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 270cdd5de50SDave Gerlach int m3_power_state; 271cdd5de50SDave Gerlach int ret = 0; 272cdd5de50SDave Gerlach 273cdd5de50SDave Gerlach if (!wkup_m3_is_available(m3_ipc)) 274cdd5de50SDave Gerlach return -ENODEV; 275cdd5de50SDave Gerlach 276cdd5de50SDave Gerlach switch (state) { 277cdd5de50SDave Gerlach case WKUP_M3_DEEPSLEEP: 278cdd5de50SDave Gerlach m3_power_state = IPC_CMD_DS0; 279cdd5de50SDave Gerlach break; 280cdd5de50SDave Gerlach case WKUP_M3_STANDBY: 281cdd5de50SDave Gerlach m3_power_state = IPC_CMD_STANDBY; 282cdd5de50SDave Gerlach break; 283cdd5de50SDave Gerlach case WKUP_M3_IDLE: 284cdd5de50SDave Gerlach m3_power_state = IPC_CMD_IDLE; 285cdd5de50SDave Gerlach break; 286cdd5de50SDave Gerlach default: 287cdd5de50SDave Gerlach return 1; 288cdd5de50SDave Gerlach } 289cdd5de50SDave Gerlach 290cdd5de50SDave Gerlach /* Program each required IPC register then write defaults to others */ 291cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0); 292cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1); 293cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type, 4); 294cdd5de50SDave Gerlach 295cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); 296cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); 297cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); 298cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6); 299cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7); 300cdd5de50SDave Gerlach 301cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_MSG_FOR_LP; 302cdd5de50SDave Gerlach 303cdd5de50SDave Gerlach if (state == WKUP_M3_IDLE) 304cdd5de50SDave Gerlach ret = wkup_m3_ping_noirq(m3_ipc); 305cdd5de50SDave Gerlach else 306cdd5de50SDave Gerlach ret = wkup_m3_ping(m3_ipc); 307cdd5de50SDave Gerlach 308cdd5de50SDave Gerlach if (ret) { 309cdd5de50SDave Gerlach dev_err(dev, "Unable to ping CM3\n"); 310cdd5de50SDave Gerlach return ret; 311cdd5de50SDave Gerlach } 312cdd5de50SDave Gerlach 313cdd5de50SDave Gerlach return 0; 314cdd5de50SDave Gerlach } 315cdd5de50SDave Gerlach 316cdd5de50SDave Gerlach /** 317cdd5de50SDave Gerlach * wkup_m3_finish_low_power - Return m3 to reset state 318cdd5de50SDave Gerlach * 319cdd5de50SDave Gerlach * Returns 0 if reset was successful, otherwise returns error code 320cdd5de50SDave Gerlach */ 321cdd5de50SDave Gerlach static int wkup_m3_finish_low_power(struct wkup_m3_ipc *m3_ipc) 322cdd5de50SDave Gerlach { 323cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 324cdd5de50SDave Gerlach int ret = 0; 325cdd5de50SDave Gerlach 326cdd5de50SDave Gerlach if (!wkup_m3_is_available(m3_ipc)) 327cdd5de50SDave Gerlach return -ENODEV; 328cdd5de50SDave Gerlach 329cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, IPC_CMD_RESET, 1); 330cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); 331cdd5de50SDave Gerlach 332cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_MSG_FOR_RESET; 333cdd5de50SDave Gerlach 334cdd5de50SDave Gerlach ret = wkup_m3_ping(m3_ipc); 335cdd5de50SDave Gerlach if (ret) { 336cdd5de50SDave Gerlach dev_err(dev, "Unable to ping CM3\n"); 337cdd5de50SDave Gerlach return ret; 338cdd5de50SDave Gerlach } 339cdd5de50SDave Gerlach 340cdd5de50SDave Gerlach return 0; 341cdd5de50SDave Gerlach } 342cdd5de50SDave Gerlach 3437a872b6fSKeerthy /** 344ec93b62fSDave Gerlach * wkup_m3_request_wake_src - Get the wakeup source info passed from wkup_m3 345ec93b62fSDave Gerlach * @m3_ipc: Pointer to wkup_m3_ipc context 346ec93b62fSDave Gerlach */ 347ec93b62fSDave Gerlach static const char *wkup_m3_request_wake_src(struct wkup_m3_ipc *m3_ipc) 348ec93b62fSDave Gerlach { 349ec93b62fSDave Gerlach unsigned int wakeup_src_idx; 350ec93b62fSDave Gerlach int j, val; 351ec93b62fSDave Gerlach 352ec93b62fSDave Gerlach val = wkup_m3_ctrl_ipc_read(m3_ipc, 6); 353ec93b62fSDave Gerlach 354ec93b62fSDave Gerlach wakeup_src_idx = val & M3_WAKE_SRC_MASK; 355ec93b62fSDave Gerlach 356ec93b62fSDave Gerlach for (j = 0; j < ARRAY_SIZE(wakeups) - 1; j++) { 357ec93b62fSDave Gerlach if (wakeups[j].irq_nr == wakeup_src_idx) 358ec93b62fSDave Gerlach return wakeups[j].src; 359ec93b62fSDave Gerlach } 360ec93b62fSDave Gerlach return wakeups[j].src; 361ec93b62fSDave Gerlach } 362ec93b62fSDave Gerlach 363ec93b62fSDave Gerlach /** 3647a872b6fSKeerthy * wkup_m3_set_rtc_only - Set the rtc_only flag 3657a872b6fSKeerthy * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the 3667a872b6fSKeerthy * wakeup src value 3677a872b6fSKeerthy */ 3687a872b6fSKeerthy static void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc) 3697a872b6fSKeerthy { 3707a872b6fSKeerthy if (m3_ipc_state) 3717a872b6fSKeerthy m3_ipc_state->is_rtc_only = true; 3727a872b6fSKeerthy } 3737a872b6fSKeerthy 374cdd5de50SDave Gerlach static struct wkup_m3_ipc_ops ipc_ops = { 375cdd5de50SDave Gerlach .set_mem_type = wkup_m3_set_mem_type, 376cdd5de50SDave Gerlach .set_resume_address = wkup_m3_set_resume_address, 377cdd5de50SDave Gerlach .prepare_low_power = wkup_m3_prepare_low_power, 378cdd5de50SDave Gerlach .finish_low_power = wkup_m3_finish_low_power, 379cdd5de50SDave Gerlach .request_pm_status = wkup_m3_request_pm_status, 380ec93b62fSDave Gerlach .request_wake_src = wkup_m3_request_wake_src, 3817a872b6fSKeerthy .set_rtc_only = wkup_m3_set_rtc_only, 382cdd5de50SDave Gerlach }; 383cdd5de50SDave Gerlach 384cdd5de50SDave Gerlach /** 385cdd5de50SDave Gerlach * wkup_m3_ipc_get - Return handle to wkup_m3_ipc 386cdd5de50SDave Gerlach * 387cdd5de50SDave Gerlach * Returns NULL if the wkup_m3 is not yet available, otherwise returns 388cdd5de50SDave Gerlach * pointer to wkup_m3_ipc struct. 389cdd5de50SDave Gerlach */ 390cdd5de50SDave Gerlach struct wkup_m3_ipc *wkup_m3_ipc_get(void) 391cdd5de50SDave Gerlach { 392cdd5de50SDave Gerlach if (m3_ipc_state) 393cdd5de50SDave Gerlach get_device(m3_ipc_state->dev); 394cdd5de50SDave Gerlach else 395cdd5de50SDave Gerlach return NULL; 396cdd5de50SDave Gerlach 397cdd5de50SDave Gerlach return m3_ipc_state; 398cdd5de50SDave Gerlach } 399cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_get); 400cdd5de50SDave Gerlach 401cdd5de50SDave Gerlach /** 402cdd5de50SDave Gerlach * wkup_m3_ipc_put - Free handle to wkup_m3_ipc returned from wkup_m3_ipc_get 403cdd5de50SDave Gerlach * @m3_ipc: A pointer to wkup_m3_ipc struct returned by wkup_m3_ipc_get 404cdd5de50SDave Gerlach */ 405cdd5de50SDave Gerlach void wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc) 406cdd5de50SDave Gerlach { 407cdd5de50SDave Gerlach if (m3_ipc_state) 408cdd5de50SDave Gerlach put_device(m3_ipc_state->dev); 409cdd5de50SDave Gerlach } 410cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_put); 411cdd5de50SDave Gerlach 412cdd5de50SDave Gerlach static void wkup_m3_rproc_boot_thread(struct wkup_m3_ipc *m3_ipc) 413cdd5de50SDave Gerlach { 414cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 415cdd5de50SDave Gerlach int ret; 416cdd5de50SDave Gerlach 417cdd5de50SDave Gerlach init_completion(&m3_ipc->sync_complete); 418cdd5de50SDave Gerlach 419cdd5de50SDave Gerlach ret = rproc_boot(m3_ipc->rproc); 420cdd5de50SDave Gerlach if (ret) 421cdd5de50SDave Gerlach dev_err(dev, "rproc_boot failed\n"); 422cdd5de50SDave Gerlach 423cdd5de50SDave Gerlach do_exit(0); 424cdd5de50SDave Gerlach } 425cdd5de50SDave Gerlach 426cdd5de50SDave Gerlach static int wkup_m3_ipc_probe(struct platform_device *pdev) 427cdd5de50SDave Gerlach { 428cdd5de50SDave Gerlach struct device *dev = &pdev->dev; 429cdd5de50SDave Gerlach int irq, ret; 430cdd5de50SDave Gerlach phandle rproc_phandle; 431cdd5de50SDave Gerlach struct rproc *m3_rproc; 432cdd5de50SDave Gerlach struct resource *res; 433cdd5de50SDave Gerlach struct task_struct *task; 434cdd5de50SDave Gerlach struct wkup_m3_ipc *m3_ipc; 435cdd5de50SDave Gerlach 436cdd5de50SDave Gerlach m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL); 437cdd5de50SDave Gerlach if (!m3_ipc) 438cdd5de50SDave Gerlach return -ENOMEM; 439cdd5de50SDave Gerlach 440cdd5de50SDave Gerlach res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 441cdd5de50SDave Gerlach m3_ipc->ipc_mem_base = devm_ioremap_resource(dev, res); 442cdd5de50SDave Gerlach if (IS_ERR(m3_ipc->ipc_mem_base)) { 443cdd5de50SDave Gerlach dev_err(dev, "could not ioremap ipc_mem\n"); 444cdd5de50SDave Gerlach return PTR_ERR(m3_ipc->ipc_mem_base); 445cdd5de50SDave Gerlach } 446cdd5de50SDave Gerlach 447cdd5de50SDave Gerlach irq = platform_get_irq(pdev, 0); 448cdd5de50SDave Gerlach if (!irq) { 449cdd5de50SDave Gerlach dev_err(&pdev->dev, "no irq resource\n"); 450cdd5de50SDave Gerlach return -ENXIO; 451cdd5de50SDave Gerlach } 452cdd5de50SDave Gerlach 453cdd5de50SDave Gerlach ret = devm_request_irq(dev, irq, wkup_m3_txev_handler, 454cdd5de50SDave Gerlach 0, "wkup_m3_txev", m3_ipc); 455cdd5de50SDave Gerlach if (ret) { 456cdd5de50SDave Gerlach dev_err(dev, "request_irq failed\n"); 457cdd5de50SDave Gerlach return ret; 458cdd5de50SDave Gerlach } 459cdd5de50SDave Gerlach 460cdd5de50SDave Gerlach m3_ipc->mbox_client.dev = dev; 461cdd5de50SDave Gerlach m3_ipc->mbox_client.tx_done = NULL; 462cdd5de50SDave Gerlach m3_ipc->mbox_client.tx_prepare = NULL; 463cdd5de50SDave Gerlach m3_ipc->mbox_client.rx_callback = NULL; 464cdd5de50SDave Gerlach m3_ipc->mbox_client.tx_block = false; 465cdd5de50SDave Gerlach m3_ipc->mbox_client.knows_txdone = false; 466cdd5de50SDave Gerlach 467cdd5de50SDave Gerlach m3_ipc->mbox = mbox_request_channel(&m3_ipc->mbox_client, 0); 468cdd5de50SDave Gerlach 469cdd5de50SDave Gerlach if (IS_ERR(m3_ipc->mbox)) { 470cdd5de50SDave Gerlach dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n", 471cdd5de50SDave Gerlach PTR_ERR(m3_ipc->mbox)); 472cdd5de50SDave Gerlach return PTR_ERR(m3_ipc->mbox); 473cdd5de50SDave Gerlach } 474cdd5de50SDave Gerlach 475cdd5de50SDave Gerlach if (of_property_read_u32(dev->of_node, "ti,rproc", &rproc_phandle)) { 476cdd5de50SDave Gerlach dev_err(&pdev->dev, "could not get rproc phandle\n"); 477cdd5de50SDave Gerlach ret = -ENODEV; 478cdd5de50SDave Gerlach goto err_free_mbox; 479cdd5de50SDave Gerlach } 480cdd5de50SDave Gerlach 481cdd5de50SDave Gerlach m3_rproc = rproc_get_by_phandle(rproc_phandle); 482cdd5de50SDave Gerlach if (!m3_rproc) { 483cdd5de50SDave Gerlach dev_err(&pdev->dev, "could not get rproc handle\n"); 484cdd5de50SDave Gerlach ret = -EPROBE_DEFER; 485cdd5de50SDave Gerlach goto err_free_mbox; 486cdd5de50SDave Gerlach } 487cdd5de50SDave Gerlach 488cdd5de50SDave Gerlach m3_ipc->rproc = m3_rproc; 489cdd5de50SDave Gerlach m3_ipc->dev = dev; 490cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_RESET; 491cdd5de50SDave Gerlach 492cdd5de50SDave Gerlach m3_ipc->ops = &ipc_ops; 493cdd5de50SDave Gerlach 494cdd5de50SDave Gerlach /* 495cdd5de50SDave Gerlach * Wait for firmware loading completion in a thread so we 496cdd5de50SDave Gerlach * can boot the wkup_m3 as soon as it's ready without holding 497cdd5de50SDave Gerlach * up kernel boot 498cdd5de50SDave Gerlach */ 499cdd5de50SDave Gerlach task = kthread_run((void *)wkup_m3_rproc_boot_thread, m3_ipc, 500cdd5de50SDave Gerlach "wkup_m3_rproc_loader"); 501cdd5de50SDave Gerlach 502cdd5de50SDave Gerlach if (IS_ERR(task)) { 503cdd5de50SDave Gerlach dev_err(dev, "can't create rproc_boot thread\n"); 50436b29eb3SWei Yongjun ret = PTR_ERR(task); 505cdd5de50SDave Gerlach goto err_put_rproc; 506cdd5de50SDave Gerlach } 507cdd5de50SDave Gerlach 508cdd5de50SDave Gerlach m3_ipc_state = m3_ipc; 509cdd5de50SDave Gerlach 510cdd5de50SDave Gerlach return 0; 511cdd5de50SDave Gerlach 512cdd5de50SDave Gerlach err_put_rproc: 513cdd5de50SDave Gerlach rproc_put(m3_rproc); 514cdd5de50SDave Gerlach err_free_mbox: 515cdd5de50SDave Gerlach mbox_free_channel(m3_ipc->mbox); 516cdd5de50SDave Gerlach return ret; 517cdd5de50SDave Gerlach } 518cdd5de50SDave Gerlach 519cdd5de50SDave Gerlach static int wkup_m3_ipc_remove(struct platform_device *pdev) 520cdd5de50SDave Gerlach { 521cdd5de50SDave Gerlach mbox_free_channel(m3_ipc_state->mbox); 522cdd5de50SDave Gerlach 523cdd5de50SDave Gerlach rproc_shutdown(m3_ipc_state->rproc); 524cdd5de50SDave Gerlach rproc_put(m3_ipc_state->rproc); 525cdd5de50SDave Gerlach 526cdd5de50SDave Gerlach m3_ipc_state = NULL; 527cdd5de50SDave Gerlach 528cdd5de50SDave Gerlach return 0; 529cdd5de50SDave Gerlach } 530cdd5de50SDave Gerlach 531990c1009SArnd Bergmann static int __maybe_unused wkup_m3_ipc_suspend(struct device *dev) 5327a872b6fSKeerthy { 5337a872b6fSKeerthy /* 5347a872b6fSKeerthy * Nothing needs to be done on suspend even with rtc_only flag set 5357a872b6fSKeerthy */ 5367a872b6fSKeerthy return 0; 5377a872b6fSKeerthy } 5387a872b6fSKeerthy 539990c1009SArnd Bergmann static int __maybe_unused wkup_m3_ipc_resume(struct device *dev) 5407a872b6fSKeerthy { 5417a872b6fSKeerthy if (m3_ipc_state->is_rtc_only) { 5427a872b6fSKeerthy rproc_shutdown(m3_ipc_state->rproc); 5437a872b6fSKeerthy rproc_boot(m3_ipc_state->rproc); 5447a872b6fSKeerthy } 5457a872b6fSKeerthy 5467a872b6fSKeerthy m3_ipc_state->is_rtc_only = false; 5477a872b6fSKeerthy 5487a872b6fSKeerthy return 0; 5497a872b6fSKeerthy } 5507a872b6fSKeerthy 5517a872b6fSKeerthy static const struct dev_pm_ops wkup_m3_ipc_pm_ops = { 5527a872b6fSKeerthy SET_SYSTEM_SLEEP_PM_OPS(wkup_m3_ipc_suspend, wkup_m3_ipc_resume) 5537a872b6fSKeerthy }; 5547a872b6fSKeerthy 555cdd5de50SDave Gerlach static const struct of_device_id wkup_m3_ipc_of_match[] = { 556cdd5de50SDave Gerlach { .compatible = "ti,am3352-wkup-m3-ipc", }, 557cdd5de50SDave Gerlach { .compatible = "ti,am4372-wkup-m3-ipc", }, 558cdd5de50SDave Gerlach {}, 559cdd5de50SDave Gerlach }; 560cdd5de50SDave Gerlach MODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match); 561cdd5de50SDave Gerlach 562cdd5de50SDave Gerlach static struct platform_driver wkup_m3_ipc_driver = { 563cdd5de50SDave Gerlach .probe = wkup_m3_ipc_probe, 564cdd5de50SDave Gerlach .remove = wkup_m3_ipc_remove, 565cdd5de50SDave Gerlach .driver = { 566cdd5de50SDave Gerlach .name = "wkup_m3_ipc", 567cdd5de50SDave Gerlach .of_match_table = wkup_m3_ipc_of_match, 5687a872b6fSKeerthy .pm = &wkup_m3_ipc_pm_ops, 569cdd5de50SDave Gerlach }, 570cdd5de50SDave Gerlach }; 571cdd5de50SDave Gerlach 572cdd5de50SDave Gerlach module_platform_driver(wkup_m3_ipc_driver); 573cdd5de50SDave Gerlach 574cdd5de50SDave Gerlach MODULE_LICENSE("GPL v2"); 575cdd5de50SDave Gerlach MODULE_DESCRIPTION("wkup m3 remote processor ipc driver"); 576cdd5de50SDave Gerlach MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); 577