162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMx3 Wkup M3 IPC driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Texas Instruments, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Dave Gerlach <d-gerlach@ti.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/debugfs.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/firmware.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/kthread.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/irq.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/omap-mailbox.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/remoteproc.h> 2262306a36Sopenharmony_ci#include <linux/suspend.h> 2362306a36Sopenharmony_ci#include <linux/wkup_m3_ipc.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define AM33XX_CTRL_IPC_REG_COUNT 0x8 2662306a36Sopenharmony_ci#define AM33XX_CTRL_IPC_REG_OFFSET(m) (0x4 + 4 * (m)) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* AM33XX M3_TXEV_EOI register */ 2962306a36Sopenharmony_ci#define AM33XX_CONTROL_M3_TXEV_EOI 0x00 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define AM33XX_M3_TXEV_ACK (0x1 << 0) 3262306a36Sopenharmony_ci#define AM33XX_M3_TXEV_ENABLE (0x0 << 0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define IPC_CMD_DS0 0x4 3562306a36Sopenharmony_ci#define IPC_CMD_STANDBY 0xc 3662306a36Sopenharmony_ci#define IPC_CMD_IDLE 0x10 3762306a36Sopenharmony_ci#define IPC_CMD_RESET 0xe 3862306a36Sopenharmony_ci#define DS_IPC_DEFAULT 0xffffffff 3962306a36Sopenharmony_ci#define M3_VERSION_UNKNOWN 0x0000ffff 4062306a36Sopenharmony_ci#define M3_BASELINE_VERSION 0x191 4162306a36Sopenharmony_ci#define M3_STATUS_RESP_MASK (0xffff << 16) 4262306a36Sopenharmony_ci#define M3_FW_VERSION_MASK 0xffff 4362306a36Sopenharmony_ci#define M3_WAKE_SRC_MASK 0xff 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define IPC_MEM_TYPE_SHIFT (0x0) 4662306a36Sopenharmony_ci#define IPC_MEM_TYPE_MASK (0x7 << 0) 4762306a36Sopenharmony_ci#define IPC_VTT_STAT_SHIFT (0x3) 4862306a36Sopenharmony_ci#define IPC_VTT_STAT_MASK (0x1 << 3) 4962306a36Sopenharmony_ci#define IPC_VTT_GPIO_PIN_SHIFT (0x4) 5062306a36Sopenharmony_ci#define IPC_VTT_GPIO_PIN_MASK (0x3f << 4) 5162306a36Sopenharmony_ci#define IPC_IO_ISOLATION_STAT_SHIFT (10) 5262306a36Sopenharmony_ci#define IPC_IO_ISOLATION_STAT_MASK (0x1 << 10) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define IPC_DBG_HALT_SHIFT (11) 5562306a36Sopenharmony_ci#define IPC_DBG_HALT_MASK (0x1 << 11) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define M3_STATE_UNKNOWN 0 5862306a36Sopenharmony_ci#define M3_STATE_RESET 1 5962306a36Sopenharmony_ci#define M3_STATE_INITED 2 6062306a36Sopenharmony_ci#define M3_STATE_MSG_FOR_LP 3 6162306a36Sopenharmony_ci#define M3_STATE_MSG_FOR_RESET 4 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define WKUP_M3_SD_FW_MAGIC 0x570C 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define WKUP_M3_DMEM_START 0x80000 6662306a36Sopenharmony_ci#define WKUP_M3_AUXDATA_OFFSET 0x1000 6762306a36Sopenharmony_ci#define WKUP_M3_AUXDATA_SIZE 0xFF 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct wkup_m3_ipc *m3_ipc_state; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic const struct wkup_m3_wakeup_src wakeups[] = { 7262306a36Sopenharmony_ci {.irq_nr = 16, .src = "PRCM"}, 7362306a36Sopenharmony_ci {.irq_nr = 35, .src = "USB0_PHY"}, 7462306a36Sopenharmony_ci {.irq_nr = 36, .src = "USB1_PHY"}, 7562306a36Sopenharmony_ci {.irq_nr = 40, .src = "I2C0"}, 7662306a36Sopenharmony_ci {.irq_nr = 41, .src = "RTC Timer"}, 7762306a36Sopenharmony_ci {.irq_nr = 42, .src = "RTC Alarm"}, 7862306a36Sopenharmony_ci {.irq_nr = 43, .src = "Timer0"}, 7962306a36Sopenharmony_ci {.irq_nr = 44, .src = "Timer1"}, 8062306a36Sopenharmony_ci {.irq_nr = 45, .src = "UART"}, 8162306a36Sopenharmony_ci {.irq_nr = 46, .src = "GPIO0"}, 8262306a36Sopenharmony_ci {.irq_nr = 48, .src = "MPU_WAKE"}, 8362306a36Sopenharmony_ci {.irq_nr = 49, .src = "WDT0"}, 8462306a36Sopenharmony_ci {.irq_nr = 50, .src = "WDT1"}, 8562306a36Sopenharmony_ci {.irq_nr = 51, .src = "ADC_TSC"}, 8662306a36Sopenharmony_ci {.irq_nr = 0, .src = "Unknown"}, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/** 9062306a36Sopenharmony_ci * wkup_m3_copy_aux_data - Copy auxiliary data to special region of m3 dmem 9162306a36Sopenharmony_ci * @data - pointer to data 9262306a36Sopenharmony_ci * @sz - size of data to copy (limit 256 bytes) 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * Copies any additional blob of data to the wkup_m3 dmem to be used by the 9562306a36Sopenharmony_ci * firmware 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc, 9862306a36Sopenharmony_ci const void *data, int sz) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned long aux_data_dev_addr; 10162306a36Sopenharmony_ci void *aux_data_addr; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET; 10462306a36Sopenharmony_ci aux_data_addr = rproc_da_to_va(m3_ipc->rproc, 10562306a36Sopenharmony_ci aux_data_dev_addr, 10662306a36Sopenharmony_ci WKUP_M3_AUXDATA_SIZE, 10762306a36Sopenharmony_ci NULL); 10862306a36Sopenharmony_ci memcpy(aux_data_addr, data, sz); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return WKUP_M3_AUXDATA_OFFSET; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic void wkup_m3_scale_data_fw_cb(const struct firmware *fw, void *context) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci unsigned long val, aux_base; 11662306a36Sopenharmony_ci struct wkup_m3_scale_data_header hdr; 11762306a36Sopenharmony_ci struct wkup_m3_ipc *m3_ipc = context; 11862306a36Sopenharmony_ci struct device *dev = m3_ipc->dev; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (!fw) { 12162306a36Sopenharmony_ci dev_err(dev, "Voltage scale fw name given but file missing.\n"); 12262306a36Sopenharmony_ci return; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci memcpy(&hdr, fw->data, sizeof(hdr)); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (hdr.magic != WKUP_M3_SD_FW_MAGIC) { 12862306a36Sopenharmony_ci dev_err(dev, "PM: Voltage Scale Data binary does not appear valid.\n"); 12962306a36Sopenharmony_ci goto release_sd_fw; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci aux_base = wkup_m3_copy_aux_data(m3_ipc, fw->data + sizeof(hdr), 13362306a36Sopenharmony_ci fw->size - sizeof(hdr)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci val = (aux_base + hdr.sleep_offset); 13662306a36Sopenharmony_ci val |= ((aux_base + hdr.wake_offset) << 16); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci m3_ipc->volt_scale_offsets = val; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cirelease_sd_fw: 14162306a36Sopenharmony_ci release_firmware(fw); 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc, 14562306a36Sopenharmony_ci struct device *dev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int ret = 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * If no name is provided, user has already been warned, pm will 15162306a36Sopenharmony_ci * still work so return 0 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!m3_ipc->sd_fw_name) 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, 15862306a36Sopenharmony_ci m3_ipc->sd_fw_name, dev, GFP_ATOMIC, 15962306a36Sopenharmony_ci m3_ipc, wkup_m3_scale_data_fw_cb); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 16562306a36Sopenharmony_cistatic void wkup_m3_set_halt_late(bool enabled) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (enabled) 16862306a36Sopenharmony_ci m3_ipc_state->halt = (1 << IPC_DBG_HALT_SHIFT); 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci m3_ipc_state->halt = 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int option_get(void *data, u64 *val) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci u32 *option = data; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci *val = *option; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int option_set(void *data, u64 val) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci u32 *option = data; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *option = val; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (option == &m3_ipc_state->halt) { 18962306a36Sopenharmony_ci if (val) 19062306a36Sopenharmony_ci wkup_m3_set_halt_late(true); 19162306a36Sopenharmony_ci else 19262306a36Sopenharmony_ci wkup_m3_set_halt_late(false); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(wkup_m3_ipc_option_fops, option_get, option_set, 19962306a36Sopenharmony_ci "%llu\n"); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci m3_ipc->dbg_path = debugfs_create_dir("wkup_m3_ipc", NULL); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (IS_ERR(m3_ipc->dbg_path)) 20662306a36Sopenharmony_ci return -EINVAL; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci (void)debugfs_create_file("enable_late_halt", 0644, 20962306a36Sopenharmony_ci m3_ipc->dbg_path, 21062306a36Sopenharmony_ci &m3_ipc->halt, 21162306a36Sopenharmony_ci &wkup_m3_ipc_option_fops); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci debugfs_remove_recursive(m3_ipc->dbg_path); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci#else 22162306a36Sopenharmony_cistatic inline int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci writel(AM33XX_M3_TXEV_ACK, 23462306a36Sopenharmony_ci m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void am33xx_txev_enable(struct wkup_m3_ipc *m3_ipc) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci writel(AM33XX_M3_TXEV_ENABLE, 24062306a36Sopenharmony_ci m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc *m3_ipc, 24462306a36Sopenharmony_ci u32 val, int ipc_reg_num) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT, 24762306a36Sopenharmony_ci "ipc register operation out of range")) 24862306a36Sopenharmony_ci return; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci writel(val, m3_ipc->ipc_mem_base + 25162306a36Sopenharmony_ci AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num)); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic unsigned int wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc *m3_ipc, 25562306a36Sopenharmony_ci int ipc_reg_num) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT, 25862306a36Sopenharmony_ci "ipc register operation out of range")) 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return readl(m3_ipc->ipc_mem_base + 26262306a36Sopenharmony_ci AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num)); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int wkup_m3_fw_version_read(struct wkup_m3_ipc *m3_ipc) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci int val; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci val = wkup_m3_ctrl_ipc_read(m3_ipc, 2); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return val & M3_FW_VERSION_MASK; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct wkup_m3_ipc *m3_ipc = ipc_data; 27762306a36Sopenharmony_ci struct device *dev = m3_ipc->dev; 27862306a36Sopenharmony_ci int ver = 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci am33xx_txev_eoi(m3_ipc); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci switch (m3_ipc->state) { 28362306a36Sopenharmony_ci case M3_STATE_RESET: 28462306a36Sopenharmony_ci ver = wkup_m3_fw_version_read(m3_ipc); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (ver == M3_VERSION_UNKNOWN || 28762306a36Sopenharmony_ci ver < M3_BASELINE_VERSION) { 28862306a36Sopenharmony_ci dev_warn(dev, "CM3 Firmware Version %x not supported\n", 28962306a36Sopenharmony_ci ver); 29062306a36Sopenharmony_ci } else { 29162306a36Sopenharmony_ci dev_info(dev, "CM3 Firmware Version = 0x%x\n", ver); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci m3_ipc->state = M3_STATE_INITED; 29562306a36Sopenharmony_ci wkup_m3_init_scale_data(m3_ipc, dev); 29662306a36Sopenharmony_ci complete(&m3_ipc->sync_complete); 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case M3_STATE_MSG_FOR_RESET: 29962306a36Sopenharmony_ci m3_ipc->state = M3_STATE_INITED; 30062306a36Sopenharmony_ci complete(&m3_ipc->sync_complete); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case M3_STATE_MSG_FOR_LP: 30362306a36Sopenharmony_ci complete(&m3_ipc->sync_complete); 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case M3_STATE_UNKNOWN: 30662306a36Sopenharmony_ci dev_warn(dev, "Unknown CM3 State\n"); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci am33xx_txev_enable(m3_ipc); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return IRQ_HANDLED; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct device *dev = m3_ipc->dev; 31762306a36Sopenharmony_ci mbox_msg_t dummy_msg = 0; 31862306a36Sopenharmony_ci int ret; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!m3_ipc->mbox) { 32162306a36Sopenharmony_ci dev_err(dev, 32262306a36Sopenharmony_ci "No IPC channel to communicate with wkup_m3!\n"); 32362306a36Sopenharmony_ci return -EIO; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * Write a dummy message to the mailbox in order to trigger the RX 32862306a36Sopenharmony_ci * interrupt to alert the M3 that data is available in the IPC 32962306a36Sopenharmony_ci * registers. We must enable the IRQ here and disable it after in 33062306a36Sopenharmony_ci * the RX callback to avoid multiple interrupts being received 33162306a36Sopenharmony_ci * by the CM3. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci ret = mbox_send_message(m3_ipc->mbox, &dummy_msg); 33462306a36Sopenharmony_ci if (ret < 0) { 33562306a36Sopenharmony_ci dev_err(dev, "%s: mbox_send_message() failed: %d\n", 33662306a36Sopenharmony_ci __func__, ret); 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ret = wait_for_completion_timeout(&m3_ipc->sync_complete, 34162306a36Sopenharmony_ci msecs_to_jiffies(500)); 34262306a36Sopenharmony_ci if (!ret) { 34362306a36Sopenharmony_ci dev_err(dev, "MPU<->CM3 sync failure\n"); 34462306a36Sopenharmony_ci m3_ipc->state = M3_STATE_UNKNOWN; 34562306a36Sopenharmony_ci return -EIO; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci mbox_client_txdone(m3_ipc->mbox, 0); 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct device *dev = m3_ipc->dev; 35562306a36Sopenharmony_ci mbox_msg_t dummy_msg = 0; 35662306a36Sopenharmony_ci int ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!m3_ipc->mbox) { 35962306a36Sopenharmony_ci dev_err(dev, 36062306a36Sopenharmony_ci "No IPC channel to communicate with wkup_m3!\n"); 36162306a36Sopenharmony_ci return -EIO; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ret = mbox_send_message(m3_ipc->mbox, &dummy_msg); 36562306a36Sopenharmony_ci if (ret < 0) { 36662306a36Sopenharmony_ci dev_err(dev, "%s: mbox_send_message() failed: %d\n", 36762306a36Sopenharmony_ci __func__, ret); 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mbox_client_txdone(m3_ipc->mbox, 0); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int wkup_m3_is_available(struct wkup_m3_ipc *m3_ipc) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci return ((m3_ipc->state != M3_STATE_RESET) && 37862306a36Sopenharmony_ci (m3_ipc->state != M3_STATE_UNKNOWN)); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic void wkup_m3_set_vtt_gpio(struct wkup_m3_ipc *m3_ipc, int gpio) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci m3_ipc->vtt_conf = (1 << IPC_VTT_STAT_SHIFT) | 38462306a36Sopenharmony_ci (gpio << IPC_VTT_GPIO_PIN_SHIFT); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void wkup_m3_set_io_isolation(struct wkup_m3_ipc *m3_ipc) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci m3_ipc->isolation_conf = (1 << IPC_IO_ISOLATION_STAT_SHIFT); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* Public functions */ 39362306a36Sopenharmony_ci/** 39462306a36Sopenharmony_ci * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use 39562306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context 39662306a36Sopenharmony_ci * @mem_type: memory type value read directly from emif 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * wkup_m3 must know what memory type is in use to properly suspend 39962306a36Sopenharmony_ci * and resume. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_cistatic void wkup_m3_set_mem_type(struct wkup_m3_ipc *m3_ipc, int mem_type) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci m3_ipc->mem_type = mem_type; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/** 40762306a36Sopenharmony_ci * wkup_m3_set_resume_address - Pass wkup_m3 resume address 40862306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context 40962306a36Sopenharmony_ci * @addr: Physical address from which resume code should execute 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cistatic void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci m3_ipc->resume_addr = (unsigned long)addr; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/** 41762306a36Sopenharmony_ci * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend 41862306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context 41962306a36Sopenharmony_ci * 42062306a36Sopenharmony_ci * Returns code representing the status of a low power mode transition. 42162306a36Sopenharmony_ci * 0 - Successful transition 42262306a36Sopenharmony_ci * 1 - Failure to transition to low power state 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cistatic int wkup_m3_request_pm_status(struct wkup_m3_ipc *m3_ipc) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci unsigned int i; 42762306a36Sopenharmony_ci int val; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci val = wkup_m3_ctrl_ipc_read(m3_ipc, 1); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci i = M3_STATUS_RESP_MASK & val; 43262306a36Sopenharmony_ci i >>= __ffs(M3_STATUS_RESP_MASK); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return i; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * wkup_m3_prepare_low_power - Request preparation for transition to 43962306a36Sopenharmony_ci * low power state 44062306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context 44162306a36Sopenharmony_ci * @state: A kernel suspend state to enter, either MEM or STANDBY 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * Returns 0 if preparation was successful, otherwise returns error code 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_cistatic int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct device *dev = m3_ipc->dev; 44862306a36Sopenharmony_ci int m3_power_state; 44962306a36Sopenharmony_ci int ret = 0; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (!wkup_m3_is_available(m3_ipc)) 45262306a36Sopenharmony_ci return -ENODEV; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci switch (state) { 45562306a36Sopenharmony_ci case WKUP_M3_DEEPSLEEP: 45662306a36Sopenharmony_ci m3_power_state = IPC_CMD_DS0; 45762306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->volt_scale_offsets, 5); 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci case WKUP_M3_STANDBY: 46062306a36Sopenharmony_ci m3_power_state = IPC_CMD_STANDBY; 46162306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci case WKUP_M3_IDLE: 46462306a36Sopenharmony_ci m3_power_state = IPC_CMD_IDLE; 46562306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci default: 46862306a36Sopenharmony_ci return 1; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Program each required IPC register then write defaults to others */ 47262306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0); 47362306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1); 47462306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type | 47562306a36Sopenharmony_ci m3_ipc->vtt_conf | 47662306a36Sopenharmony_ci m3_ipc->isolation_conf | 47762306a36Sopenharmony_ci m3_ipc->halt, 4); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); 48062306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); 48162306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6); 48262306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci m3_ipc->state = M3_STATE_MSG_FOR_LP; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (state == WKUP_M3_IDLE) 48762306a36Sopenharmony_ci ret = wkup_m3_ping_noirq(m3_ipc); 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci ret = wkup_m3_ping(m3_ipc); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (ret) { 49262306a36Sopenharmony_ci dev_err(dev, "Unable to ping CM3\n"); 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/** 50062306a36Sopenharmony_ci * wkup_m3_finish_low_power - Return m3 to reset state 50162306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context 50262306a36Sopenharmony_ci * 50362306a36Sopenharmony_ci * Returns 0 if reset was successful, otherwise returns error code 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_cistatic int wkup_m3_finish_low_power(struct wkup_m3_ipc *m3_ipc) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct device *dev = m3_ipc->dev; 50862306a36Sopenharmony_ci int ret = 0; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (!wkup_m3_is_available(m3_ipc)) 51162306a36Sopenharmony_ci return -ENODEV; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, IPC_CMD_RESET, 1); 51462306a36Sopenharmony_ci wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci m3_ipc->state = M3_STATE_MSG_FOR_RESET; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci ret = wkup_m3_ping(m3_ipc); 51962306a36Sopenharmony_ci if (ret) { 52062306a36Sopenharmony_ci dev_err(dev, "Unable to ping CM3\n"); 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * wkup_m3_request_wake_src - Get the wakeup source info passed from wkup_m3 52962306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic const char *wkup_m3_request_wake_src(struct wkup_m3_ipc *m3_ipc) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci unsigned int wakeup_src_idx; 53462306a36Sopenharmony_ci int j, val; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci val = wkup_m3_ctrl_ipc_read(m3_ipc, 6); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci wakeup_src_idx = val & M3_WAKE_SRC_MASK; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(wakeups) - 1; j++) { 54162306a36Sopenharmony_ci if (wakeups[j].irq_nr == wakeup_src_idx) 54262306a36Sopenharmony_ci return wakeups[j].src; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci return wakeups[j].src; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/** 54862306a36Sopenharmony_ci * wkup_m3_set_rtc_only - Set the rtc_only flag 54962306a36Sopenharmony_ci * @m3_ipc: Pointer to wkup_m3_ipc context 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_cistatic void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci if (m3_ipc_state) 55462306a36Sopenharmony_ci m3_ipc_state->is_rtc_only = true; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic struct wkup_m3_ipc_ops ipc_ops = { 55862306a36Sopenharmony_ci .set_mem_type = wkup_m3_set_mem_type, 55962306a36Sopenharmony_ci .set_resume_address = wkup_m3_set_resume_address, 56062306a36Sopenharmony_ci .prepare_low_power = wkup_m3_prepare_low_power, 56162306a36Sopenharmony_ci .finish_low_power = wkup_m3_finish_low_power, 56262306a36Sopenharmony_ci .request_pm_status = wkup_m3_request_pm_status, 56362306a36Sopenharmony_ci .request_wake_src = wkup_m3_request_wake_src, 56462306a36Sopenharmony_ci .set_rtc_only = wkup_m3_set_rtc_only, 56562306a36Sopenharmony_ci}; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/** 56862306a36Sopenharmony_ci * wkup_m3_ipc_get - Return handle to wkup_m3_ipc 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * Returns NULL if the wkup_m3 is not yet available, otherwise returns 57162306a36Sopenharmony_ci * pointer to wkup_m3_ipc struct. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_cistruct wkup_m3_ipc *wkup_m3_ipc_get(void) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci if (m3_ipc_state) 57662306a36Sopenharmony_ci get_device(m3_ipc_state->dev); 57762306a36Sopenharmony_ci else 57862306a36Sopenharmony_ci return NULL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return m3_ipc_state; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wkup_m3_ipc_get); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/** 58562306a36Sopenharmony_ci * wkup_m3_ipc_put - Free handle to wkup_m3_ipc returned from wkup_m3_ipc_get 58662306a36Sopenharmony_ci * @m3_ipc: A pointer to wkup_m3_ipc struct returned by wkup_m3_ipc_get 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_civoid wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci if (m3_ipc_state) 59162306a36Sopenharmony_ci put_device(m3_ipc_state->dev); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wkup_m3_ipc_put); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int wkup_m3_rproc_boot_thread(void *arg) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct wkup_m3_ipc *m3_ipc = arg; 59862306a36Sopenharmony_ci struct device *dev = m3_ipc->dev; 59962306a36Sopenharmony_ci int ret; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci init_completion(&m3_ipc->sync_complete); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ret = rproc_boot(m3_ipc->rproc); 60462306a36Sopenharmony_ci if (ret) 60562306a36Sopenharmony_ci dev_err(dev, "rproc_boot failed\n"); 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci m3_ipc_state = m3_ipc; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int wkup_m3_ipc_probe(struct platform_device *pdev) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 61562306a36Sopenharmony_ci int irq, ret, temp; 61662306a36Sopenharmony_ci phandle rproc_phandle; 61762306a36Sopenharmony_ci struct rproc *m3_rproc; 61862306a36Sopenharmony_ci struct task_struct *task; 61962306a36Sopenharmony_ci struct wkup_m3_ipc *m3_ipc; 62062306a36Sopenharmony_ci struct device_node *np = dev->of_node; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL); 62362306a36Sopenharmony_ci if (!m3_ipc) 62462306a36Sopenharmony_ci return -ENOMEM; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci m3_ipc->ipc_mem_base = devm_platform_ioremap_resource(pdev, 0); 62762306a36Sopenharmony_ci if (IS_ERR(m3_ipc->ipc_mem_base)) 62862306a36Sopenharmony_ci return PTR_ERR(m3_ipc->ipc_mem_base); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 63162306a36Sopenharmony_ci if (irq < 0) 63262306a36Sopenharmony_ci return irq; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, wkup_m3_txev_handler, 63562306a36Sopenharmony_ci 0, "wkup_m3_txev", m3_ipc); 63662306a36Sopenharmony_ci if (ret) { 63762306a36Sopenharmony_ci dev_err(dev, "request_irq failed\n"); 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci m3_ipc->mbox_client.dev = dev; 64262306a36Sopenharmony_ci m3_ipc->mbox_client.tx_done = NULL; 64362306a36Sopenharmony_ci m3_ipc->mbox_client.tx_prepare = NULL; 64462306a36Sopenharmony_ci m3_ipc->mbox_client.rx_callback = NULL; 64562306a36Sopenharmony_ci m3_ipc->mbox_client.tx_block = false; 64662306a36Sopenharmony_ci m3_ipc->mbox_client.knows_txdone = false; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci m3_ipc->mbox = mbox_request_channel(&m3_ipc->mbox_client, 0); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (IS_ERR(m3_ipc->mbox)) { 65162306a36Sopenharmony_ci dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n", 65262306a36Sopenharmony_ci PTR_ERR(m3_ipc->mbox)); 65362306a36Sopenharmony_ci return PTR_ERR(m3_ipc->mbox); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "ti,rproc", &rproc_phandle)) { 65762306a36Sopenharmony_ci dev_err(&pdev->dev, "could not get rproc phandle\n"); 65862306a36Sopenharmony_ci ret = -ENODEV; 65962306a36Sopenharmony_ci goto err_free_mbox; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci m3_rproc = rproc_get_by_phandle(rproc_phandle); 66362306a36Sopenharmony_ci if (!m3_rproc) { 66462306a36Sopenharmony_ci dev_err(&pdev->dev, "could not get rproc handle\n"); 66562306a36Sopenharmony_ci ret = -EPROBE_DEFER; 66662306a36Sopenharmony_ci goto err_free_mbox; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci m3_ipc->rproc = m3_rproc; 67062306a36Sopenharmony_ci m3_ipc->dev = dev; 67162306a36Sopenharmony_ci m3_ipc->state = M3_STATE_RESET; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci m3_ipc->ops = &ipc_ops; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (!of_property_read_u32(np, "ti,vtt-gpio-pin", &temp)) { 67662306a36Sopenharmony_ci if (temp >= 0 && temp <= 31) 67762306a36Sopenharmony_ci wkup_m3_set_vtt_gpio(m3_ipc, temp); 67862306a36Sopenharmony_ci else 67962306a36Sopenharmony_ci dev_warn(dev, "Invalid VTT GPIO(%d) pin\n", temp); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (of_property_read_bool(np, "ti,set-io-isolation")) 68362306a36Sopenharmony_ci wkup_m3_set_io_isolation(m3_ipc); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ret = of_property_read_string(np, "firmware-name", 68662306a36Sopenharmony_ci &m3_ipc->sd_fw_name); 68762306a36Sopenharmony_ci if (ret) { 68862306a36Sopenharmony_ci dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n"); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* 69262306a36Sopenharmony_ci * Wait for firmware loading completion in a thread so we 69362306a36Sopenharmony_ci * can boot the wkup_m3 as soon as it's ready without holding 69462306a36Sopenharmony_ci * up kernel boot 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci task = kthread_run(wkup_m3_rproc_boot_thread, m3_ipc, 69762306a36Sopenharmony_ci "wkup_m3_rproc_loader"); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (IS_ERR(task)) { 70062306a36Sopenharmony_ci dev_err(dev, "can't create rproc_boot thread\n"); 70162306a36Sopenharmony_ci ret = PTR_ERR(task); 70262306a36Sopenharmony_ci goto err_put_rproc; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci wkup_m3_ipc_dbg_init(m3_ipc); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cierr_put_rproc: 71062306a36Sopenharmony_ci rproc_put(m3_rproc); 71162306a36Sopenharmony_cierr_free_mbox: 71262306a36Sopenharmony_ci mbox_free_channel(m3_ipc->mbox); 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int wkup_m3_ipc_remove(struct platform_device *pdev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci wkup_m3_ipc_dbg_destroy(m3_ipc_state); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci mbox_free_channel(m3_ipc_state->mbox); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci rproc_shutdown(m3_ipc_state->rproc); 72362306a36Sopenharmony_ci rproc_put(m3_ipc_state->rproc); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci m3_ipc_state = NULL; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic int __maybe_unused wkup_m3_ipc_suspend(struct device *dev) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci /* 73362306a36Sopenharmony_ci * Nothing needs to be done on suspend even with rtc_only flag set 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int __maybe_unused wkup_m3_ipc_resume(struct device *dev) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci if (m3_ipc_state->is_rtc_only) { 74162306a36Sopenharmony_ci rproc_shutdown(m3_ipc_state->rproc); 74262306a36Sopenharmony_ci rproc_boot(m3_ipc_state->rproc); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci m3_ipc_state->is_rtc_only = false; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic const struct dev_pm_ops wkup_m3_ipc_pm_ops = { 75162306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(wkup_m3_ipc_suspend, wkup_m3_ipc_resume) 75262306a36Sopenharmony_ci}; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic const struct of_device_id wkup_m3_ipc_of_match[] = { 75562306a36Sopenharmony_ci { .compatible = "ti,am3352-wkup-m3-ipc", }, 75662306a36Sopenharmony_ci { .compatible = "ti,am4372-wkup-m3-ipc", }, 75762306a36Sopenharmony_ci {}, 75862306a36Sopenharmony_ci}; 75962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic struct platform_driver wkup_m3_ipc_driver = { 76262306a36Sopenharmony_ci .probe = wkup_m3_ipc_probe, 76362306a36Sopenharmony_ci .remove = wkup_m3_ipc_remove, 76462306a36Sopenharmony_ci .driver = { 76562306a36Sopenharmony_ci .name = "wkup_m3_ipc", 76662306a36Sopenharmony_ci .of_match_table = wkup_m3_ipc_of_match, 76762306a36Sopenharmony_ci .pm = &wkup_m3_ipc_pm_ops, 76862306a36Sopenharmony_ci }, 76962306a36Sopenharmony_ci}; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cimodule_platform_driver(wkup_m3_ipc_driver); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 77462306a36Sopenharmony_ciMODULE_DESCRIPTION("wkup m3 remote processor ipc driver"); 77562306a36Sopenharmony_ciMODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); 776