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 49cdd5de50SDave Gerlach 50cdd5de50SDave Gerlach #define M3_STATE_UNKNOWN 0 51cdd5de50SDave Gerlach #define M3_STATE_RESET 1 52cdd5de50SDave Gerlach #define M3_STATE_INITED 2 53cdd5de50SDave Gerlach #define M3_STATE_MSG_FOR_LP 3 54cdd5de50SDave Gerlach #define M3_STATE_MSG_FOR_RESET 4 55cdd5de50SDave Gerlach 56cdd5de50SDave Gerlach static struct wkup_m3_ipc *m3_ipc_state; 57cdd5de50SDave Gerlach 58cdd5de50SDave Gerlach static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc) 59cdd5de50SDave Gerlach { 60cdd5de50SDave Gerlach writel(AM33XX_M3_TXEV_ACK, 61cdd5de50SDave Gerlach m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI); 62cdd5de50SDave Gerlach } 63cdd5de50SDave Gerlach 64cdd5de50SDave Gerlach static void am33xx_txev_enable(struct wkup_m3_ipc *m3_ipc) 65cdd5de50SDave Gerlach { 66cdd5de50SDave Gerlach writel(AM33XX_M3_TXEV_ENABLE, 67cdd5de50SDave Gerlach m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI); 68cdd5de50SDave Gerlach } 69cdd5de50SDave Gerlach 70cdd5de50SDave Gerlach static void wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc *m3_ipc, 71cdd5de50SDave Gerlach u32 val, int ipc_reg_num) 72cdd5de50SDave Gerlach { 73cdd5de50SDave Gerlach if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT, 74cdd5de50SDave Gerlach "ipc register operation out of range")) 75cdd5de50SDave Gerlach return; 76cdd5de50SDave Gerlach 77cdd5de50SDave Gerlach writel(val, m3_ipc->ipc_mem_base + 78cdd5de50SDave Gerlach AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num)); 79cdd5de50SDave Gerlach } 80cdd5de50SDave Gerlach 81cdd5de50SDave Gerlach static unsigned int wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc *m3_ipc, 82cdd5de50SDave Gerlach 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 0; 87cdd5de50SDave Gerlach 88cdd5de50SDave Gerlach return readl(m3_ipc->ipc_mem_base + 89cdd5de50SDave Gerlach AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num)); 90cdd5de50SDave Gerlach } 91cdd5de50SDave Gerlach 92cdd5de50SDave Gerlach static int wkup_m3_fw_version_read(struct wkup_m3_ipc *m3_ipc) 93cdd5de50SDave Gerlach { 94cdd5de50SDave Gerlach int val; 95cdd5de50SDave Gerlach 96cdd5de50SDave Gerlach val = wkup_m3_ctrl_ipc_read(m3_ipc, 2); 97cdd5de50SDave Gerlach 98cdd5de50SDave Gerlach return val & M3_FW_VERSION_MASK; 99cdd5de50SDave Gerlach } 100cdd5de50SDave Gerlach 101cdd5de50SDave Gerlach static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data) 102cdd5de50SDave Gerlach { 103cdd5de50SDave Gerlach struct wkup_m3_ipc *m3_ipc = ipc_data; 104cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 105cdd5de50SDave Gerlach int ver = 0; 106cdd5de50SDave Gerlach 107cdd5de50SDave Gerlach am33xx_txev_eoi(m3_ipc); 108cdd5de50SDave Gerlach 109cdd5de50SDave Gerlach switch (m3_ipc->state) { 110cdd5de50SDave Gerlach case M3_STATE_RESET: 111cdd5de50SDave Gerlach ver = wkup_m3_fw_version_read(m3_ipc); 112cdd5de50SDave Gerlach 113cdd5de50SDave Gerlach if (ver == M3_VERSION_UNKNOWN || 114cdd5de50SDave Gerlach ver < M3_BASELINE_VERSION) { 115cdd5de50SDave Gerlach dev_warn(dev, "CM3 Firmware Version %x not supported\n", 116cdd5de50SDave Gerlach ver); 117cdd5de50SDave Gerlach } else { 118cdd5de50SDave Gerlach dev_info(dev, "CM3 Firmware Version = 0x%x\n", ver); 119cdd5de50SDave Gerlach } 120cdd5de50SDave Gerlach 121cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_INITED; 122cdd5de50SDave Gerlach complete(&m3_ipc->sync_complete); 123cdd5de50SDave Gerlach break; 124cdd5de50SDave Gerlach case M3_STATE_MSG_FOR_RESET: 125cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_INITED; 126cdd5de50SDave Gerlach complete(&m3_ipc->sync_complete); 127cdd5de50SDave Gerlach break; 128cdd5de50SDave Gerlach case M3_STATE_MSG_FOR_LP: 129cdd5de50SDave Gerlach complete(&m3_ipc->sync_complete); 130cdd5de50SDave Gerlach break; 131cdd5de50SDave Gerlach case M3_STATE_UNKNOWN: 132cdd5de50SDave Gerlach dev_warn(dev, "Unknown CM3 State\n"); 133cdd5de50SDave Gerlach } 134cdd5de50SDave Gerlach 135cdd5de50SDave Gerlach am33xx_txev_enable(m3_ipc); 136cdd5de50SDave Gerlach 137cdd5de50SDave Gerlach return IRQ_HANDLED; 138cdd5de50SDave Gerlach } 139cdd5de50SDave Gerlach 140cdd5de50SDave Gerlach static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc) 141cdd5de50SDave Gerlach { 142cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 143cdd5de50SDave Gerlach mbox_msg_t dummy_msg = 0; 144cdd5de50SDave Gerlach int ret; 145cdd5de50SDave Gerlach 146cdd5de50SDave Gerlach if (!m3_ipc->mbox) { 147cdd5de50SDave Gerlach dev_err(dev, 148cdd5de50SDave Gerlach "No IPC channel to communicate with wkup_m3!\n"); 149cdd5de50SDave Gerlach return -EIO; 150cdd5de50SDave Gerlach } 151cdd5de50SDave Gerlach 152cdd5de50SDave Gerlach /* 153cdd5de50SDave Gerlach * Write a dummy message to the mailbox in order to trigger the RX 154cdd5de50SDave Gerlach * interrupt to alert the M3 that data is available in the IPC 155cdd5de50SDave Gerlach * registers. We must enable the IRQ here and disable it after in 156cdd5de50SDave Gerlach * the RX callback to avoid multiple interrupts being received 157cdd5de50SDave Gerlach * by the CM3. 158cdd5de50SDave Gerlach */ 159cdd5de50SDave Gerlach ret = mbox_send_message(m3_ipc->mbox, &dummy_msg); 160cdd5de50SDave Gerlach if (ret < 0) { 161cdd5de50SDave Gerlach dev_err(dev, "%s: mbox_send_message() failed: %d\n", 162cdd5de50SDave Gerlach __func__, ret); 163cdd5de50SDave Gerlach return ret; 164cdd5de50SDave Gerlach } 165cdd5de50SDave Gerlach 166cdd5de50SDave Gerlach ret = wait_for_completion_timeout(&m3_ipc->sync_complete, 167cdd5de50SDave Gerlach msecs_to_jiffies(500)); 168cdd5de50SDave Gerlach if (!ret) { 169cdd5de50SDave Gerlach dev_err(dev, "MPU<->CM3 sync failure\n"); 170cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_UNKNOWN; 171cdd5de50SDave Gerlach return -EIO; 172cdd5de50SDave Gerlach } 173cdd5de50SDave Gerlach 174cdd5de50SDave Gerlach mbox_client_txdone(m3_ipc->mbox, 0); 175cdd5de50SDave Gerlach return 0; 176cdd5de50SDave Gerlach } 177cdd5de50SDave Gerlach 178cdd5de50SDave Gerlach static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc) 179cdd5de50SDave Gerlach { 180cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 181cdd5de50SDave Gerlach mbox_msg_t dummy_msg = 0; 182cdd5de50SDave Gerlach int ret; 183cdd5de50SDave Gerlach 184cdd5de50SDave Gerlach if (!m3_ipc->mbox) { 185cdd5de50SDave Gerlach dev_err(dev, 186cdd5de50SDave Gerlach "No IPC channel to communicate with wkup_m3!\n"); 187cdd5de50SDave Gerlach return -EIO; 188cdd5de50SDave Gerlach } 189cdd5de50SDave Gerlach 190cdd5de50SDave Gerlach ret = mbox_send_message(m3_ipc->mbox, &dummy_msg); 191cdd5de50SDave Gerlach if (ret < 0) { 192cdd5de50SDave Gerlach dev_err(dev, "%s: mbox_send_message() failed: %d\n", 193cdd5de50SDave Gerlach __func__, ret); 194cdd5de50SDave Gerlach return ret; 195cdd5de50SDave Gerlach } 196cdd5de50SDave Gerlach 197cdd5de50SDave Gerlach mbox_client_txdone(m3_ipc->mbox, 0); 198cdd5de50SDave Gerlach return 0; 199cdd5de50SDave Gerlach } 200cdd5de50SDave Gerlach 201cdd5de50SDave Gerlach static int wkup_m3_is_available(struct wkup_m3_ipc *m3_ipc) 202cdd5de50SDave Gerlach { 203cdd5de50SDave Gerlach return ((m3_ipc->state != M3_STATE_RESET) && 204cdd5de50SDave Gerlach (m3_ipc->state != M3_STATE_UNKNOWN)); 205cdd5de50SDave Gerlach } 206cdd5de50SDave Gerlach 207cdd5de50SDave Gerlach /* Public functions */ 208cdd5de50SDave Gerlach /** 209cdd5de50SDave Gerlach * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use 210cdd5de50SDave Gerlach * @mem_type: memory type value read directly from emif 211cdd5de50SDave Gerlach * 212cdd5de50SDave Gerlach * wkup_m3 must know what memory type is in use to properly suspend 213cdd5de50SDave Gerlach * and resume. 214cdd5de50SDave Gerlach */ 215cdd5de50SDave Gerlach static void wkup_m3_set_mem_type(struct wkup_m3_ipc *m3_ipc, int mem_type) 216cdd5de50SDave Gerlach { 217cdd5de50SDave Gerlach m3_ipc->mem_type = mem_type; 218cdd5de50SDave Gerlach } 219cdd5de50SDave Gerlach 220cdd5de50SDave Gerlach /** 221cdd5de50SDave Gerlach * wkup_m3_set_resume_address - Pass wkup_m3 resume address 222cdd5de50SDave Gerlach * @addr: Physical address from which resume code should execute 223cdd5de50SDave Gerlach */ 224cdd5de50SDave Gerlach static void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr) 225cdd5de50SDave Gerlach { 226cdd5de50SDave Gerlach m3_ipc->resume_addr = (unsigned long)addr; 227cdd5de50SDave Gerlach } 228cdd5de50SDave Gerlach 229cdd5de50SDave Gerlach /** 230cdd5de50SDave Gerlach * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend 231cdd5de50SDave Gerlach * 232cdd5de50SDave Gerlach * Returns code representing the status of a low power mode transition. 233cdd5de50SDave Gerlach * 0 - Successful transition 234cdd5de50SDave Gerlach * 1 - Failure to transition to low power state 235cdd5de50SDave Gerlach */ 236cdd5de50SDave Gerlach static int wkup_m3_request_pm_status(struct wkup_m3_ipc *m3_ipc) 237cdd5de50SDave Gerlach { 238cdd5de50SDave Gerlach unsigned int i; 239cdd5de50SDave Gerlach int val; 240cdd5de50SDave Gerlach 241cdd5de50SDave Gerlach val = wkup_m3_ctrl_ipc_read(m3_ipc, 1); 242cdd5de50SDave Gerlach 243cdd5de50SDave Gerlach i = M3_STATUS_RESP_MASK & val; 244cdd5de50SDave Gerlach i >>= __ffs(M3_STATUS_RESP_MASK); 245cdd5de50SDave Gerlach 246cdd5de50SDave Gerlach return i; 247cdd5de50SDave Gerlach } 248cdd5de50SDave Gerlach 249cdd5de50SDave Gerlach /** 250cdd5de50SDave Gerlach * wkup_m3_prepare_low_power - Request preparation for transition to 251cdd5de50SDave Gerlach * low power state 252cdd5de50SDave Gerlach * @state: A kernel suspend state to enter, either MEM or STANDBY 253cdd5de50SDave Gerlach * 254cdd5de50SDave Gerlach * Returns 0 if preparation was successful, otherwise returns error code 255cdd5de50SDave Gerlach */ 256cdd5de50SDave Gerlach static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) 257cdd5de50SDave Gerlach { 258cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 259cdd5de50SDave Gerlach int m3_power_state; 260cdd5de50SDave Gerlach int ret = 0; 261cdd5de50SDave Gerlach 262cdd5de50SDave Gerlach if (!wkup_m3_is_available(m3_ipc)) 263cdd5de50SDave Gerlach return -ENODEV; 264cdd5de50SDave Gerlach 265cdd5de50SDave Gerlach switch (state) { 266cdd5de50SDave Gerlach case WKUP_M3_DEEPSLEEP: 267cdd5de50SDave Gerlach m3_power_state = IPC_CMD_DS0; 268cdd5de50SDave Gerlach break; 269cdd5de50SDave Gerlach case WKUP_M3_STANDBY: 270cdd5de50SDave Gerlach m3_power_state = IPC_CMD_STANDBY; 271cdd5de50SDave Gerlach break; 272cdd5de50SDave Gerlach case WKUP_M3_IDLE: 273cdd5de50SDave Gerlach m3_power_state = IPC_CMD_IDLE; 274cdd5de50SDave Gerlach break; 275cdd5de50SDave Gerlach default: 276cdd5de50SDave Gerlach return 1; 277cdd5de50SDave Gerlach } 278cdd5de50SDave Gerlach 279cdd5de50SDave Gerlach /* Program each required IPC register then write defaults to others */ 280cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0); 281cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1); 282cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type, 4); 283cdd5de50SDave Gerlach 284cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); 285cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); 286cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); 287cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6); 288cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7); 289cdd5de50SDave Gerlach 290cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_MSG_FOR_LP; 291cdd5de50SDave Gerlach 292cdd5de50SDave Gerlach if (state == WKUP_M3_IDLE) 293cdd5de50SDave Gerlach ret = wkup_m3_ping_noirq(m3_ipc); 294cdd5de50SDave Gerlach else 295cdd5de50SDave Gerlach ret = wkup_m3_ping(m3_ipc); 296cdd5de50SDave Gerlach 297cdd5de50SDave Gerlach if (ret) { 298cdd5de50SDave Gerlach dev_err(dev, "Unable to ping CM3\n"); 299cdd5de50SDave Gerlach return ret; 300cdd5de50SDave Gerlach } 301cdd5de50SDave Gerlach 302cdd5de50SDave Gerlach return 0; 303cdd5de50SDave Gerlach } 304cdd5de50SDave Gerlach 305cdd5de50SDave Gerlach /** 306cdd5de50SDave Gerlach * wkup_m3_finish_low_power - Return m3 to reset state 307cdd5de50SDave Gerlach * 308cdd5de50SDave Gerlach * Returns 0 if reset was successful, otherwise returns error code 309cdd5de50SDave Gerlach */ 310cdd5de50SDave Gerlach static int wkup_m3_finish_low_power(struct wkup_m3_ipc *m3_ipc) 311cdd5de50SDave Gerlach { 312cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 313cdd5de50SDave Gerlach int ret = 0; 314cdd5de50SDave Gerlach 315cdd5de50SDave Gerlach if (!wkup_m3_is_available(m3_ipc)) 316cdd5de50SDave Gerlach return -ENODEV; 317cdd5de50SDave Gerlach 318cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, IPC_CMD_RESET, 1); 319cdd5de50SDave Gerlach wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); 320cdd5de50SDave Gerlach 321cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_MSG_FOR_RESET; 322cdd5de50SDave Gerlach 323cdd5de50SDave Gerlach ret = wkup_m3_ping(m3_ipc); 324cdd5de50SDave Gerlach if (ret) { 325cdd5de50SDave Gerlach dev_err(dev, "Unable to ping CM3\n"); 326cdd5de50SDave Gerlach return ret; 327cdd5de50SDave Gerlach } 328cdd5de50SDave Gerlach 329cdd5de50SDave Gerlach return 0; 330cdd5de50SDave Gerlach } 331cdd5de50SDave Gerlach 3327a872b6fSKeerthy /** 3337a872b6fSKeerthy * wkup_m3_set_rtc_only - Set the rtc_only flag 3347a872b6fSKeerthy * @wkup_m3_wakeup: struct wkup_m3_wakeup_src * gets assigned the 3357a872b6fSKeerthy * wakeup src value 3367a872b6fSKeerthy */ 3377a872b6fSKeerthy static void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc) 3387a872b6fSKeerthy { 3397a872b6fSKeerthy if (m3_ipc_state) 3407a872b6fSKeerthy m3_ipc_state->is_rtc_only = true; 3417a872b6fSKeerthy } 3427a872b6fSKeerthy 343cdd5de50SDave Gerlach static struct wkup_m3_ipc_ops ipc_ops = { 344cdd5de50SDave Gerlach .set_mem_type = wkup_m3_set_mem_type, 345cdd5de50SDave Gerlach .set_resume_address = wkup_m3_set_resume_address, 346cdd5de50SDave Gerlach .prepare_low_power = wkup_m3_prepare_low_power, 347cdd5de50SDave Gerlach .finish_low_power = wkup_m3_finish_low_power, 348cdd5de50SDave Gerlach .request_pm_status = wkup_m3_request_pm_status, 3497a872b6fSKeerthy .set_rtc_only = wkup_m3_set_rtc_only, 350cdd5de50SDave Gerlach }; 351cdd5de50SDave Gerlach 352cdd5de50SDave Gerlach /** 353cdd5de50SDave Gerlach * wkup_m3_ipc_get - Return handle to wkup_m3_ipc 354cdd5de50SDave Gerlach * 355cdd5de50SDave Gerlach * Returns NULL if the wkup_m3 is not yet available, otherwise returns 356cdd5de50SDave Gerlach * pointer to wkup_m3_ipc struct. 357cdd5de50SDave Gerlach */ 358cdd5de50SDave Gerlach struct wkup_m3_ipc *wkup_m3_ipc_get(void) 359cdd5de50SDave Gerlach { 360cdd5de50SDave Gerlach if (m3_ipc_state) 361cdd5de50SDave Gerlach get_device(m3_ipc_state->dev); 362cdd5de50SDave Gerlach else 363cdd5de50SDave Gerlach return NULL; 364cdd5de50SDave Gerlach 365cdd5de50SDave Gerlach return m3_ipc_state; 366cdd5de50SDave Gerlach } 367cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_get); 368cdd5de50SDave Gerlach 369cdd5de50SDave Gerlach /** 370cdd5de50SDave Gerlach * wkup_m3_ipc_put - Free handle to wkup_m3_ipc returned from wkup_m3_ipc_get 371cdd5de50SDave Gerlach * @m3_ipc: A pointer to wkup_m3_ipc struct returned by wkup_m3_ipc_get 372cdd5de50SDave Gerlach */ 373cdd5de50SDave Gerlach void wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc) 374cdd5de50SDave Gerlach { 375cdd5de50SDave Gerlach if (m3_ipc_state) 376cdd5de50SDave Gerlach put_device(m3_ipc_state->dev); 377cdd5de50SDave Gerlach } 378cdd5de50SDave Gerlach EXPORT_SYMBOL_GPL(wkup_m3_ipc_put); 379cdd5de50SDave Gerlach 380cdd5de50SDave Gerlach static void wkup_m3_rproc_boot_thread(struct wkup_m3_ipc *m3_ipc) 381cdd5de50SDave Gerlach { 382cdd5de50SDave Gerlach struct device *dev = m3_ipc->dev; 383cdd5de50SDave Gerlach int ret; 384cdd5de50SDave Gerlach 385cdd5de50SDave Gerlach init_completion(&m3_ipc->sync_complete); 386cdd5de50SDave Gerlach 387cdd5de50SDave Gerlach ret = rproc_boot(m3_ipc->rproc); 388cdd5de50SDave Gerlach if (ret) 389cdd5de50SDave Gerlach dev_err(dev, "rproc_boot failed\n"); 390cdd5de50SDave Gerlach 391cdd5de50SDave Gerlach do_exit(0); 392cdd5de50SDave Gerlach } 393cdd5de50SDave Gerlach 394cdd5de50SDave Gerlach static int wkup_m3_ipc_probe(struct platform_device *pdev) 395cdd5de50SDave Gerlach { 396cdd5de50SDave Gerlach struct device *dev = &pdev->dev; 397cdd5de50SDave Gerlach int irq, ret; 398cdd5de50SDave Gerlach phandle rproc_phandle; 399cdd5de50SDave Gerlach struct rproc *m3_rproc; 400cdd5de50SDave Gerlach struct resource *res; 401cdd5de50SDave Gerlach struct task_struct *task; 402cdd5de50SDave Gerlach struct wkup_m3_ipc *m3_ipc; 403cdd5de50SDave Gerlach 404cdd5de50SDave Gerlach m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL); 405cdd5de50SDave Gerlach if (!m3_ipc) 406cdd5de50SDave Gerlach return -ENOMEM; 407cdd5de50SDave Gerlach 408cdd5de50SDave Gerlach res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 409cdd5de50SDave Gerlach m3_ipc->ipc_mem_base = devm_ioremap_resource(dev, res); 410cdd5de50SDave Gerlach if (IS_ERR(m3_ipc->ipc_mem_base)) { 411cdd5de50SDave Gerlach dev_err(dev, "could not ioremap ipc_mem\n"); 412cdd5de50SDave Gerlach return PTR_ERR(m3_ipc->ipc_mem_base); 413cdd5de50SDave Gerlach } 414cdd5de50SDave Gerlach 415cdd5de50SDave Gerlach irq = platform_get_irq(pdev, 0); 416cdd5de50SDave Gerlach if (!irq) { 417cdd5de50SDave Gerlach dev_err(&pdev->dev, "no irq resource\n"); 418cdd5de50SDave Gerlach return -ENXIO; 419cdd5de50SDave Gerlach } 420cdd5de50SDave Gerlach 421cdd5de50SDave Gerlach ret = devm_request_irq(dev, irq, wkup_m3_txev_handler, 422cdd5de50SDave Gerlach 0, "wkup_m3_txev", m3_ipc); 423cdd5de50SDave Gerlach if (ret) { 424cdd5de50SDave Gerlach dev_err(dev, "request_irq failed\n"); 425cdd5de50SDave Gerlach return ret; 426cdd5de50SDave Gerlach } 427cdd5de50SDave Gerlach 428cdd5de50SDave Gerlach m3_ipc->mbox_client.dev = dev; 429cdd5de50SDave Gerlach m3_ipc->mbox_client.tx_done = NULL; 430cdd5de50SDave Gerlach m3_ipc->mbox_client.tx_prepare = NULL; 431cdd5de50SDave Gerlach m3_ipc->mbox_client.rx_callback = NULL; 432cdd5de50SDave Gerlach m3_ipc->mbox_client.tx_block = false; 433cdd5de50SDave Gerlach m3_ipc->mbox_client.knows_txdone = false; 434cdd5de50SDave Gerlach 435cdd5de50SDave Gerlach m3_ipc->mbox = mbox_request_channel(&m3_ipc->mbox_client, 0); 436cdd5de50SDave Gerlach 437cdd5de50SDave Gerlach if (IS_ERR(m3_ipc->mbox)) { 438cdd5de50SDave Gerlach dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n", 439cdd5de50SDave Gerlach PTR_ERR(m3_ipc->mbox)); 440cdd5de50SDave Gerlach return PTR_ERR(m3_ipc->mbox); 441cdd5de50SDave Gerlach } 442cdd5de50SDave Gerlach 443cdd5de50SDave Gerlach if (of_property_read_u32(dev->of_node, "ti,rproc", &rproc_phandle)) { 444cdd5de50SDave Gerlach dev_err(&pdev->dev, "could not get rproc phandle\n"); 445cdd5de50SDave Gerlach ret = -ENODEV; 446cdd5de50SDave Gerlach goto err_free_mbox; 447cdd5de50SDave Gerlach } 448cdd5de50SDave Gerlach 449cdd5de50SDave Gerlach m3_rproc = rproc_get_by_phandle(rproc_phandle); 450cdd5de50SDave Gerlach if (!m3_rproc) { 451cdd5de50SDave Gerlach dev_err(&pdev->dev, "could not get rproc handle\n"); 452cdd5de50SDave Gerlach ret = -EPROBE_DEFER; 453cdd5de50SDave Gerlach goto err_free_mbox; 454cdd5de50SDave Gerlach } 455cdd5de50SDave Gerlach 456cdd5de50SDave Gerlach m3_ipc->rproc = m3_rproc; 457cdd5de50SDave Gerlach m3_ipc->dev = dev; 458cdd5de50SDave Gerlach m3_ipc->state = M3_STATE_RESET; 459cdd5de50SDave Gerlach 460cdd5de50SDave Gerlach m3_ipc->ops = &ipc_ops; 461cdd5de50SDave Gerlach 462cdd5de50SDave Gerlach /* 463cdd5de50SDave Gerlach * Wait for firmware loading completion in a thread so we 464cdd5de50SDave Gerlach * can boot the wkup_m3 as soon as it's ready without holding 465cdd5de50SDave Gerlach * up kernel boot 466cdd5de50SDave Gerlach */ 467cdd5de50SDave Gerlach task = kthread_run((void *)wkup_m3_rproc_boot_thread, m3_ipc, 468cdd5de50SDave Gerlach "wkup_m3_rproc_loader"); 469cdd5de50SDave Gerlach 470cdd5de50SDave Gerlach if (IS_ERR(task)) { 471cdd5de50SDave Gerlach dev_err(dev, "can't create rproc_boot thread\n"); 47236b29eb3SWei Yongjun ret = PTR_ERR(task); 473cdd5de50SDave Gerlach goto err_put_rproc; 474cdd5de50SDave Gerlach } 475cdd5de50SDave Gerlach 476cdd5de50SDave Gerlach m3_ipc_state = m3_ipc; 477cdd5de50SDave Gerlach 478cdd5de50SDave Gerlach return 0; 479cdd5de50SDave Gerlach 480cdd5de50SDave Gerlach err_put_rproc: 481cdd5de50SDave Gerlach rproc_put(m3_rproc); 482cdd5de50SDave Gerlach err_free_mbox: 483cdd5de50SDave Gerlach mbox_free_channel(m3_ipc->mbox); 484cdd5de50SDave Gerlach return ret; 485cdd5de50SDave Gerlach } 486cdd5de50SDave Gerlach 487cdd5de50SDave Gerlach static int wkup_m3_ipc_remove(struct platform_device *pdev) 488cdd5de50SDave Gerlach { 489cdd5de50SDave Gerlach mbox_free_channel(m3_ipc_state->mbox); 490cdd5de50SDave Gerlach 491cdd5de50SDave Gerlach rproc_shutdown(m3_ipc_state->rproc); 492cdd5de50SDave Gerlach rproc_put(m3_ipc_state->rproc); 493cdd5de50SDave Gerlach 494cdd5de50SDave Gerlach m3_ipc_state = NULL; 495cdd5de50SDave Gerlach 496cdd5de50SDave Gerlach return 0; 497cdd5de50SDave Gerlach } 498cdd5de50SDave Gerlach 4997a872b6fSKeerthy #ifdef CONFIG_PM 5007a872b6fSKeerthy static int wkup_m3_ipc_suspend(struct device *dev) 5017a872b6fSKeerthy { 5027a872b6fSKeerthy /* 5037a872b6fSKeerthy * Nothing needs to be done on suspend even with rtc_only flag set 5047a872b6fSKeerthy */ 5057a872b6fSKeerthy return 0; 5067a872b6fSKeerthy } 5077a872b6fSKeerthy 5087a872b6fSKeerthy static int wkup_m3_ipc_resume(struct device *dev) 5097a872b6fSKeerthy { 5107a872b6fSKeerthy if (m3_ipc_state->is_rtc_only) { 5117a872b6fSKeerthy rproc_shutdown(m3_ipc_state->rproc); 5127a872b6fSKeerthy rproc_boot(m3_ipc_state->rproc); 5137a872b6fSKeerthy } 5147a872b6fSKeerthy 5157a872b6fSKeerthy m3_ipc_state->is_rtc_only = false; 5167a872b6fSKeerthy 5177a872b6fSKeerthy return 0; 5187a872b6fSKeerthy } 5197a872b6fSKeerthy 5207a872b6fSKeerthy static const struct dev_pm_ops wkup_m3_ipc_pm_ops = { 5217a872b6fSKeerthy SET_SYSTEM_SLEEP_PM_OPS(wkup_m3_ipc_suspend, wkup_m3_ipc_resume) 5227a872b6fSKeerthy }; 5237a872b6fSKeerthy #endif 5247a872b6fSKeerthy 525cdd5de50SDave Gerlach static const struct of_device_id wkup_m3_ipc_of_match[] = { 526cdd5de50SDave Gerlach { .compatible = "ti,am3352-wkup-m3-ipc", }, 527cdd5de50SDave Gerlach { .compatible = "ti,am4372-wkup-m3-ipc", }, 528cdd5de50SDave Gerlach {}, 529cdd5de50SDave Gerlach }; 530cdd5de50SDave Gerlach MODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match); 531cdd5de50SDave Gerlach 532cdd5de50SDave Gerlach static struct platform_driver wkup_m3_ipc_driver = { 533cdd5de50SDave Gerlach .probe = wkup_m3_ipc_probe, 534cdd5de50SDave Gerlach .remove = wkup_m3_ipc_remove, 535cdd5de50SDave Gerlach .driver = { 536cdd5de50SDave Gerlach .name = "wkup_m3_ipc", 537cdd5de50SDave Gerlach .of_match_table = wkup_m3_ipc_of_match, 5387a872b6fSKeerthy #ifdef CONFIG_PM 5397a872b6fSKeerthy .pm = &wkup_m3_ipc_pm_ops, 5407a872b6fSKeerthy #endif 541cdd5de50SDave Gerlach }, 542cdd5de50SDave Gerlach }; 543cdd5de50SDave Gerlach 544cdd5de50SDave Gerlach module_platform_driver(wkup_m3_ipc_driver); 545cdd5de50SDave Gerlach 546cdd5de50SDave Gerlach MODULE_LICENSE("GPL v2"); 547cdd5de50SDave Gerlach MODULE_DESCRIPTION("wkup m3 remote processor ipc driver"); 548cdd5de50SDave Gerlach MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); 549