771 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * AMx3 Wkup M3 IPC driver4 *5 * Copyright (C) 2015 Texas Instruments, Inc.6 *7 * Dave Gerlach <d-gerlach@ti.com>8 */9 10#include <linux/debugfs.h>11#include <linux/err.h>12#include <linux/firmware.h>13#include <linux/kernel.h>14#include <linux/kthread.h>15#include <linux/interrupt.h>16#include <linux/irq.h>17#include <linux/module.h>18#include <linux/of.h>19#include <linux/platform_device.h>20#include <linux/remoteproc.h>21#include <linux/suspend.h>22#include <linux/wkup_m3_ipc.h>23 24#define AM33XX_CTRL_IPC_REG_COUNT 0x825#define AM33XX_CTRL_IPC_REG_OFFSET(m) (0x4 + 4 * (m))26 27/* AM33XX M3_TXEV_EOI register */28#define AM33XX_CONTROL_M3_TXEV_EOI 0x0029 30#define AM33XX_M3_TXEV_ACK (0x1 << 0)31#define AM33XX_M3_TXEV_ENABLE (0x0 << 0)32 33#define IPC_CMD_DS0 0x434#define IPC_CMD_STANDBY 0xc35#define IPC_CMD_IDLE 0x1036#define IPC_CMD_RESET 0xe37#define DS_IPC_DEFAULT 0xffffffff38#define M3_VERSION_UNKNOWN 0x0000ffff39#define M3_BASELINE_VERSION 0x19140#define M3_STATUS_RESP_MASK (0xffff << 16)41#define M3_FW_VERSION_MASK 0xffff42#define M3_WAKE_SRC_MASK 0xff43 44#define IPC_MEM_TYPE_SHIFT (0x0)45#define IPC_MEM_TYPE_MASK (0x7 << 0)46#define IPC_VTT_STAT_SHIFT (0x3)47#define IPC_VTT_STAT_MASK (0x1 << 3)48#define IPC_VTT_GPIO_PIN_SHIFT (0x4)49#define IPC_VTT_GPIO_PIN_MASK (0x3f << 4)50#define IPC_IO_ISOLATION_STAT_SHIFT (10)51#define IPC_IO_ISOLATION_STAT_MASK (0x1 << 10)52 53#define IPC_DBG_HALT_SHIFT (11)54#define IPC_DBG_HALT_MASK (0x1 << 11)55 56#define M3_STATE_UNKNOWN 057#define M3_STATE_RESET 158#define M3_STATE_INITED 259#define M3_STATE_MSG_FOR_LP 360#define M3_STATE_MSG_FOR_RESET 461 62#define WKUP_M3_SD_FW_MAGIC 0x570C63 64#define WKUP_M3_DMEM_START 0x8000065#define WKUP_M3_AUXDATA_OFFSET 0x100066#define WKUP_M3_AUXDATA_SIZE 0xFF67 68static struct wkup_m3_ipc *m3_ipc_state;69 70static const struct wkup_m3_wakeup_src wakeups[] = {71 {.irq_nr = 16, .src = "PRCM"},72 {.irq_nr = 35, .src = "USB0_PHY"},73 {.irq_nr = 36, .src = "USB1_PHY"},74 {.irq_nr = 40, .src = "I2C0"},75 {.irq_nr = 41, .src = "RTC Timer"},76 {.irq_nr = 42, .src = "RTC Alarm"},77 {.irq_nr = 43, .src = "Timer0"},78 {.irq_nr = 44, .src = "Timer1"},79 {.irq_nr = 45, .src = "UART"},80 {.irq_nr = 46, .src = "GPIO0"},81 {.irq_nr = 48, .src = "MPU_WAKE"},82 {.irq_nr = 49, .src = "WDT0"},83 {.irq_nr = 50, .src = "WDT1"},84 {.irq_nr = 51, .src = "ADC_TSC"},85 {.irq_nr = 0, .src = "Unknown"},86};87 88/**89 * wkup_m3_copy_aux_data - Copy auxiliary data to special region of m3 dmem90 * @data - pointer to data91 * @sz - size of data to copy (limit 256 bytes)92 *93 * Copies any additional blob of data to the wkup_m3 dmem to be used by the94 * firmware95 */96static unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc,97 const void *data, int sz)98{99 unsigned long aux_data_dev_addr;100 void *aux_data_addr;101 102 aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET;103 aux_data_addr = rproc_da_to_va(m3_ipc->rproc,104 aux_data_dev_addr,105 WKUP_M3_AUXDATA_SIZE,106 NULL);107 memcpy(aux_data_addr, data, sz);108 109 return WKUP_M3_AUXDATA_OFFSET;110}111 112static void wkup_m3_scale_data_fw_cb(const struct firmware *fw, void *context)113{114 unsigned long val, aux_base;115 struct wkup_m3_scale_data_header hdr;116 struct wkup_m3_ipc *m3_ipc = context;117 struct device *dev = m3_ipc->dev;118 119 if (!fw) {120 dev_err(dev, "Voltage scale fw name given but file missing.\n");121 return;122 }123 124 memcpy(&hdr, fw->data, sizeof(hdr));125 126 if (hdr.magic != WKUP_M3_SD_FW_MAGIC) {127 dev_err(dev, "PM: Voltage Scale Data binary does not appear valid.\n");128 goto release_sd_fw;129 }130 131 aux_base = wkup_m3_copy_aux_data(m3_ipc, fw->data + sizeof(hdr),132 fw->size - sizeof(hdr));133 134 val = (aux_base + hdr.sleep_offset);135 val |= ((aux_base + hdr.wake_offset) << 16);136 137 m3_ipc->volt_scale_offsets = val;138 139release_sd_fw:140 release_firmware(fw);141};142 143static int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc,144 struct device *dev)145{146 int ret = 0;147 148 /*149 * If no name is provided, user has already been warned, pm will150 * still work so return 0151 */152 153 if (!m3_ipc->sd_fw_name)154 return ret;155 156 ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,157 m3_ipc->sd_fw_name, dev, GFP_ATOMIC,158 m3_ipc, wkup_m3_scale_data_fw_cb);159 160 return ret;161}162 163#ifdef CONFIG_DEBUG_FS164static void wkup_m3_set_halt_late(bool enabled)165{166 if (enabled)167 m3_ipc_state->halt = (1 << IPC_DBG_HALT_SHIFT);168 else169 m3_ipc_state->halt = 0;170}171 172static int option_get(void *data, u64 *val)173{174 u32 *option = data;175 176 *val = *option;177 178 return 0;179}180 181static int option_set(void *data, u64 val)182{183 u32 *option = data;184 185 *option = val;186 187 if (option == &m3_ipc_state->halt) {188 if (val)189 wkup_m3_set_halt_late(true);190 else191 wkup_m3_set_halt_late(false);192 }193 194 return 0;195}196 197DEFINE_SIMPLE_ATTRIBUTE(wkup_m3_ipc_option_fops, option_get, option_set,198 "%llu\n");199 200static int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc)201{202 m3_ipc->dbg_path = debugfs_create_dir("wkup_m3_ipc", NULL);203 204 if (IS_ERR(m3_ipc->dbg_path))205 return -EINVAL;206 207 (void)debugfs_create_file("enable_late_halt", 0644,208 m3_ipc->dbg_path,209 &m3_ipc->halt,210 &wkup_m3_ipc_option_fops);211 212 return 0;213}214 215static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc)216{217 debugfs_remove_recursive(m3_ipc->dbg_path);218}219#else220static inline int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc)221{222 return 0;223}224 225static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc)226{227}228#endif /* CONFIG_DEBUG_FS */229 230static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc)231{232 writel(AM33XX_M3_TXEV_ACK,233 m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);234}235 236static void am33xx_txev_enable(struct wkup_m3_ipc *m3_ipc)237{238 writel(AM33XX_M3_TXEV_ENABLE,239 m3_ipc->ipc_mem_base + AM33XX_CONTROL_M3_TXEV_EOI);240}241 242static void wkup_m3_ctrl_ipc_write(struct wkup_m3_ipc *m3_ipc,243 u32 val, int ipc_reg_num)244{245 if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,246 "ipc register operation out of range"))247 return;248 249 writel(val, m3_ipc->ipc_mem_base +250 AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));251}252 253static unsigned int wkup_m3_ctrl_ipc_read(struct wkup_m3_ipc *m3_ipc,254 int ipc_reg_num)255{256 if (WARN(ipc_reg_num < 0 || ipc_reg_num > AM33XX_CTRL_IPC_REG_COUNT,257 "ipc register operation out of range"))258 return 0;259 260 return readl(m3_ipc->ipc_mem_base +261 AM33XX_CTRL_IPC_REG_OFFSET(ipc_reg_num));262}263 264static int wkup_m3_fw_version_read(struct wkup_m3_ipc *m3_ipc)265{266 int val;267 268 val = wkup_m3_ctrl_ipc_read(m3_ipc, 2);269 270 return val & M3_FW_VERSION_MASK;271}272 273static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data)274{275 struct wkup_m3_ipc *m3_ipc = ipc_data;276 struct device *dev = m3_ipc->dev;277 int ver = 0;278 279 am33xx_txev_eoi(m3_ipc);280 281 switch (m3_ipc->state) {282 case M3_STATE_RESET:283 ver = wkup_m3_fw_version_read(m3_ipc);284 285 if (ver == M3_VERSION_UNKNOWN ||286 ver < M3_BASELINE_VERSION) {287 dev_warn(dev, "CM3 Firmware Version %x not supported\n",288 ver);289 } else {290 dev_info(dev, "CM3 Firmware Version = 0x%x\n", ver);291 }292 293 m3_ipc->state = M3_STATE_INITED;294 wkup_m3_init_scale_data(m3_ipc, dev);295 complete(&m3_ipc->sync_complete);296 break;297 case M3_STATE_MSG_FOR_RESET:298 m3_ipc->state = M3_STATE_INITED;299 complete(&m3_ipc->sync_complete);300 break;301 case M3_STATE_MSG_FOR_LP:302 complete(&m3_ipc->sync_complete);303 break;304 case M3_STATE_UNKNOWN:305 dev_warn(dev, "Unknown CM3 State\n");306 }307 308 am33xx_txev_enable(m3_ipc);309 310 return IRQ_HANDLED;311}312 313static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc)314{315 struct device *dev = m3_ipc->dev;316 int ret;317 318 if (!m3_ipc->mbox) {319 dev_err(dev,320 "No IPC channel to communicate with wkup_m3!\n");321 return -EIO;322 }323 324 /*325 * Write a dummy message to the mailbox in order to trigger the RX326 * interrupt to alert the M3 that data is available in the IPC327 * registers. We must enable the IRQ here and disable it after in328 * the RX callback to avoid multiple interrupts being received329 * by the CM3.330 */331 ret = mbox_send_message(m3_ipc->mbox, NULL);332 if (ret < 0) {333 dev_err(dev, "%s: mbox_send_message() failed: %d\n",334 __func__, ret);335 return ret;336 }337 338 ret = wait_for_completion_timeout(&m3_ipc->sync_complete,339 msecs_to_jiffies(500));340 if (!ret) {341 dev_err(dev, "MPU<->CM3 sync failure\n");342 m3_ipc->state = M3_STATE_UNKNOWN;343 return -EIO;344 }345 346 mbox_client_txdone(m3_ipc->mbox, 0);347 return 0;348}349 350static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc)351{352 struct device *dev = m3_ipc->dev;353 int ret;354 355 if (!m3_ipc->mbox) {356 dev_err(dev,357 "No IPC channel to communicate with wkup_m3!\n");358 return -EIO;359 }360 361 ret = mbox_send_message(m3_ipc->mbox, NULL);362 if (ret < 0) {363 dev_err(dev, "%s: mbox_send_message() failed: %d\n",364 __func__, ret);365 return ret;366 }367 368 mbox_client_txdone(m3_ipc->mbox, 0);369 return 0;370}371 372static int wkup_m3_is_available(struct wkup_m3_ipc *m3_ipc)373{374 return ((m3_ipc->state != M3_STATE_RESET) &&375 (m3_ipc->state != M3_STATE_UNKNOWN));376}377 378static void wkup_m3_set_vtt_gpio(struct wkup_m3_ipc *m3_ipc, int gpio)379{380 m3_ipc->vtt_conf = (1 << IPC_VTT_STAT_SHIFT) |381 (gpio << IPC_VTT_GPIO_PIN_SHIFT);382}383 384static void wkup_m3_set_io_isolation(struct wkup_m3_ipc *m3_ipc)385{386 m3_ipc->isolation_conf = (1 << IPC_IO_ISOLATION_STAT_SHIFT);387}388 389/* Public functions */390/**391 * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use392 * @m3_ipc: Pointer to wkup_m3_ipc context393 * @mem_type: memory type value read directly from emif394 *395 * wkup_m3 must know what memory type is in use to properly suspend396 * and resume.397 */398static void wkup_m3_set_mem_type(struct wkup_m3_ipc *m3_ipc, int mem_type)399{400 m3_ipc->mem_type = mem_type;401}402 403/**404 * wkup_m3_set_resume_address - Pass wkup_m3 resume address405 * @m3_ipc: Pointer to wkup_m3_ipc context406 * @addr: Physical address from which resume code should execute407 */408static void wkup_m3_set_resume_address(struct wkup_m3_ipc *m3_ipc, void *addr)409{410 m3_ipc->resume_addr = (unsigned long)addr;411}412 413/**414 * wkup_m3_request_pm_status - Retrieve wkup_m3 status code after suspend415 * @m3_ipc: Pointer to wkup_m3_ipc context416 *417 * Returns code representing the status of a low power mode transition.418 * 0 - Successful transition419 * 1 - Failure to transition to low power state420 */421static int wkup_m3_request_pm_status(struct wkup_m3_ipc *m3_ipc)422{423 unsigned int i;424 int val;425 426 val = wkup_m3_ctrl_ipc_read(m3_ipc, 1);427 428 i = M3_STATUS_RESP_MASK & val;429 i >>= __ffs(M3_STATUS_RESP_MASK);430 431 return i;432}433 434/**435 * wkup_m3_prepare_low_power - Request preparation for transition to436 * low power state437 * @m3_ipc: Pointer to wkup_m3_ipc context438 * @state: A kernel suspend state to enter, either MEM or STANDBY439 *440 * Returns 0 if preparation was successful, otherwise returns error code441 */442static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state)443{444 struct device *dev = m3_ipc->dev;445 int m3_power_state;446 int ret = 0;447 448 if (!wkup_m3_is_available(m3_ipc))449 return -ENODEV;450 451 switch (state) {452 case WKUP_M3_DEEPSLEEP:453 m3_power_state = IPC_CMD_DS0;454 wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->volt_scale_offsets, 5);455 break;456 case WKUP_M3_STANDBY:457 m3_power_state = IPC_CMD_STANDBY;458 wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);459 break;460 case WKUP_M3_IDLE:461 m3_power_state = IPC_CMD_IDLE;462 wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);463 break;464 default:465 return 1;466 }467 468 /* Program each required IPC register then write defaults to others */469 wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0);470 wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1);471 wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type |472 m3_ipc->vtt_conf |473 m3_ipc->isolation_conf |474 m3_ipc->halt, 4);475 476 wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);477 wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3);478 wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6);479 wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7);480 481 m3_ipc->state = M3_STATE_MSG_FOR_LP;482 483 if (state == WKUP_M3_IDLE)484 ret = wkup_m3_ping_noirq(m3_ipc);485 else486 ret = wkup_m3_ping(m3_ipc);487 488 if (ret) {489 dev_err(dev, "Unable to ping CM3\n");490 return ret;491 }492 493 return 0;494}495 496/**497 * wkup_m3_finish_low_power - Return m3 to reset state498 * @m3_ipc: Pointer to wkup_m3_ipc context499 *500 * Returns 0 if reset was successful, otherwise returns error code501 */502static int wkup_m3_finish_low_power(struct wkup_m3_ipc *m3_ipc)503{504 struct device *dev = m3_ipc->dev;505 int ret = 0;506 507 if (!wkup_m3_is_available(m3_ipc))508 return -ENODEV;509 510 wkup_m3_ctrl_ipc_write(m3_ipc, IPC_CMD_RESET, 1);511 wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);512 513 m3_ipc->state = M3_STATE_MSG_FOR_RESET;514 515 ret = wkup_m3_ping(m3_ipc);516 if (ret) {517 dev_err(dev, "Unable to ping CM3\n");518 return ret;519 }520 521 return 0;522}523 524/**525 * wkup_m3_request_wake_src - Get the wakeup source info passed from wkup_m3526 * @m3_ipc: Pointer to wkup_m3_ipc context527 */528static const char *wkup_m3_request_wake_src(struct wkup_m3_ipc *m3_ipc)529{530 unsigned int wakeup_src_idx;531 int j, val;532 533 val = wkup_m3_ctrl_ipc_read(m3_ipc, 6);534 535 wakeup_src_idx = val & M3_WAKE_SRC_MASK;536 537 for (j = 0; j < ARRAY_SIZE(wakeups) - 1; j++) {538 if (wakeups[j].irq_nr == wakeup_src_idx)539 return wakeups[j].src;540 }541 return wakeups[j].src;542}543 544/**545 * wkup_m3_set_rtc_only - Set the rtc_only flag546 * @m3_ipc: Pointer to wkup_m3_ipc context547 */548static void wkup_m3_set_rtc_only(struct wkup_m3_ipc *m3_ipc)549{550 if (m3_ipc_state)551 m3_ipc_state->is_rtc_only = true;552}553 554static struct wkup_m3_ipc_ops ipc_ops = {555 .set_mem_type = wkup_m3_set_mem_type,556 .set_resume_address = wkup_m3_set_resume_address,557 .prepare_low_power = wkup_m3_prepare_low_power,558 .finish_low_power = wkup_m3_finish_low_power,559 .request_pm_status = wkup_m3_request_pm_status,560 .request_wake_src = wkup_m3_request_wake_src,561 .set_rtc_only = wkup_m3_set_rtc_only,562};563 564/**565 * wkup_m3_ipc_get - Return handle to wkup_m3_ipc566 *567 * Returns NULL if the wkup_m3 is not yet available, otherwise returns568 * pointer to wkup_m3_ipc struct.569 */570struct wkup_m3_ipc *wkup_m3_ipc_get(void)571{572 if (m3_ipc_state)573 get_device(m3_ipc_state->dev);574 else575 return NULL;576 577 return m3_ipc_state;578}579EXPORT_SYMBOL_GPL(wkup_m3_ipc_get);580 581/**582 * wkup_m3_ipc_put - Free handle to wkup_m3_ipc returned from wkup_m3_ipc_get583 * @m3_ipc: A pointer to wkup_m3_ipc struct returned by wkup_m3_ipc_get584 */585void wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc)586{587 if (m3_ipc_state)588 put_device(m3_ipc_state->dev);589}590EXPORT_SYMBOL_GPL(wkup_m3_ipc_put);591 592static int wkup_m3_rproc_boot_thread(void *arg)593{594 struct wkup_m3_ipc *m3_ipc = arg;595 struct device *dev = m3_ipc->dev;596 int ret;597 598 init_completion(&m3_ipc->sync_complete);599 600 ret = rproc_boot(m3_ipc->rproc);601 if (ret)602 dev_err(dev, "rproc_boot failed\n");603 else604 m3_ipc_state = m3_ipc;605 606 return 0;607}608 609static int wkup_m3_ipc_probe(struct platform_device *pdev)610{611 struct device *dev = &pdev->dev;612 int irq, ret, temp;613 phandle rproc_phandle;614 struct rproc *m3_rproc;615 struct task_struct *task;616 struct wkup_m3_ipc *m3_ipc;617 struct device_node *np = dev->of_node;618 619 m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL);620 if (!m3_ipc)621 return -ENOMEM;622 623 m3_ipc->ipc_mem_base = devm_platform_ioremap_resource(pdev, 0);624 if (IS_ERR(m3_ipc->ipc_mem_base))625 return PTR_ERR(m3_ipc->ipc_mem_base);626 627 irq = platform_get_irq(pdev, 0);628 if (irq < 0)629 return irq;630 631 ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,632 0, "wkup_m3_txev", m3_ipc);633 if (ret) {634 dev_err(dev, "request_irq failed\n");635 return ret;636 }637 638 m3_ipc->mbox_client.dev = dev;639 m3_ipc->mbox_client.tx_done = NULL;640 m3_ipc->mbox_client.tx_prepare = NULL;641 m3_ipc->mbox_client.rx_callback = NULL;642 m3_ipc->mbox_client.tx_block = false;643 m3_ipc->mbox_client.knows_txdone = false;644 645 m3_ipc->mbox = mbox_request_channel(&m3_ipc->mbox_client, 0);646 647 if (IS_ERR(m3_ipc->mbox)) {648 dev_err(dev, "IPC Request for A8->M3 Channel failed! %ld\n",649 PTR_ERR(m3_ipc->mbox));650 return PTR_ERR(m3_ipc->mbox);651 }652 653 if (of_property_read_u32(dev->of_node, "ti,rproc", &rproc_phandle)) {654 dev_err(&pdev->dev, "could not get rproc phandle\n");655 ret = -ENODEV;656 goto err_free_mbox;657 }658 659 m3_rproc = rproc_get_by_phandle(rproc_phandle);660 if (!m3_rproc) {661 dev_err(&pdev->dev, "could not get rproc handle\n");662 ret = -EPROBE_DEFER;663 goto err_free_mbox;664 }665 666 m3_ipc->rproc = m3_rproc;667 m3_ipc->dev = dev;668 m3_ipc->state = M3_STATE_RESET;669 670 m3_ipc->ops = &ipc_ops;671 672 if (!of_property_read_u32(np, "ti,vtt-gpio-pin", &temp)) {673 if (temp >= 0 && temp <= 31)674 wkup_m3_set_vtt_gpio(m3_ipc, temp);675 else676 dev_warn(dev, "Invalid VTT GPIO(%d) pin\n", temp);677 }678 679 if (of_property_read_bool(np, "ti,set-io-isolation"))680 wkup_m3_set_io_isolation(m3_ipc);681 682 ret = of_property_read_string(np, "firmware-name",683 &m3_ipc->sd_fw_name);684 if (ret) {685 dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n");686 }687 688 /*689 * Wait for firmware loading completion in a thread so we690 * can boot the wkup_m3 as soon as it's ready without holding691 * up kernel boot692 */693 task = kthread_run(wkup_m3_rproc_boot_thread, m3_ipc,694 "wkup_m3_rproc_loader");695 696 if (IS_ERR(task)) {697 dev_err(dev, "can't create rproc_boot thread\n");698 ret = PTR_ERR(task);699 goto err_put_rproc;700 }701 702 wkup_m3_ipc_dbg_init(m3_ipc);703 704 return 0;705 706err_put_rproc:707 rproc_put(m3_rproc);708err_free_mbox:709 mbox_free_channel(m3_ipc->mbox);710 return ret;711}712 713static void wkup_m3_ipc_remove(struct platform_device *pdev)714{715 wkup_m3_ipc_dbg_destroy(m3_ipc_state);716 717 mbox_free_channel(m3_ipc_state->mbox);718 719 rproc_shutdown(m3_ipc_state->rproc);720 rproc_put(m3_ipc_state->rproc);721 722 m3_ipc_state = NULL;723}724 725static int __maybe_unused wkup_m3_ipc_suspend(struct device *dev)726{727 /*728 * Nothing needs to be done on suspend even with rtc_only flag set729 */730 return 0;731}732 733static int __maybe_unused wkup_m3_ipc_resume(struct device *dev)734{735 if (m3_ipc_state->is_rtc_only) {736 rproc_shutdown(m3_ipc_state->rproc);737 rproc_boot(m3_ipc_state->rproc);738 }739 740 m3_ipc_state->is_rtc_only = false;741 742 return 0;743}744 745static const struct dev_pm_ops wkup_m3_ipc_pm_ops = {746 SET_SYSTEM_SLEEP_PM_OPS(wkup_m3_ipc_suspend, wkup_m3_ipc_resume)747};748 749static const struct of_device_id wkup_m3_ipc_of_match[] = {750 { .compatible = "ti,am3352-wkup-m3-ipc", },751 { .compatible = "ti,am4372-wkup-m3-ipc", },752 {},753};754MODULE_DEVICE_TABLE(of, wkup_m3_ipc_of_match);755 756static struct platform_driver wkup_m3_ipc_driver = {757 .probe = wkup_m3_ipc_probe,758 .remove_new = wkup_m3_ipc_remove,759 .driver = {760 .name = "wkup_m3_ipc",761 .of_match_table = wkup_m3_ipc_of_match,762 .pm = &wkup_m3_ipc_pm_ops,763 },764};765 766module_platform_driver(wkup_m3_ipc_driver);767 768MODULE_LICENSE("GPL v2");769MODULE_DESCRIPTION("wkup m3 remote processor ipc driver");770MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");771