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