18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMAP Remote Processor driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011-2020 Texas Instruments Incorporated - http://www.ti.com/ 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Google, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com> 98c2ecf20Sopenharmony_ci * Brian Swetland <swetland@google.com> 108c2ecf20Sopenharmony_ci * Fernando Guzman Lugo <fernando.lugo@ti.com> 118c2ecf20Sopenharmony_ci * Mark Grosen <mgrosen@ti.com> 128c2ecf20Sopenharmony_ci * Suman Anna <s-anna@ti.com> 138c2ecf20Sopenharmony_ci * Hari Kanigeri <h-kanigeri2@ti.com> 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/clk.h> 198c2ecf20Sopenharmony_ci#include <linux/clk/ti.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <linux/of_device.h> 238c2ecf20Sopenharmony_ci#include <linux/of_reserved_mem.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 268c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 278c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 288c2ecf20Sopenharmony_ci#include <linux/remoteproc.h> 298c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h> 308c2ecf20Sopenharmony_ci#include <linux/omap-iommu.h> 318c2ecf20Sopenharmony_ci#include <linux/omap-mailbox.h> 328c2ecf20Sopenharmony_ci#include <linux/regmap.h> 338c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 348c2ecf20Sopenharmony_ci#include <linux/reset.h> 358c2ecf20Sopenharmony_ci#include <clocksource/timer-ti-dm.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/platform_data/dmtimer-omap.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "omap_remoteproc.h" 408c2ecf20Sopenharmony_ci#include "remoteproc_internal.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* default auto-suspend delay (ms) */ 438c2ecf20Sopenharmony_ci#define DEFAULT_AUTOSUSPEND_DELAY 10000 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * struct omap_rproc_boot_data - boot data structure for the DSP omap rprocs 478c2ecf20Sopenharmony_ci * @syscon: regmap handle for the system control configuration module 488c2ecf20Sopenharmony_ci * @boot_reg: boot register offset within the @syscon regmap 498c2ecf20Sopenharmony_ci * @boot_reg_shift: bit-field shift required for the boot address value in 508c2ecf20Sopenharmony_ci * @boot_reg 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistruct omap_rproc_boot_data { 538c2ecf20Sopenharmony_ci struct regmap *syscon; 548c2ecf20Sopenharmony_ci unsigned int boot_reg; 558c2ecf20Sopenharmony_ci unsigned int boot_reg_shift; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * struct omap_rproc_mem - internal memory structure 608c2ecf20Sopenharmony_ci * @cpu_addr: MPU virtual address of the memory region 618c2ecf20Sopenharmony_ci * @bus_addr: bus address used to access the memory region 628c2ecf20Sopenharmony_ci * @dev_addr: device address of the memory region from DSP view 638c2ecf20Sopenharmony_ci * @size: size of the memory region 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistruct omap_rproc_mem { 668c2ecf20Sopenharmony_ci void __iomem *cpu_addr; 678c2ecf20Sopenharmony_ci phys_addr_t bus_addr; 688c2ecf20Sopenharmony_ci u32 dev_addr; 698c2ecf20Sopenharmony_ci size_t size; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/** 738c2ecf20Sopenharmony_ci * struct omap_rproc_timer - data structure for a timer used by a omap rproc 748c2ecf20Sopenharmony_ci * @odt: timer pointer 758c2ecf20Sopenharmony_ci * @timer_ops: OMAP dmtimer ops for @odt timer 768c2ecf20Sopenharmony_ci * @irq: timer irq 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistruct omap_rproc_timer { 798c2ecf20Sopenharmony_ci struct omap_dm_timer *odt; 808c2ecf20Sopenharmony_ci const struct omap_dm_timer_ops *timer_ops; 818c2ecf20Sopenharmony_ci int irq; 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/** 858c2ecf20Sopenharmony_ci * struct omap_rproc - omap remote processor state 868c2ecf20Sopenharmony_ci * @mbox: mailbox channel handle 878c2ecf20Sopenharmony_ci * @client: mailbox client to request the mailbox channel 888c2ecf20Sopenharmony_ci * @boot_data: boot data structure for setting processor boot address 898c2ecf20Sopenharmony_ci * @mem: internal memory regions data 908c2ecf20Sopenharmony_ci * @num_mems: number of internal memory regions 918c2ecf20Sopenharmony_ci * @num_timers: number of rproc timer(s) 928c2ecf20Sopenharmony_ci * @num_wd_timers: number of rproc watchdog timers 938c2ecf20Sopenharmony_ci * @timers: timer(s) info used by rproc 948c2ecf20Sopenharmony_ci * @autosuspend_delay: auto-suspend delay value to be used for runtime pm 958c2ecf20Sopenharmony_ci * @need_resume: if true a resume is needed in the system resume callback 968c2ecf20Sopenharmony_ci * @rproc: rproc handle 978c2ecf20Sopenharmony_ci * @reset: reset handle 988c2ecf20Sopenharmony_ci * @pm_comp: completion primitive to sync for suspend response 998c2ecf20Sopenharmony_ci * @fck: functional clock for the remoteproc 1008c2ecf20Sopenharmony_ci * @suspend_acked: state machine flag to store the suspend request ack 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistruct omap_rproc { 1038c2ecf20Sopenharmony_ci struct mbox_chan *mbox; 1048c2ecf20Sopenharmony_ci struct mbox_client client; 1058c2ecf20Sopenharmony_ci struct omap_rproc_boot_data *boot_data; 1068c2ecf20Sopenharmony_ci struct omap_rproc_mem *mem; 1078c2ecf20Sopenharmony_ci int num_mems; 1088c2ecf20Sopenharmony_ci int num_timers; 1098c2ecf20Sopenharmony_ci int num_wd_timers; 1108c2ecf20Sopenharmony_ci struct omap_rproc_timer *timers; 1118c2ecf20Sopenharmony_ci int autosuspend_delay; 1128c2ecf20Sopenharmony_ci bool need_resume; 1138c2ecf20Sopenharmony_ci struct rproc *rproc; 1148c2ecf20Sopenharmony_ci struct reset_control *reset; 1158c2ecf20Sopenharmony_ci struct completion pm_comp; 1168c2ecf20Sopenharmony_ci struct clk *fck; 1178c2ecf20Sopenharmony_ci bool suspend_acked; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * struct omap_rproc_mem_data - memory definitions for an omap remote processor 1228c2ecf20Sopenharmony_ci * @name: name for this memory entry 1238c2ecf20Sopenharmony_ci * @dev_addr: device address for the memory entry 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistruct omap_rproc_mem_data { 1268c2ecf20Sopenharmony_ci const char *name; 1278c2ecf20Sopenharmony_ci const u32 dev_addr; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/** 1318c2ecf20Sopenharmony_ci * struct omap_rproc_dev_data - device data for the omap remote processor 1328c2ecf20Sopenharmony_ci * @device_name: device name of the remote processor 1338c2ecf20Sopenharmony_ci * @mems: memory definitions for this remote processor 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cistruct omap_rproc_dev_data { 1368c2ecf20Sopenharmony_ci const char *device_name; 1378c2ecf20Sopenharmony_ci const struct omap_rproc_mem_data *mems; 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/** 1418c2ecf20Sopenharmony_ci * omap_rproc_request_timer() - request a timer for a remoteproc 1428c2ecf20Sopenharmony_ci * @dev: device requesting the timer 1438c2ecf20Sopenharmony_ci * @np: device node pointer to the desired timer 1448c2ecf20Sopenharmony_ci * @timer: handle to a struct omap_rproc_timer to return the timer handle 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * This helper function is used primarily to request a timer associated with 1478c2ecf20Sopenharmony_ci * a remoteproc. The returned handle is stored in the .odt field of the 1488c2ecf20Sopenharmony_ci * @timer structure passed in, and is used to invoke other timer specific 1498c2ecf20Sopenharmony_ci * ops (like starting a timer either during device initialization or during 1508c2ecf20Sopenharmony_ci * a resume operation, or for stopping/freeing a timer). 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * Return: 0 on success, otherwise an appropriate failure 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic int omap_rproc_request_timer(struct device *dev, struct device_node *np, 1558c2ecf20Sopenharmony_ci struct omap_rproc_timer *timer) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci timer->odt = timer->timer_ops->request_by_node(np); 1608c2ecf20Sopenharmony_ci if (!timer->odt) { 1618c2ecf20Sopenharmony_ci dev_err(dev, "request for timer node %p failed\n", np); 1628c2ecf20Sopenharmony_ci return -EBUSY; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = timer->timer_ops->set_source(timer->odt, OMAP_TIMER_SRC_SYS_CLK); 1668c2ecf20Sopenharmony_ci if (ret) { 1678c2ecf20Sopenharmony_ci dev_err(dev, "error setting OMAP_TIMER_SRC_SYS_CLK as source for timer node %p\n", 1688c2ecf20Sopenharmony_ci np); 1698c2ecf20Sopenharmony_ci timer->timer_ops->free(timer->odt); 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* clean counter, remoteproc code will set the value */ 1748c2ecf20Sopenharmony_ci timer->timer_ops->set_load(timer->odt, 0); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/** 1808c2ecf20Sopenharmony_ci * omap_rproc_start_timer() - start a timer for a remoteproc 1818c2ecf20Sopenharmony_ci * @timer: handle to a OMAP rproc timer 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * This helper function is used to start a timer associated with a remoteproc, 1848c2ecf20Sopenharmony_ci * obtained using the request_timer ops. The helper function needs to be 1858c2ecf20Sopenharmony_ci * invoked by the driver to start the timer (during device initialization) 1868c2ecf20Sopenharmony_ci * or to just resume the timer. 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * Return: 0 on success, otherwise a failure as returned by DMTimer ops 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic inline int omap_rproc_start_timer(struct omap_rproc_timer *timer) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return timer->timer_ops->start(timer->odt); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * omap_rproc_stop_timer() - stop a timer for a remoteproc 1978c2ecf20Sopenharmony_ci * @timer: handle to a OMAP rproc timer 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * This helper function is used to disable a timer associated with a 2008c2ecf20Sopenharmony_ci * remoteproc, and needs to be called either during a device shutdown 2018c2ecf20Sopenharmony_ci * or suspend operation. The separate helper function allows the driver 2028c2ecf20Sopenharmony_ci * to just stop a timer without having to release the timer during a 2038c2ecf20Sopenharmony_ci * suspend operation. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * Return: 0 on success, otherwise a failure as returned by DMTimer ops 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic inline int omap_rproc_stop_timer(struct omap_rproc_timer *timer) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci return timer->timer_ops->stop(timer->odt); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/** 2138c2ecf20Sopenharmony_ci * omap_rproc_release_timer() - release a timer for a remoteproc 2148c2ecf20Sopenharmony_ci * @timer: handle to a OMAP rproc timer 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * This helper function is used primarily to release a timer associated 2178c2ecf20Sopenharmony_ci * with a remoteproc. The dmtimer will be available for other clients to 2188c2ecf20Sopenharmony_ci * use once released. 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * Return: 0 on success, otherwise a failure as returned by DMTimer ops 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic inline int omap_rproc_release_timer(struct omap_rproc_timer *timer) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci return timer->timer_ops->free(timer->odt); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/** 2288c2ecf20Sopenharmony_ci * omap_rproc_get_timer_irq() - get the irq for a timer 2298c2ecf20Sopenharmony_ci * @timer: handle to a OMAP rproc timer 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * This function is used to get the irq associated with a watchdog timer. The 2328c2ecf20Sopenharmony_ci * function is called by the OMAP remoteproc driver to register a interrupt 2338c2ecf20Sopenharmony_ci * handler to handle watchdog events on the remote processor. 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Return: irq id on success, otherwise a failure as returned by DMTimer ops 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_cistatic inline int omap_rproc_get_timer_irq(struct omap_rproc_timer *timer) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return timer->timer_ops->get_irq(timer->odt); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/** 2438c2ecf20Sopenharmony_ci * omap_rproc_ack_timer_irq() - acknowledge a timer irq 2448c2ecf20Sopenharmony_ci * @timer: handle to a OMAP rproc timer 2458c2ecf20Sopenharmony_ci * 2468c2ecf20Sopenharmony_ci * This function is used to clear the irq associated with a watchdog timer. The 2478c2ecf20Sopenharmony_ci * The function is called by the OMAP remoteproc upon a watchdog event on the 2488c2ecf20Sopenharmony_ci * remote processor to clear the interrupt status of the watchdog timer. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic inline void omap_rproc_ack_timer_irq(struct omap_rproc_timer *timer) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci timer->timer_ops->write_status(timer->odt, OMAP_TIMER_INT_OVERFLOW); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/** 2568c2ecf20Sopenharmony_ci * omap_rproc_watchdog_isr() - Watchdog ISR handler for remoteproc device 2578c2ecf20Sopenharmony_ci * @irq: IRQ number associated with a watchdog timer 2588c2ecf20Sopenharmony_ci * @data: IRQ handler data 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * This ISR routine executes the required necessary low-level code to 2618c2ecf20Sopenharmony_ci * acknowledge a watchdog timer interrupt. There can be multiple watchdog 2628c2ecf20Sopenharmony_ci * timers associated with a rproc (like IPUs which have 2 watchdog timers, 2638c2ecf20Sopenharmony_ci * one per Cortex M3/M4 core), so a lookup has to be performed to identify 2648c2ecf20Sopenharmony_ci * the timer to acknowledge its interrupt. 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * The function also invokes rproc_report_crash to report the watchdog event 2678c2ecf20Sopenharmony_ci * to the remoteproc driver core, to trigger a recovery. 2688c2ecf20Sopenharmony_ci * 2698c2ecf20Sopenharmony_ci * Return: IRQ_HANDLED on success, otherwise IRQ_NONE 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic irqreturn_t omap_rproc_watchdog_isr(int irq, void *data) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct rproc *rproc = data; 2748c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 2758c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 2768c2ecf20Sopenharmony_ci struct omap_rproc_timer *timers = oproc->timers; 2778c2ecf20Sopenharmony_ci struct omap_rproc_timer *wd_timer = NULL; 2788c2ecf20Sopenharmony_ci int num_timers = oproc->num_timers + oproc->num_wd_timers; 2798c2ecf20Sopenharmony_ci int i; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci for (i = oproc->num_timers; i < num_timers; i++) { 2828c2ecf20Sopenharmony_ci if (timers[i].irq > 0 && irq == timers[i].irq) { 2838c2ecf20Sopenharmony_ci wd_timer = &timers[i]; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!wd_timer) { 2898c2ecf20Sopenharmony_ci dev_err(dev, "invalid timer\n"); 2908c2ecf20Sopenharmony_ci return IRQ_NONE; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci omap_rproc_ack_timer_irq(wd_timer); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci rproc_report_crash(rproc, RPROC_WATCHDOG); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/** 3018c2ecf20Sopenharmony_ci * omap_rproc_enable_timers() - enable the timers for a remoteproc 3028c2ecf20Sopenharmony_ci * @rproc: handle of a remote processor 3038c2ecf20Sopenharmony_ci * @configure: boolean flag used to acquire and configure the timer handle 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * This function is used primarily to enable the timers associated with 3068c2ecf20Sopenharmony_ci * a remoteproc. The configure flag is provided to allow the driver to 3078c2ecf20Sopenharmony_ci * to either acquire and start a timer (during device initialization) or 3088c2ecf20Sopenharmony_ci * to just start a timer (during a resume operation). 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * Return: 0 on success, otherwise an appropriate failure 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic int omap_rproc_enable_timers(struct rproc *rproc, bool configure) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int i; 3158c2ecf20Sopenharmony_ci int ret = 0; 3168c2ecf20Sopenharmony_ci struct platform_device *tpdev; 3178c2ecf20Sopenharmony_ci struct dmtimer_platform_data *tpdata; 3188c2ecf20Sopenharmony_ci const struct omap_dm_timer_ops *timer_ops; 3198c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 3208c2ecf20Sopenharmony_ci struct omap_rproc_timer *timers = oproc->timers; 3218c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 3228c2ecf20Sopenharmony_ci struct device_node *np = NULL; 3238c2ecf20Sopenharmony_ci int num_timers = oproc->num_timers + oproc->num_wd_timers; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (!num_timers) 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!configure) 3298c2ecf20Sopenharmony_ci goto start_timers; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci for (i = 0; i < num_timers; i++) { 3328c2ecf20Sopenharmony_ci if (i < oproc->num_timers) 3338c2ecf20Sopenharmony_ci np = of_parse_phandle(dev->of_node, "ti,timers", i); 3348c2ecf20Sopenharmony_ci else 3358c2ecf20Sopenharmony_ci np = of_parse_phandle(dev->of_node, 3368c2ecf20Sopenharmony_ci "ti,watchdog-timers", 3378c2ecf20Sopenharmony_ci (i - oproc->num_timers)); 3388c2ecf20Sopenharmony_ci if (!np) { 3398c2ecf20Sopenharmony_ci ret = -ENXIO; 3408c2ecf20Sopenharmony_ci dev_err(dev, "device node lookup for timer at index %d failed: %d\n", 3418c2ecf20Sopenharmony_ci i < oproc->num_timers ? i : 3428c2ecf20Sopenharmony_ci i - oproc->num_timers, ret); 3438c2ecf20Sopenharmony_ci goto free_timers; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci tpdev = of_find_device_by_node(np); 3478c2ecf20Sopenharmony_ci if (!tpdev) { 3488c2ecf20Sopenharmony_ci ret = -ENODEV; 3498c2ecf20Sopenharmony_ci dev_err(dev, "could not get timer platform device\n"); 3508c2ecf20Sopenharmony_ci goto put_node; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci tpdata = dev_get_platdata(&tpdev->dev); 3548c2ecf20Sopenharmony_ci put_device(&tpdev->dev); 3558c2ecf20Sopenharmony_ci if (!tpdata) { 3568c2ecf20Sopenharmony_ci ret = -EINVAL; 3578c2ecf20Sopenharmony_ci dev_err(dev, "dmtimer pdata structure NULL\n"); 3588c2ecf20Sopenharmony_ci goto put_node; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci timer_ops = tpdata->timer_ops; 3628c2ecf20Sopenharmony_ci if (!timer_ops || !timer_ops->request_by_node || 3638c2ecf20Sopenharmony_ci !timer_ops->set_source || !timer_ops->set_load || 3648c2ecf20Sopenharmony_ci !timer_ops->free || !timer_ops->start || 3658c2ecf20Sopenharmony_ci !timer_ops->stop || !timer_ops->get_irq || 3668c2ecf20Sopenharmony_ci !timer_ops->write_status) { 3678c2ecf20Sopenharmony_ci ret = -EINVAL; 3688c2ecf20Sopenharmony_ci dev_err(dev, "device does not have required timer ops\n"); 3698c2ecf20Sopenharmony_ci goto put_node; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci timers[i].irq = -1; 3738c2ecf20Sopenharmony_ci timers[i].timer_ops = timer_ops; 3748c2ecf20Sopenharmony_ci ret = omap_rproc_request_timer(dev, np, &timers[i]); 3758c2ecf20Sopenharmony_ci if (ret) { 3768c2ecf20Sopenharmony_ci dev_err(dev, "request for timer %p failed: %d\n", np, 3778c2ecf20Sopenharmony_ci ret); 3788c2ecf20Sopenharmony_ci goto put_node; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci of_node_put(np); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (i >= oproc->num_timers) { 3838c2ecf20Sopenharmony_ci timers[i].irq = omap_rproc_get_timer_irq(&timers[i]); 3848c2ecf20Sopenharmony_ci if (timers[i].irq < 0) { 3858c2ecf20Sopenharmony_ci dev_err(dev, "get_irq for timer %p failed: %d\n", 3868c2ecf20Sopenharmony_ci np, timers[i].irq); 3878c2ecf20Sopenharmony_ci ret = -EBUSY; 3888c2ecf20Sopenharmony_ci goto free_timers; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ret = request_irq(timers[i].irq, 3928c2ecf20Sopenharmony_ci omap_rproc_watchdog_isr, IRQF_SHARED, 3938c2ecf20Sopenharmony_ci "rproc-wdt", rproc); 3948c2ecf20Sopenharmony_ci if (ret) { 3958c2ecf20Sopenharmony_ci dev_err(dev, "error requesting irq for timer %p\n", 3968c2ecf20Sopenharmony_ci np); 3978c2ecf20Sopenharmony_ci omap_rproc_release_timer(&timers[i]); 3988c2ecf20Sopenharmony_ci timers[i].odt = NULL; 3998c2ecf20Sopenharmony_ci timers[i].timer_ops = NULL; 4008c2ecf20Sopenharmony_ci timers[i].irq = -1; 4018c2ecf20Sopenharmony_ci goto free_timers; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistart_timers: 4078c2ecf20Sopenharmony_ci for (i = 0; i < num_timers; i++) { 4088c2ecf20Sopenharmony_ci ret = omap_rproc_start_timer(&timers[i]); 4098c2ecf20Sopenharmony_ci if (ret) { 4108c2ecf20Sopenharmony_ci dev_err(dev, "start timer %p failed failed: %d\n", np, 4118c2ecf20Sopenharmony_ci ret); 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci if (ret) { 4168c2ecf20Sopenharmony_ci while (i >= 0) { 4178c2ecf20Sopenharmony_ci omap_rproc_stop_timer(&timers[i]); 4188c2ecf20Sopenharmony_ci i--; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci goto put_node; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciput_node: 4258c2ecf20Sopenharmony_ci if (configure) 4268c2ecf20Sopenharmony_ci of_node_put(np); 4278c2ecf20Sopenharmony_cifree_timers: 4288c2ecf20Sopenharmony_ci while (i--) { 4298c2ecf20Sopenharmony_ci if (i >= oproc->num_timers) 4308c2ecf20Sopenharmony_ci free_irq(timers[i].irq, rproc); 4318c2ecf20Sopenharmony_ci omap_rproc_release_timer(&timers[i]); 4328c2ecf20Sopenharmony_ci timers[i].odt = NULL; 4338c2ecf20Sopenharmony_ci timers[i].timer_ops = NULL; 4348c2ecf20Sopenharmony_ci timers[i].irq = -1; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return ret; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/** 4418c2ecf20Sopenharmony_ci * omap_rproc_disable_timers() - disable the timers for a remoteproc 4428c2ecf20Sopenharmony_ci * @rproc: handle of a remote processor 4438c2ecf20Sopenharmony_ci * @configure: boolean flag used to release the timer handle 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * This function is used primarily to disable the timers associated with 4468c2ecf20Sopenharmony_ci * a remoteproc. The configure flag is provided to allow the driver to 4478c2ecf20Sopenharmony_ci * to either stop and release a timer (during device shutdown) or to just 4488c2ecf20Sopenharmony_ci * stop a timer (during a suspend operation). 4498c2ecf20Sopenharmony_ci * 4508c2ecf20Sopenharmony_ci * Return: 0 on success or no timers 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_cistatic int omap_rproc_disable_timers(struct rproc *rproc, bool configure) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci int i; 4558c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 4568c2ecf20Sopenharmony_ci struct omap_rproc_timer *timers = oproc->timers; 4578c2ecf20Sopenharmony_ci int num_timers = oproc->num_timers + oproc->num_wd_timers; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!num_timers) 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci for (i = 0; i < num_timers; i++) { 4638c2ecf20Sopenharmony_ci omap_rproc_stop_timer(&timers[i]); 4648c2ecf20Sopenharmony_ci if (configure) { 4658c2ecf20Sopenharmony_ci if (i >= oproc->num_timers) 4668c2ecf20Sopenharmony_ci free_irq(timers[i].irq, rproc); 4678c2ecf20Sopenharmony_ci omap_rproc_release_timer(&timers[i]); 4688c2ecf20Sopenharmony_ci timers[i].odt = NULL; 4698c2ecf20Sopenharmony_ci timers[i].timer_ops = NULL; 4708c2ecf20Sopenharmony_ci timers[i].irq = -1; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/** 4788c2ecf20Sopenharmony_ci * omap_rproc_mbox_callback() - inbound mailbox message handler 4798c2ecf20Sopenharmony_ci * @client: mailbox client pointer used for requesting the mailbox channel 4808c2ecf20Sopenharmony_ci * @data: mailbox payload 4818c2ecf20Sopenharmony_ci * 4828c2ecf20Sopenharmony_ci * This handler is invoked by omap's mailbox driver whenever a mailbox 4838c2ecf20Sopenharmony_ci * message is received. Usually, the mailbox payload simply contains 4848c2ecf20Sopenharmony_ci * the index of the virtqueue that is kicked by the remote processor, 4858c2ecf20Sopenharmony_ci * and we let remoteproc core handle it. 4868c2ecf20Sopenharmony_ci * 4878c2ecf20Sopenharmony_ci * In addition to virtqueue indices, we also have some out-of-band values 4888c2ecf20Sopenharmony_ci * that indicates different events. Those values are deliberately very 4898c2ecf20Sopenharmony_ci * big so they don't coincide with virtqueue indices. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic void omap_rproc_mbox_callback(struct mbox_client *client, void *data) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct omap_rproc *oproc = container_of(client, struct omap_rproc, 4948c2ecf20Sopenharmony_ci client); 4958c2ecf20Sopenharmony_ci struct device *dev = oproc->rproc->dev.parent; 4968c2ecf20Sopenharmony_ci const char *name = oproc->rproc->name; 4978c2ecf20Sopenharmony_ci u32 msg = (u32)data; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci dev_dbg(dev, "mbox msg: 0x%x\n", msg); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci switch (msg) { 5028c2ecf20Sopenharmony_ci case RP_MBOX_CRASH: 5038c2ecf20Sopenharmony_ci /* 5048c2ecf20Sopenharmony_ci * remoteproc detected an exception, notify the rproc core. 5058c2ecf20Sopenharmony_ci * The remoteproc core will handle the recovery. 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci dev_err(dev, "omap rproc %s crashed\n", name); 5088c2ecf20Sopenharmony_ci rproc_report_crash(oproc->rproc, RPROC_FATAL_ERROR); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case RP_MBOX_ECHO_REPLY: 5118c2ecf20Sopenharmony_ci dev_info(dev, "received echo reply from %s\n", name); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci case RP_MBOX_SUSPEND_ACK: 5148c2ecf20Sopenharmony_ci case RP_MBOX_SUSPEND_CANCEL: 5158c2ecf20Sopenharmony_ci oproc->suspend_acked = msg == RP_MBOX_SUSPEND_ACK; 5168c2ecf20Sopenharmony_ci complete(&oproc->pm_comp); 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci default: 5198c2ecf20Sopenharmony_ci if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG) 5208c2ecf20Sopenharmony_ci return; 5218c2ecf20Sopenharmony_ci if (msg > oproc->rproc->max_notifyid) { 5228c2ecf20Sopenharmony_ci dev_dbg(dev, "dropping unknown message 0x%x", msg); 5238c2ecf20Sopenharmony_ci return; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci /* msg contains the index of the triggered vring */ 5268c2ecf20Sopenharmony_ci if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE) 5278c2ecf20Sopenharmony_ci dev_dbg(dev, "no message was found in vqid %d\n", msg); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* kick a virtqueue */ 5328c2ecf20Sopenharmony_cistatic void omap_rproc_kick(struct rproc *rproc, int vqid) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 5358c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 5368c2ecf20Sopenharmony_ci int ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* wake up the rproc before kicking it */ 5398c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 5408c2ecf20Sopenharmony_ci if (WARN_ON(ret < 0)) { 5418c2ecf20Sopenharmony_ci dev_err(dev, "pm_runtime_get_sync() failed during kick, ret = %d\n", 5428c2ecf20Sopenharmony_ci ret); 5438c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 5448c2ecf20Sopenharmony_ci return; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* send the index of the triggered virtqueue in the mailbox payload */ 5488c2ecf20Sopenharmony_ci ret = mbox_send_message(oproc->mbox, (void *)vqid); 5498c2ecf20Sopenharmony_ci if (ret < 0) 5508c2ecf20Sopenharmony_ci dev_err(dev, "failed to send mailbox message, status = %d\n", 5518c2ecf20Sopenharmony_ci ret); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 5548c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci/** 5588c2ecf20Sopenharmony_ci * omap_rproc_write_dsp_boot_addr() - set boot address for DSP remote processor 5598c2ecf20Sopenharmony_ci * @rproc: handle of a remote processor 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * Set boot address for a supported DSP remote processor. 5628c2ecf20Sopenharmony_ci * 5638c2ecf20Sopenharmony_ci * Return: 0 on success, or -EINVAL if boot address is not aligned properly 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_cistatic int omap_rproc_write_dsp_boot_addr(struct rproc *rproc) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 5688c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 5698c2ecf20Sopenharmony_ci struct omap_rproc_boot_data *bdata = oproc->boot_data; 5708c2ecf20Sopenharmony_ci u32 offset = bdata->boot_reg; 5718c2ecf20Sopenharmony_ci u32 value; 5728c2ecf20Sopenharmony_ci u32 mask; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (rproc->bootaddr & (SZ_1K - 1)) { 5758c2ecf20Sopenharmony_ci dev_err(dev, "invalid boot address 0x%llx, must be aligned on a 1KB boundary\n", 5768c2ecf20Sopenharmony_ci rproc->bootaddr); 5778c2ecf20Sopenharmony_ci return -EINVAL; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci value = rproc->bootaddr >> bdata->boot_reg_shift; 5818c2ecf20Sopenharmony_ci mask = ~(SZ_1K - 1) >> bdata->boot_reg_shift; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return regmap_update_bits(bdata->syscon, offset, mask, value); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci/* 5878c2ecf20Sopenharmony_ci * Power up the remote processor. 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * This function will be invoked only after the firmware for this rproc 5908c2ecf20Sopenharmony_ci * was loaded, parsed successfully, and all of its resource requirements 5918c2ecf20Sopenharmony_ci * were met. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_cistatic int omap_rproc_start(struct rproc *rproc) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 5968c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 5978c2ecf20Sopenharmony_ci int ret; 5988c2ecf20Sopenharmony_ci struct mbox_client *client = &oproc->client; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (oproc->boot_data) { 6018c2ecf20Sopenharmony_ci ret = omap_rproc_write_dsp_boot_addr(rproc); 6028c2ecf20Sopenharmony_ci if (ret) 6038c2ecf20Sopenharmony_ci return ret; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci client->dev = dev; 6078c2ecf20Sopenharmony_ci client->tx_done = NULL; 6088c2ecf20Sopenharmony_ci client->rx_callback = omap_rproc_mbox_callback; 6098c2ecf20Sopenharmony_ci client->tx_block = false; 6108c2ecf20Sopenharmony_ci client->knows_txdone = false; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci oproc->mbox = mbox_request_channel(client, 0); 6138c2ecf20Sopenharmony_ci if (IS_ERR(oproc->mbox)) { 6148c2ecf20Sopenharmony_ci ret = -EBUSY; 6158c2ecf20Sopenharmony_ci dev_err(dev, "mbox_request_channel failed: %ld\n", 6168c2ecf20Sopenharmony_ci PTR_ERR(oproc->mbox)); 6178c2ecf20Sopenharmony_ci return ret; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* 6218c2ecf20Sopenharmony_ci * Ping the remote processor. this is only for sanity-sake; 6228c2ecf20Sopenharmony_ci * there is no functional effect whatsoever. 6238c2ecf20Sopenharmony_ci * 6248c2ecf20Sopenharmony_ci * Note that the reply will _not_ arrive immediately: this message 6258c2ecf20Sopenharmony_ci * will wait in the mailbox fifo until the remote processor is booted. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); 6288c2ecf20Sopenharmony_ci if (ret < 0) { 6298c2ecf20Sopenharmony_ci dev_err(dev, "mbox_send_message failed: %d\n", ret); 6308c2ecf20Sopenharmony_ci goto put_mbox; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci ret = omap_rproc_enable_timers(rproc, true); 6348c2ecf20Sopenharmony_ci if (ret) { 6358c2ecf20Sopenharmony_ci dev_err(dev, "omap_rproc_enable_timers failed: %d\n", ret); 6368c2ecf20Sopenharmony_ci goto put_mbox; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci ret = reset_control_deassert(oproc->reset); 6408c2ecf20Sopenharmony_ci if (ret) { 6418c2ecf20Sopenharmony_ci dev_err(dev, "reset control deassert failed: %d\n", ret); 6428c2ecf20Sopenharmony_ci goto disable_timers; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* 6468c2ecf20Sopenharmony_ci * remote processor is up, so update the runtime pm status and 6478c2ecf20Sopenharmony_ci * enable the auto-suspend. The device usage count is incremented 6488c2ecf20Sopenharmony_ci * manually for balancing it for auto-suspend 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 6518c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(dev); 6528c2ecf20Sopenharmony_ci pm_runtime_get_noresume(dev); 6538c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 6548c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 6558c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return 0; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cidisable_timers: 6608c2ecf20Sopenharmony_ci omap_rproc_disable_timers(rproc, true); 6618c2ecf20Sopenharmony_ciput_mbox: 6628c2ecf20Sopenharmony_ci mbox_free_channel(oproc->mbox); 6638c2ecf20Sopenharmony_ci return ret; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* power off the remote processor */ 6678c2ecf20Sopenharmony_cistatic int omap_rproc_stop(struct rproc *rproc) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 6708c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 6718c2ecf20Sopenharmony_ci int ret; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* 6748c2ecf20Sopenharmony_ci * cancel any possible scheduled runtime suspend by incrementing 6758c2ecf20Sopenharmony_ci * the device usage count, and resuming the device. The remoteproc 6768c2ecf20Sopenharmony_ci * also needs to be woken up if suspended, to avoid the remoteproc 6778c2ecf20Sopenharmony_ci * OS to continue to remember any context that it has saved, and 6788c2ecf20Sopenharmony_ci * avoid potential issues in misindentifying a subsequent device 6798c2ecf20Sopenharmony_ci * reboot as a power restore boot 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 6828c2ecf20Sopenharmony_ci if (ret < 0) { 6838c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 6848c2ecf20Sopenharmony_ci return ret; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ret = reset_control_assert(oproc->reset); 6888c2ecf20Sopenharmony_ci if (ret) 6898c2ecf20Sopenharmony_ci goto out; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ret = omap_rproc_disable_timers(rproc, true); 6928c2ecf20Sopenharmony_ci if (ret) 6938c2ecf20Sopenharmony_ci goto enable_device; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci mbox_free_channel(oproc->mbox); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* 6988c2ecf20Sopenharmony_ci * update the runtime pm states and status now that the remoteproc 6998c2ecf20Sopenharmony_ci * has stopped 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 7028c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(dev); 7038c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 7048c2ecf20Sopenharmony_ci pm_runtime_set_suspended(dev); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cienable_device: 7098c2ecf20Sopenharmony_ci reset_control_deassert(oproc->reset); 7108c2ecf20Sopenharmony_ciout: 7118c2ecf20Sopenharmony_ci /* schedule the next auto-suspend */ 7128c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 7138c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev); 7148c2ecf20Sopenharmony_ci return ret; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/** 7188c2ecf20Sopenharmony_ci * omap_rproc_da_to_va() - internal memory translation helper 7198c2ecf20Sopenharmony_ci * @rproc: remote processor to apply the address translation for 7208c2ecf20Sopenharmony_ci * @da: device address to translate 7218c2ecf20Sopenharmony_ci * @len: length of the memory buffer 7228c2ecf20Sopenharmony_ci * 7238c2ecf20Sopenharmony_ci * Custom function implementing the rproc .da_to_va ops to provide address 7248c2ecf20Sopenharmony_ci * translation (device address to kernel virtual address) for internal RAMs 7258c2ecf20Sopenharmony_ci * present in a DSP or IPU device). The translated addresses can be used 7268c2ecf20Sopenharmony_ci * either by the remoteproc core for loading, or by any rpmsg bus drivers. 7278c2ecf20Sopenharmony_ci * 7288c2ecf20Sopenharmony_ci * Return: translated virtual address in kernel memory space on success, 7298c2ecf20Sopenharmony_ci * or NULL on failure. 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_cistatic void *omap_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 7348c2ecf20Sopenharmony_ci int i; 7358c2ecf20Sopenharmony_ci u32 offset; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (len <= 0) 7388c2ecf20Sopenharmony_ci return NULL; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!oproc->num_mems) 7418c2ecf20Sopenharmony_ci return NULL; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci for (i = 0; i < oproc->num_mems; i++) { 7448c2ecf20Sopenharmony_ci if (da >= oproc->mem[i].dev_addr && da + len <= 7458c2ecf20Sopenharmony_ci oproc->mem[i].dev_addr + oproc->mem[i].size) { 7468c2ecf20Sopenharmony_ci offset = da - oproc->mem[i].dev_addr; 7478c2ecf20Sopenharmony_ci /* __force to make sparse happy with type conversion */ 7488c2ecf20Sopenharmony_ci return (__force void *)(oproc->mem[i].cpu_addr + 7498c2ecf20Sopenharmony_ci offset); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return NULL; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic const struct rproc_ops omap_rproc_ops = { 7578c2ecf20Sopenharmony_ci .start = omap_rproc_start, 7588c2ecf20Sopenharmony_ci .stop = omap_rproc_stop, 7598c2ecf20Sopenharmony_ci .kick = omap_rproc_kick, 7608c2ecf20Sopenharmony_ci .da_to_va = omap_rproc_da_to_va, 7618c2ecf20Sopenharmony_ci}; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 7648c2ecf20Sopenharmony_cistatic bool _is_rproc_in_standby(struct omap_rproc *oproc) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci return ti_clk_is_in_standby(oproc->fck); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/* 1 sec is long enough time to let the remoteproc side suspend the device */ 7708c2ecf20Sopenharmony_ci#define DEF_SUSPEND_TIMEOUT 1000 7718c2ecf20Sopenharmony_cistatic int _omap_rproc_suspend(struct rproc *rproc, bool auto_suspend) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 7748c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 7758c2ecf20Sopenharmony_ci unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT); 7768c2ecf20Sopenharmony_ci unsigned long ta = jiffies + to; 7778c2ecf20Sopenharmony_ci u32 suspend_msg = auto_suspend ? 7788c2ecf20Sopenharmony_ci RP_MBOX_SUSPEND_AUTO : RP_MBOX_SUSPEND_SYSTEM; 7798c2ecf20Sopenharmony_ci int ret; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci reinit_completion(&oproc->pm_comp); 7828c2ecf20Sopenharmony_ci oproc->suspend_acked = false; 7838c2ecf20Sopenharmony_ci ret = mbox_send_message(oproc->mbox, (void *)suspend_msg); 7848c2ecf20Sopenharmony_ci if (ret < 0) { 7858c2ecf20Sopenharmony_ci dev_err(dev, "PM mbox_send_message failed: %d\n", ret); 7868c2ecf20Sopenharmony_ci return ret; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&oproc->pm_comp, to); 7908c2ecf20Sopenharmony_ci if (!oproc->suspend_acked) 7918c2ecf20Sopenharmony_ci return -EBUSY; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* 7948c2ecf20Sopenharmony_ci * The remoteproc side is returning the ACK message before saving the 7958c2ecf20Sopenharmony_ci * context, because the context saving is performed within a SYS/BIOS 7968c2ecf20Sopenharmony_ci * function, and it cannot have any inter-dependencies against the IPC 7978c2ecf20Sopenharmony_ci * layer. Also, as the SYS/BIOS needs to preserve properly the processor 7988c2ecf20Sopenharmony_ci * register set, sending this ACK or signalling the completion of the 7998c2ecf20Sopenharmony_ci * context save through a shared memory variable can never be the 8008c2ecf20Sopenharmony_ci * absolute last thing to be executed on the remoteproc side, and the 8018c2ecf20Sopenharmony_ci * MPU cannot use the ACK message as a sync point to put the remoteproc 8028c2ecf20Sopenharmony_ci * into reset. The only way to ensure that the remote processor has 8038c2ecf20Sopenharmony_ci * completed saving the context is to check that the module has reached 8048c2ecf20Sopenharmony_ci * STANDBY state (after saving the context, the SYS/BIOS executes the 8058c2ecf20Sopenharmony_ci * appropriate target-specific WFI instruction causing the module to 8068c2ecf20Sopenharmony_ci * enter STANDBY). 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_ci while (!_is_rproc_in_standby(oproc)) { 8098c2ecf20Sopenharmony_ci if (time_after(jiffies, ta)) 8108c2ecf20Sopenharmony_ci return -ETIME; 8118c2ecf20Sopenharmony_ci schedule(); 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci ret = reset_control_assert(oproc->reset); 8158c2ecf20Sopenharmony_ci if (ret) { 8168c2ecf20Sopenharmony_ci dev_err(dev, "reset assert during suspend failed %d\n", ret); 8178c2ecf20Sopenharmony_ci return ret; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ret = omap_rproc_disable_timers(rproc, false); 8218c2ecf20Sopenharmony_ci if (ret) { 8228c2ecf20Sopenharmony_ci dev_err(dev, "disabling timers during suspend failed %d\n", 8238c2ecf20Sopenharmony_ci ret); 8248c2ecf20Sopenharmony_ci goto enable_device; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci /* 8288c2ecf20Sopenharmony_ci * IOMMUs would have to be disabled specifically for runtime suspend. 8298c2ecf20Sopenharmony_ci * They are handled automatically through System PM callbacks for 8308c2ecf20Sopenharmony_ci * regular system suspend 8318c2ecf20Sopenharmony_ci */ 8328c2ecf20Sopenharmony_ci if (auto_suspend) { 8338c2ecf20Sopenharmony_ci ret = omap_iommu_domain_deactivate(rproc->domain); 8348c2ecf20Sopenharmony_ci if (ret) { 8358c2ecf20Sopenharmony_ci dev_err(dev, "iommu domain deactivate failed %d\n", 8368c2ecf20Sopenharmony_ci ret); 8378c2ecf20Sopenharmony_ci goto enable_timers; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci return 0; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cienable_timers: 8448c2ecf20Sopenharmony_ci /* ignore errors on re-enabling code */ 8458c2ecf20Sopenharmony_ci omap_rproc_enable_timers(rproc, false); 8468c2ecf20Sopenharmony_cienable_device: 8478c2ecf20Sopenharmony_ci reset_control_deassert(oproc->reset); 8488c2ecf20Sopenharmony_ci return ret; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic int _omap_rproc_resume(struct rproc *rproc, bool auto_suspend) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 8548c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 8558c2ecf20Sopenharmony_ci int ret; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* 8588c2ecf20Sopenharmony_ci * IOMMUs would have to be enabled specifically for runtime resume. 8598c2ecf20Sopenharmony_ci * They would have been already enabled automatically through System 8608c2ecf20Sopenharmony_ci * PM callbacks for regular system resume 8618c2ecf20Sopenharmony_ci */ 8628c2ecf20Sopenharmony_ci if (auto_suspend) { 8638c2ecf20Sopenharmony_ci ret = omap_iommu_domain_activate(rproc->domain); 8648c2ecf20Sopenharmony_ci if (ret) { 8658c2ecf20Sopenharmony_ci dev_err(dev, "omap_iommu activate failed %d\n", ret); 8668c2ecf20Sopenharmony_ci goto out; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* boot address could be lost after suspend, so restore it */ 8718c2ecf20Sopenharmony_ci if (oproc->boot_data) { 8728c2ecf20Sopenharmony_ci ret = omap_rproc_write_dsp_boot_addr(rproc); 8738c2ecf20Sopenharmony_ci if (ret) { 8748c2ecf20Sopenharmony_ci dev_err(dev, "boot address restore failed %d\n", ret); 8758c2ecf20Sopenharmony_ci goto suspend_iommu; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci ret = omap_rproc_enable_timers(rproc, false); 8808c2ecf20Sopenharmony_ci if (ret) { 8818c2ecf20Sopenharmony_ci dev_err(dev, "enabling timers during resume failed %d\n", ret); 8828c2ecf20Sopenharmony_ci goto suspend_iommu; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci ret = reset_control_deassert(oproc->reset); 8868c2ecf20Sopenharmony_ci if (ret) { 8878c2ecf20Sopenharmony_ci dev_err(dev, "reset deassert during resume failed %d\n", ret); 8888c2ecf20Sopenharmony_ci goto disable_timers; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return 0; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cidisable_timers: 8948c2ecf20Sopenharmony_ci omap_rproc_disable_timers(rproc, false); 8958c2ecf20Sopenharmony_cisuspend_iommu: 8968c2ecf20Sopenharmony_ci if (auto_suspend) 8978c2ecf20Sopenharmony_ci omap_iommu_domain_deactivate(rproc->domain); 8988c2ecf20Sopenharmony_ciout: 8998c2ecf20Sopenharmony_ci return ret; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int __maybe_unused omap_rproc_suspend(struct device *dev) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 9058c2ecf20Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 9068c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 9078c2ecf20Sopenharmony_ci int ret = 0; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci mutex_lock(&rproc->lock); 9108c2ecf20Sopenharmony_ci if (rproc->state == RPROC_OFFLINE) 9118c2ecf20Sopenharmony_ci goto out; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (rproc->state == RPROC_SUSPENDED) 9148c2ecf20Sopenharmony_ci goto out; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (rproc->state != RPROC_RUNNING) { 9178c2ecf20Sopenharmony_ci ret = -EBUSY; 9188c2ecf20Sopenharmony_ci goto out; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci ret = _omap_rproc_suspend(rproc, false); 9228c2ecf20Sopenharmony_ci if (ret) { 9238c2ecf20Sopenharmony_ci dev_err(dev, "suspend failed %d\n", ret); 9248c2ecf20Sopenharmony_ci goto out; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * remoteproc is running at the time of system suspend, so remember 9298c2ecf20Sopenharmony_ci * it so as to wake it up during system resume 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci oproc->need_resume = true; 9328c2ecf20Sopenharmony_ci rproc->state = RPROC_SUSPENDED; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ciout: 9358c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 9368c2ecf20Sopenharmony_ci return ret; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic int __maybe_unused omap_rproc_resume(struct device *dev) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 9428c2ecf20Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 9438c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 9448c2ecf20Sopenharmony_ci int ret = 0; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci mutex_lock(&rproc->lock); 9478c2ecf20Sopenharmony_ci if (rproc->state == RPROC_OFFLINE) 9488c2ecf20Sopenharmony_ci goto out; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (rproc->state != RPROC_SUSPENDED) { 9518c2ecf20Sopenharmony_ci ret = -EBUSY; 9528c2ecf20Sopenharmony_ci goto out; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * remoteproc was auto-suspended at the time of system suspend, 9578c2ecf20Sopenharmony_ci * so no need to wake-up the processor (leave it in suspended 9588c2ecf20Sopenharmony_ci * state, will be woken up during a subsequent runtime_resume) 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci if (!oproc->need_resume) 9618c2ecf20Sopenharmony_ci goto out; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci ret = _omap_rproc_resume(rproc, false); 9648c2ecf20Sopenharmony_ci if (ret) { 9658c2ecf20Sopenharmony_ci dev_err(dev, "resume failed %d\n", ret); 9668c2ecf20Sopenharmony_ci goto out; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci oproc->need_resume = false; 9708c2ecf20Sopenharmony_ci rproc->state = RPROC_RUNNING; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 9738c2ecf20Sopenharmony_ciout: 9748c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 9758c2ecf20Sopenharmony_ci return ret; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic int omap_rproc_runtime_suspend(struct device *dev) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct rproc *rproc = dev_get_drvdata(dev); 9818c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 9828c2ecf20Sopenharmony_ci int ret; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci mutex_lock(&rproc->lock); 9858c2ecf20Sopenharmony_ci if (rproc->state == RPROC_CRASHED) { 9868c2ecf20Sopenharmony_ci dev_dbg(dev, "rproc cannot be runtime suspended when crashed!\n"); 9878c2ecf20Sopenharmony_ci ret = -EBUSY; 9888c2ecf20Sopenharmony_ci goto out; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci if (WARN_ON(rproc->state != RPROC_RUNNING)) { 9928c2ecf20Sopenharmony_ci dev_err(dev, "rproc cannot be runtime suspended when not running!\n"); 9938c2ecf20Sopenharmony_ci ret = -EBUSY; 9948c2ecf20Sopenharmony_ci goto out; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* 9988c2ecf20Sopenharmony_ci * do not even attempt suspend if the remote processor is not 9998c2ecf20Sopenharmony_ci * idled for runtime auto-suspend 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_ci if (!_is_rproc_in_standby(oproc)) { 10028c2ecf20Sopenharmony_ci ret = -EBUSY; 10038c2ecf20Sopenharmony_ci goto abort; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci ret = _omap_rproc_suspend(rproc, true); 10078c2ecf20Sopenharmony_ci if (ret) 10088c2ecf20Sopenharmony_ci goto abort; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci rproc->state = RPROC_SUSPENDED; 10118c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 10128c2ecf20Sopenharmony_ci return 0; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ciabort: 10158c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 10168c2ecf20Sopenharmony_ciout: 10178c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 10188c2ecf20Sopenharmony_ci return ret; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic int omap_rproc_runtime_resume(struct device *dev) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct rproc *rproc = dev_get_drvdata(dev); 10248c2ecf20Sopenharmony_ci int ret; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci mutex_lock(&rproc->lock); 10278c2ecf20Sopenharmony_ci if (WARN_ON(rproc->state != RPROC_SUSPENDED)) { 10288c2ecf20Sopenharmony_ci dev_err(dev, "rproc cannot be runtime resumed if not suspended! state=%d\n", 10298c2ecf20Sopenharmony_ci rproc->state); 10308c2ecf20Sopenharmony_ci ret = -EBUSY; 10318c2ecf20Sopenharmony_ci goto out; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci ret = _omap_rproc_resume(rproc, true); 10358c2ecf20Sopenharmony_ci if (ret) { 10368c2ecf20Sopenharmony_ci dev_err(dev, "runtime resume failed %d\n", ret); 10378c2ecf20Sopenharmony_ci goto out; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci rproc->state = RPROC_RUNNING; 10418c2ecf20Sopenharmony_ciout: 10428c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 10438c2ecf20Sopenharmony_ci return ret; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic const struct omap_rproc_mem_data ipu_mems[] = { 10488c2ecf20Sopenharmony_ci { .name = "l2ram", .dev_addr = 0x20000000 }, 10498c2ecf20Sopenharmony_ci { }, 10508c2ecf20Sopenharmony_ci}; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic const struct omap_rproc_mem_data dra7_dsp_mems[] = { 10538c2ecf20Sopenharmony_ci { .name = "l2ram", .dev_addr = 0x800000 }, 10548c2ecf20Sopenharmony_ci { .name = "l1pram", .dev_addr = 0xe00000 }, 10558c2ecf20Sopenharmony_ci { .name = "l1dram", .dev_addr = 0xf00000 }, 10568c2ecf20Sopenharmony_ci { }, 10578c2ecf20Sopenharmony_ci}; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic const struct omap_rproc_dev_data omap4_dsp_dev_data = { 10608c2ecf20Sopenharmony_ci .device_name = "dsp", 10618c2ecf20Sopenharmony_ci}; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic const struct omap_rproc_dev_data omap4_ipu_dev_data = { 10648c2ecf20Sopenharmony_ci .device_name = "ipu", 10658c2ecf20Sopenharmony_ci .mems = ipu_mems, 10668c2ecf20Sopenharmony_ci}; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic const struct omap_rproc_dev_data omap5_dsp_dev_data = { 10698c2ecf20Sopenharmony_ci .device_name = "dsp", 10708c2ecf20Sopenharmony_ci}; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic const struct omap_rproc_dev_data omap5_ipu_dev_data = { 10738c2ecf20Sopenharmony_ci .device_name = "ipu", 10748c2ecf20Sopenharmony_ci .mems = ipu_mems, 10758c2ecf20Sopenharmony_ci}; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic const struct omap_rproc_dev_data dra7_dsp_dev_data = { 10788c2ecf20Sopenharmony_ci .device_name = "dsp", 10798c2ecf20Sopenharmony_ci .mems = dra7_dsp_mems, 10808c2ecf20Sopenharmony_ci}; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic const struct omap_rproc_dev_data dra7_ipu_dev_data = { 10838c2ecf20Sopenharmony_ci .device_name = "ipu", 10848c2ecf20Sopenharmony_ci .mems = ipu_mems, 10858c2ecf20Sopenharmony_ci}; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic const struct of_device_id omap_rproc_of_match[] = { 10888c2ecf20Sopenharmony_ci { 10898c2ecf20Sopenharmony_ci .compatible = "ti,omap4-dsp", 10908c2ecf20Sopenharmony_ci .data = &omap4_dsp_dev_data, 10918c2ecf20Sopenharmony_ci }, 10928c2ecf20Sopenharmony_ci { 10938c2ecf20Sopenharmony_ci .compatible = "ti,omap4-ipu", 10948c2ecf20Sopenharmony_ci .data = &omap4_ipu_dev_data, 10958c2ecf20Sopenharmony_ci }, 10968c2ecf20Sopenharmony_ci { 10978c2ecf20Sopenharmony_ci .compatible = "ti,omap5-dsp", 10988c2ecf20Sopenharmony_ci .data = &omap5_dsp_dev_data, 10998c2ecf20Sopenharmony_ci }, 11008c2ecf20Sopenharmony_ci { 11018c2ecf20Sopenharmony_ci .compatible = "ti,omap5-ipu", 11028c2ecf20Sopenharmony_ci .data = &omap5_ipu_dev_data, 11038c2ecf20Sopenharmony_ci }, 11048c2ecf20Sopenharmony_ci { 11058c2ecf20Sopenharmony_ci .compatible = "ti,dra7-dsp", 11068c2ecf20Sopenharmony_ci .data = &dra7_dsp_dev_data, 11078c2ecf20Sopenharmony_ci }, 11088c2ecf20Sopenharmony_ci { 11098c2ecf20Sopenharmony_ci .compatible = "ti,dra7-ipu", 11108c2ecf20Sopenharmony_ci .data = &dra7_ipu_dev_data, 11118c2ecf20Sopenharmony_ci }, 11128c2ecf20Sopenharmony_ci { 11138c2ecf20Sopenharmony_ci /* end */ 11148c2ecf20Sopenharmony_ci }, 11158c2ecf20Sopenharmony_ci}; 11168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_rproc_of_match); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_cistatic const char *omap_rproc_get_firmware(struct platform_device *pdev) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci const char *fw_name; 11218c2ecf20Sopenharmony_ci int ret; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci ret = of_property_read_string(pdev->dev.of_node, "firmware-name", 11248c2ecf20Sopenharmony_ci &fw_name); 11258c2ecf20Sopenharmony_ci if (ret) 11268c2ecf20Sopenharmony_ci return ERR_PTR(ret); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci return fw_name; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic int omap_rproc_get_boot_data(struct platform_device *pdev, 11328c2ecf20Sopenharmony_ci struct rproc *rproc) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 11358c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 11368c2ecf20Sopenharmony_ci const struct omap_rproc_dev_data *data; 11378c2ecf20Sopenharmony_ci int ret; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci data = of_device_get_match_data(&pdev->dev); 11408c2ecf20Sopenharmony_ci if (!data) 11418c2ecf20Sopenharmony_ci return -ENODEV; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (!of_property_read_bool(np, "ti,bootreg")) 11448c2ecf20Sopenharmony_ci return 0; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci oproc->boot_data = devm_kzalloc(&pdev->dev, sizeof(*oproc->boot_data), 11478c2ecf20Sopenharmony_ci GFP_KERNEL); 11488c2ecf20Sopenharmony_ci if (!oproc->boot_data) 11498c2ecf20Sopenharmony_ci return -ENOMEM; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci oproc->boot_data->syscon = 11528c2ecf20Sopenharmony_ci syscon_regmap_lookup_by_phandle(np, "ti,bootreg"); 11538c2ecf20Sopenharmony_ci if (IS_ERR(oproc->boot_data->syscon)) { 11548c2ecf20Sopenharmony_ci ret = PTR_ERR(oproc->boot_data->syscon); 11558c2ecf20Sopenharmony_ci return ret; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (of_property_read_u32_index(np, "ti,bootreg", 1, 11598c2ecf20Sopenharmony_ci &oproc->boot_data->boot_reg)) { 11608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't get the boot register\n"); 11618c2ecf20Sopenharmony_ci return -EINVAL; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci of_property_read_u32_index(np, "ti,bootreg", 2, 11658c2ecf20Sopenharmony_ci &oproc->boot_data->boot_reg_shift); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci return 0; 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cistatic int omap_rproc_of_get_internal_memories(struct platform_device *pdev, 11718c2ecf20Sopenharmony_ci struct rproc *rproc) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 11748c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 11758c2ecf20Sopenharmony_ci const struct omap_rproc_dev_data *data; 11768c2ecf20Sopenharmony_ci struct resource *res; 11778c2ecf20Sopenharmony_ci int num_mems; 11788c2ecf20Sopenharmony_ci int i; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci data = of_device_get_match_data(dev); 11818c2ecf20Sopenharmony_ci if (!data) 11828c2ecf20Sopenharmony_ci return -ENODEV; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (!data->mems) 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci num_mems = of_property_count_elems_of_size(dev->of_node, "reg", 11888c2ecf20Sopenharmony_ci sizeof(u32)) / 2; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci oproc->mem = devm_kcalloc(dev, num_mems, sizeof(*oproc->mem), 11918c2ecf20Sopenharmony_ci GFP_KERNEL); 11928c2ecf20Sopenharmony_ci if (!oproc->mem) 11938c2ecf20Sopenharmony_ci return -ENOMEM; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci for (i = 0; data->mems[i].name; i++) { 11968c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 11978c2ecf20Sopenharmony_ci data->mems[i].name); 11988c2ecf20Sopenharmony_ci if (!res) { 11998c2ecf20Sopenharmony_ci dev_err(dev, "no memory defined for %s\n", 12008c2ecf20Sopenharmony_ci data->mems[i].name); 12018c2ecf20Sopenharmony_ci return -ENOMEM; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci oproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res); 12048c2ecf20Sopenharmony_ci if (IS_ERR(oproc->mem[i].cpu_addr)) { 12058c2ecf20Sopenharmony_ci dev_err(dev, "failed to parse and map %s memory\n", 12068c2ecf20Sopenharmony_ci data->mems[i].name); 12078c2ecf20Sopenharmony_ci return PTR_ERR(oproc->mem[i].cpu_addr); 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci oproc->mem[i].bus_addr = res->start; 12108c2ecf20Sopenharmony_ci oproc->mem[i].dev_addr = data->mems[i].dev_addr; 12118c2ecf20Sopenharmony_ci oproc->mem[i].size = resource_size(res); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci dev_dbg(dev, "memory %8s: bus addr %pa size 0x%x va %pK da 0x%x\n", 12148c2ecf20Sopenharmony_ci data->mems[i].name, &oproc->mem[i].bus_addr, 12158c2ecf20Sopenharmony_ci oproc->mem[i].size, oproc->mem[i].cpu_addr, 12168c2ecf20Sopenharmony_ci oproc->mem[i].dev_addr); 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci oproc->num_mems = num_mems; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci return 0; 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP_REMOTEPROC_WATCHDOG 12248c2ecf20Sopenharmony_cistatic int omap_rproc_count_wdog_timers(struct device *dev) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 12278c2ecf20Sopenharmony_ci int ret; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci ret = of_count_phandle_with_args(np, "ti,watchdog-timers", NULL); 12308c2ecf20Sopenharmony_ci if (ret <= 0) { 12318c2ecf20Sopenharmony_ci dev_dbg(dev, "device does not have watchdog timers, status = %d\n", 12328c2ecf20Sopenharmony_ci ret); 12338c2ecf20Sopenharmony_ci ret = 0; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci return ret; 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci#else 12398c2ecf20Sopenharmony_cistatic int omap_rproc_count_wdog_timers(struct device *dev) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci return 0; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci#endif 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic int omap_rproc_of_get_timers(struct platform_device *pdev, 12468c2ecf20Sopenharmony_ci struct rproc *rproc) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 12498c2ecf20Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 12508c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 12518c2ecf20Sopenharmony_ci int num_timers; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci /* 12548c2ecf20Sopenharmony_ci * Timer nodes are directly used in client nodes as phandles, so 12558c2ecf20Sopenharmony_ci * retrieve the count using appropriate size 12568c2ecf20Sopenharmony_ci */ 12578c2ecf20Sopenharmony_ci oproc->num_timers = of_count_phandle_with_args(np, "ti,timers", NULL); 12588c2ecf20Sopenharmony_ci if (oproc->num_timers <= 0) { 12598c2ecf20Sopenharmony_ci dev_dbg(dev, "device does not have timers, status = %d\n", 12608c2ecf20Sopenharmony_ci oproc->num_timers); 12618c2ecf20Sopenharmony_ci oproc->num_timers = 0; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci oproc->num_wd_timers = omap_rproc_count_wdog_timers(dev); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci num_timers = oproc->num_timers + oproc->num_wd_timers; 12678c2ecf20Sopenharmony_ci if (num_timers) { 12688c2ecf20Sopenharmony_ci oproc->timers = devm_kcalloc(dev, num_timers, 12698c2ecf20Sopenharmony_ci sizeof(*oproc->timers), 12708c2ecf20Sopenharmony_ci GFP_KERNEL); 12718c2ecf20Sopenharmony_ci if (!oproc->timers) 12728c2ecf20Sopenharmony_ci return -ENOMEM; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci dev_dbg(dev, "device has %d tick timers and %d watchdog timers\n", 12758c2ecf20Sopenharmony_ci oproc->num_timers, oproc->num_wd_timers); 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci return 0; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic int omap_rproc_probe(struct platform_device *pdev) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 12848c2ecf20Sopenharmony_ci struct omap_rproc *oproc; 12858c2ecf20Sopenharmony_ci struct rproc *rproc; 12868c2ecf20Sopenharmony_ci const char *firmware; 12878c2ecf20Sopenharmony_ci int ret; 12888c2ecf20Sopenharmony_ci struct reset_control *reset; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci if (!np) { 12918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "only DT-based devices are supported\n"); 12928c2ecf20Sopenharmony_ci return -ENODEV; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci reset = devm_reset_control_array_get_exclusive(&pdev->dev); 12968c2ecf20Sopenharmony_ci if (IS_ERR(reset)) 12978c2ecf20Sopenharmony_ci return PTR_ERR(reset); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci firmware = omap_rproc_get_firmware(pdev); 13008c2ecf20Sopenharmony_ci if (IS_ERR(firmware)) 13018c2ecf20Sopenharmony_ci return PTR_ERR(firmware); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 13048c2ecf20Sopenharmony_ci if (ret) { 13058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret); 13068c2ecf20Sopenharmony_ci return ret; 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops, 13108c2ecf20Sopenharmony_ci firmware, sizeof(*oproc)); 13118c2ecf20Sopenharmony_ci if (!rproc) 13128c2ecf20Sopenharmony_ci return -ENOMEM; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci oproc = rproc->priv; 13158c2ecf20Sopenharmony_ci oproc->rproc = rproc; 13168c2ecf20Sopenharmony_ci oproc->reset = reset; 13178c2ecf20Sopenharmony_ci /* All existing OMAP IPU and DSP processors have an MMU */ 13188c2ecf20Sopenharmony_ci rproc->has_iommu = true; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci ret = omap_rproc_of_get_internal_memories(pdev, rproc); 13218c2ecf20Sopenharmony_ci if (ret) 13228c2ecf20Sopenharmony_ci goto free_rproc; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci ret = omap_rproc_get_boot_data(pdev, rproc); 13258c2ecf20Sopenharmony_ci if (ret) 13268c2ecf20Sopenharmony_ci goto free_rproc; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci ret = omap_rproc_of_get_timers(pdev, rproc); 13298c2ecf20Sopenharmony_ci if (ret) 13308c2ecf20Sopenharmony_ci goto free_rproc; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci init_completion(&oproc->pm_comp); 13338c2ecf20Sopenharmony_ci oproc->autosuspend_delay = DEFAULT_AUTOSUSPEND_DELAY; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, "ti,autosuspend-delay-ms", 13368c2ecf20Sopenharmony_ci &oproc->autosuspend_delay); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, oproc->autosuspend_delay); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci oproc->fck = devm_clk_get(&pdev->dev, 0); 13418c2ecf20Sopenharmony_ci if (IS_ERR(oproc->fck)) { 13428c2ecf20Sopenharmony_ci ret = PTR_ERR(oproc->fck); 13438c2ecf20Sopenharmony_ci goto free_rproc; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci ret = of_reserved_mem_device_init(&pdev->dev); 13478c2ecf20Sopenharmony_ci if (ret) { 13488c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "device does not have specific CMA pool.\n"); 13498c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Typically this should be provided,\n"); 13508c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "only omit if you know what you are doing.\n"); 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rproc); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci ret = rproc_add(rproc); 13568c2ecf20Sopenharmony_ci if (ret) 13578c2ecf20Sopenharmony_ci goto release_mem; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci return 0; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cirelease_mem: 13628c2ecf20Sopenharmony_ci of_reserved_mem_device_release(&pdev->dev); 13638c2ecf20Sopenharmony_cifree_rproc: 13648c2ecf20Sopenharmony_ci rproc_free(rproc); 13658c2ecf20Sopenharmony_ci return ret; 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic int omap_rproc_remove(struct platform_device *pdev) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci rproc_del(rproc); 13738c2ecf20Sopenharmony_ci rproc_free(rproc); 13748c2ecf20Sopenharmony_ci of_reserved_mem_device_release(&pdev->dev); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci return 0; 13778c2ecf20Sopenharmony_ci} 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_cistatic const struct dev_pm_ops omap_rproc_pm_ops = { 13808c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume) 13818c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(omap_rproc_runtime_suspend, 13828c2ecf20Sopenharmony_ci omap_rproc_runtime_resume, NULL) 13838c2ecf20Sopenharmony_ci}; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cistatic struct platform_driver omap_rproc_driver = { 13868c2ecf20Sopenharmony_ci .probe = omap_rproc_probe, 13878c2ecf20Sopenharmony_ci .remove = omap_rproc_remove, 13888c2ecf20Sopenharmony_ci .driver = { 13898c2ecf20Sopenharmony_ci .name = "omap-rproc", 13908c2ecf20Sopenharmony_ci .pm = &omap_rproc_pm_ops, 13918c2ecf20Sopenharmony_ci .of_match_table = omap_rproc_of_match, 13928c2ecf20Sopenharmony_ci }, 13938c2ecf20Sopenharmony_ci}; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_cimodule_platform_driver(omap_rproc_driver); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 13988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMAP Remote Processor control driver"); 1399