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