162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP Remote Processor driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011-2020 Texas Instruments Incorporated - http://www.ti.com/ 662306a36Sopenharmony_ci * Copyright (C) 2011 Google, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com> 962306a36Sopenharmony_ci * Brian Swetland <swetland@google.com> 1062306a36Sopenharmony_ci * Fernando Guzman Lugo <fernando.lugo@ti.com> 1162306a36Sopenharmony_ci * Mark Grosen <mgrosen@ti.com> 1262306a36Sopenharmony_ci * Suman Anna <s-anna@ti.com> 1362306a36Sopenharmony_ci * Hari Kanigeri <h-kanigeri2@ti.com> 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/clk/ti.h> 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <linux/of.h> 2362306a36Sopenharmony_ci#include <linux/of_platform.h> 2462306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2862306a36Sopenharmony_ci#include <linux/interrupt.h> 2962306a36Sopenharmony_ci#include <linux/remoteproc.h> 3062306a36Sopenharmony_ci#include <linux/mailbox_client.h> 3162306a36Sopenharmony_ci#include <linux/omap-iommu.h> 3262306a36Sopenharmony_ci#include <linux/omap-mailbox.h> 3362306a36Sopenharmony_ci#include <linux/regmap.h> 3462306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 3562306a36Sopenharmony_ci#include <linux/reset.h> 3662306a36Sopenharmony_ci#include <clocksource/timer-ti-dm.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <linux/platform_data/dmtimer-omap.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include "omap_remoteproc.h" 4162306a36Sopenharmony_ci#include "remoteproc_internal.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* default auto-suspend delay (ms) */ 4462306a36Sopenharmony_ci#define DEFAULT_AUTOSUSPEND_DELAY 10000 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/** 4762306a36Sopenharmony_ci * struct omap_rproc_boot_data - boot data structure for the DSP omap rprocs 4862306a36Sopenharmony_ci * @syscon: regmap handle for the system control configuration module 4962306a36Sopenharmony_ci * @boot_reg: boot register offset within the @syscon regmap 5062306a36Sopenharmony_ci * @boot_reg_shift: bit-field shift required for the boot address value in 5162306a36Sopenharmony_ci * @boot_reg 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistruct omap_rproc_boot_data { 5462306a36Sopenharmony_ci struct regmap *syscon; 5562306a36Sopenharmony_ci unsigned int boot_reg; 5662306a36Sopenharmony_ci unsigned int boot_reg_shift; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/** 6062306a36Sopenharmony_ci * struct omap_rproc_mem - internal memory structure 6162306a36Sopenharmony_ci * @cpu_addr: MPU virtual address of the memory region 6262306a36Sopenharmony_ci * @bus_addr: bus address used to access the memory region 6362306a36Sopenharmony_ci * @dev_addr: device address of the memory region from DSP view 6462306a36Sopenharmony_ci * @size: size of the memory region 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistruct omap_rproc_mem { 6762306a36Sopenharmony_ci void __iomem *cpu_addr; 6862306a36Sopenharmony_ci phys_addr_t bus_addr; 6962306a36Sopenharmony_ci u32 dev_addr; 7062306a36Sopenharmony_ci size_t size; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/** 7462306a36Sopenharmony_ci * struct omap_rproc_timer - data structure for a timer used by a omap rproc 7562306a36Sopenharmony_ci * @odt: timer pointer 7662306a36Sopenharmony_ci * @timer_ops: OMAP dmtimer ops for @odt timer 7762306a36Sopenharmony_ci * @irq: timer irq 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistruct omap_rproc_timer { 8062306a36Sopenharmony_ci struct omap_dm_timer *odt; 8162306a36Sopenharmony_ci const struct omap_dm_timer_ops *timer_ops; 8262306a36Sopenharmony_ci int irq; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/** 8662306a36Sopenharmony_ci * struct omap_rproc - omap remote processor state 8762306a36Sopenharmony_ci * @mbox: mailbox channel handle 8862306a36Sopenharmony_ci * @client: mailbox client to request the mailbox channel 8962306a36Sopenharmony_ci * @boot_data: boot data structure for setting processor boot address 9062306a36Sopenharmony_ci * @mem: internal memory regions data 9162306a36Sopenharmony_ci * @num_mems: number of internal memory regions 9262306a36Sopenharmony_ci * @num_timers: number of rproc timer(s) 9362306a36Sopenharmony_ci * @num_wd_timers: number of rproc watchdog timers 9462306a36Sopenharmony_ci * @timers: timer(s) info used by rproc 9562306a36Sopenharmony_ci * @autosuspend_delay: auto-suspend delay value to be used for runtime pm 9662306a36Sopenharmony_ci * @need_resume: if true a resume is needed in the system resume callback 9762306a36Sopenharmony_ci * @rproc: rproc handle 9862306a36Sopenharmony_ci * @reset: reset handle 9962306a36Sopenharmony_ci * @pm_comp: completion primitive to sync for suspend response 10062306a36Sopenharmony_ci * @fck: functional clock for the remoteproc 10162306a36Sopenharmony_ci * @suspend_acked: state machine flag to store the suspend request ack 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistruct omap_rproc { 10462306a36Sopenharmony_ci struct mbox_chan *mbox; 10562306a36Sopenharmony_ci struct mbox_client client; 10662306a36Sopenharmony_ci struct omap_rproc_boot_data *boot_data; 10762306a36Sopenharmony_ci struct omap_rproc_mem *mem; 10862306a36Sopenharmony_ci int num_mems; 10962306a36Sopenharmony_ci int num_timers; 11062306a36Sopenharmony_ci int num_wd_timers; 11162306a36Sopenharmony_ci struct omap_rproc_timer *timers; 11262306a36Sopenharmony_ci int autosuspend_delay; 11362306a36Sopenharmony_ci bool need_resume; 11462306a36Sopenharmony_ci struct rproc *rproc; 11562306a36Sopenharmony_ci struct reset_control *reset; 11662306a36Sopenharmony_ci struct completion pm_comp; 11762306a36Sopenharmony_ci struct clk *fck; 11862306a36Sopenharmony_ci bool suspend_acked; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/** 12262306a36Sopenharmony_ci * struct omap_rproc_mem_data - memory definitions for an omap remote processor 12362306a36Sopenharmony_ci * @name: name for this memory entry 12462306a36Sopenharmony_ci * @dev_addr: device address for the memory entry 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_cistruct omap_rproc_mem_data { 12762306a36Sopenharmony_ci const char *name; 12862306a36Sopenharmony_ci const u32 dev_addr; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/** 13262306a36Sopenharmony_ci * struct omap_rproc_dev_data - device data for the omap remote processor 13362306a36Sopenharmony_ci * @device_name: device name of the remote processor 13462306a36Sopenharmony_ci * @mems: memory definitions for this remote processor 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistruct omap_rproc_dev_data { 13762306a36Sopenharmony_ci const char *device_name; 13862306a36Sopenharmony_ci const struct omap_rproc_mem_data *mems; 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/** 14262306a36Sopenharmony_ci * omap_rproc_request_timer() - request a timer for a remoteproc 14362306a36Sopenharmony_ci * @dev: device requesting the timer 14462306a36Sopenharmony_ci * @np: device node pointer to the desired timer 14562306a36Sopenharmony_ci * @timer: handle to a struct omap_rproc_timer to return the timer handle 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * This helper function is used primarily to request a timer associated with 14862306a36Sopenharmony_ci * a remoteproc. The returned handle is stored in the .odt field of the 14962306a36Sopenharmony_ci * @timer structure passed in, and is used to invoke other timer specific 15062306a36Sopenharmony_ci * ops (like starting a timer either during device initialization or during 15162306a36Sopenharmony_ci * a resume operation, or for stopping/freeing a timer). 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * Return: 0 on success, otherwise an appropriate failure 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_cistatic int omap_rproc_request_timer(struct device *dev, struct device_node *np, 15662306a36Sopenharmony_ci struct omap_rproc_timer *timer) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci timer->odt = timer->timer_ops->request_by_node(np); 16162306a36Sopenharmony_ci if (!timer->odt) { 16262306a36Sopenharmony_ci dev_err(dev, "request for timer node %p failed\n", np); 16362306a36Sopenharmony_ci return -EBUSY; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ret = timer->timer_ops->set_source(timer->odt, OMAP_TIMER_SRC_SYS_CLK); 16762306a36Sopenharmony_ci if (ret) { 16862306a36Sopenharmony_ci dev_err(dev, "error setting OMAP_TIMER_SRC_SYS_CLK as source for timer node %p\n", 16962306a36Sopenharmony_ci np); 17062306a36Sopenharmony_ci timer->timer_ops->free(timer->odt); 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* clean counter, remoteproc code will set the value */ 17562306a36Sopenharmony_ci timer->timer_ops->set_load(timer->odt, 0); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * omap_rproc_start_timer() - start a timer for a remoteproc 18262306a36Sopenharmony_ci * @timer: handle to a OMAP rproc timer 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * This helper function is used to start a timer associated with a remoteproc, 18562306a36Sopenharmony_ci * obtained using the request_timer ops. The helper function needs to be 18662306a36Sopenharmony_ci * invoked by the driver to start the timer (during device initialization) 18762306a36Sopenharmony_ci * or to just resume the timer. 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * Return: 0 on success, otherwise a failure as returned by DMTimer ops 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic inline int omap_rproc_start_timer(struct omap_rproc_timer *timer) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci return timer->timer_ops->start(timer->odt); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/** 19762306a36Sopenharmony_ci * omap_rproc_stop_timer() - stop a timer for a remoteproc 19862306a36Sopenharmony_ci * @timer: handle to a OMAP rproc timer 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * This helper function is used to disable a timer associated with a 20162306a36Sopenharmony_ci * remoteproc, and needs to be called either during a device shutdown 20262306a36Sopenharmony_ci * or suspend operation. The separate helper function allows the driver 20362306a36Sopenharmony_ci * to just stop a timer without having to release the timer during a 20462306a36Sopenharmony_ci * suspend operation. 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * Return: 0 on success, otherwise a failure as returned by DMTimer ops 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_cistatic inline int omap_rproc_stop_timer(struct omap_rproc_timer *timer) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return timer->timer_ops->stop(timer->odt); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * omap_rproc_release_timer() - release a timer for a remoteproc 21562306a36Sopenharmony_ci * @timer: handle to a OMAP rproc timer 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * This helper function is used primarily to release a timer associated 21862306a36Sopenharmony_ci * with a remoteproc. The dmtimer will be available for other clients to 21962306a36Sopenharmony_ci * use once released. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * Return: 0 on success, otherwise a failure as returned by DMTimer ops 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_cistatic inline int omap_rproc_release_timer(struct omap_rproc_timer *timer) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci return timer->timer_ops->free(timer->odt); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * omap_rproc_get_timer_irq() - get the irq for a timer 23062306a36Sopenharmony_ci * @timer: handle to a OMAP rproc timer 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * This function is used to get the irq associated with a watchdog timer. The 23362306a36Sopenharmony_ci * function is called by the OMAP remoteproc driver to register a interrupt 23462306a36Sopenharmony_ci * handler to handle watchdog events on the remote processor. 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * Return: irq id on success, otherwise a failure as returned by DMTimer ops 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_cistatic inline int omap_rproc_get_timer_irq(struct omap_rproc_timer *timer) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci return timer->timer_ops->get_irq(timer->odt); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/** 24462306a36Sopenharmony_ci * omap_rproc_ack_timer_irq() - acknowledge a timer irq 24562306a36Sopenharmony_ci * @timer: handle to a OMAP rproc timer 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * This function is used to clear the irq associated with a watchdog timer. 24862306a36Sopenharmony_ci * The function is called by the OMAP remoteproc upon a watchdog event on the 24962306a36Sopenharmony_ci * remote processor to clear the interrupt status of the watchdog timer. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_cistatic inline void omap_rproc_ack_timer_irq(struct omap_rproc_timer *timer) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci timer->timer_ops->write_status(timer->odt, OMAP_TIMER_INT_OVERFLOW); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/** 25762306a36Sopenharmony_ci * omap_rproc_watchdog_isr() - Watchdog ISR handler for remoteproc device 25862306a36Sopenharmony_ci * @irq: IRQ number associated with a watchdog timer 25962306a36Sopenharmony_ci * @data: IRQ handler data 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * This ISR routine executes the required necessary low-level code to 26262306a36Sopenharmony_ci * acknowledge a watchdog timer interrupt. There can be multiple watchdog 26362306a36Sopenharmony_ci * timers associated with a rproc (like IPUs which have 2 watchdog timers, 26462306a36Sopenharmony_ci * one per Cortex M3/M4 core), so a lookup has to be performed to identify 26562306a36Sopenharmony_ci * the timer to acknowledge its interrupt. 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * The function also invokes rproc_report_crash to report the watchdog event 26862306a36Sopenharmony_ci * to the remoteproc driver core, to trigger a recovery. 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Return: IRQ_HANDLED on success, otherwise IRQ_NONE 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistatic irqreturn_t omap_rproc_watchdog_isr(int irq, void *data) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct rproc *rproc = data; 27562306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 27662306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 27762306a36Sopenharmony_ci struct omap_rproc_timer *timers = oproc->timers; 27862306a36Sopenharmony_ci struct omap_rproc_timer *wd_timer = NULL; 27962306a36Sopenharmony_ci int num_timers = oproc->num_timers + oproc->num_wd_timers; 28062306a36Sopenharmony_ci int i; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (i = oproc->num_timers; i < num_timers; i++) { 28362306a36Sopenharmony_ci if (timers[i].irq > 0 && irq == timers[i].irq) { 28462306a36Sopenharmony_ci wd_timer = &timers[i]; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!wd_timer) { 29062306a36Sopenharmony_ci dev_err(dev, "invalid timer\n"); 29162306a36Sopenharmony_ci return IRQ_NONE; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci omap_rproc_ack_timer_irq(wd_timer); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci rproc_report_crash(rproc, RPROC_WATCHDOG); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return IRQ_HANDLED; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * omap_rproc_enable_timers() - enable the timers for a remoteproc 30362306a36Sopenharmony_ci * @rproc: handle of a remote processor 30462306a36Sopenharmony_ci * @configure: boolean flag used to acquire and configure the timer handle 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * This function is used primarily to enable the timers associated with 30762306a36Sopenharmony_ci * a remoteproc. The configure flag is provided to allow the driver 30862306a36Sopenharmony_ci * to either acquire and start a timer (during device initialization) or 30962306a36Sopenharmony_ci * to just start a timer (during a resume operation). 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Return: 0 on success, otherwise an appropriate failure 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cistatic int omap_rproc_enable_timers(struct rproc *rproc, bool configure) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci int i; 31662306a36Sopenharmony_ci int ret = 0; 31762306a36Sopenharmony_ci struct platform_device *tpdev; 31862306a36Sopenharmony_ci struct dmtimer_platform_data *tpdata; 31962306a36Sopenharmony_ci const struct omap_dm_timer_ops *timer_ops; 32062306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 32162306a36Sopenharmony_ci struct omap_rproc_timer *timers = oproc->timers; 32262306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 32362306a36Sopenharmony_ci struct device_node *np = NULL; 32462306a36Sopenharmony_ci int num_timers = oproc->num_timers + oproc->num_wd_timers; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!num_timers) 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (!configure) 33062306a36Sopenharmony_ci goto start_timers; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci for (i = 0; i < num_timers; i++) { 33362306a36Sopenharmony_ci if (i < oproc->num_timers) 33462306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, "ti,timers", i); 33562306a36Sopenharmony_ci else 33662306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, 33762306a36Sopenharmony_ci "ti,watchdog-timers", 33862306a36Sopenharmony_ci (i - oproc->num_timers)); 33962306a36Sopenharmony_ci if (!np) { 34062306a36Sopenharmony_ci ret = -ENXIO; 34162306a36Sopenharmony_ci dev_err(dev, "device node lookup for timer at index %d failed: %d\n", 34262306a36Sopenharmony_ci i < oproc->num_timers ? i : 34362306a36Sopenharmony_ci i - oproc->num_timers, ret); 34462306a36Sopenharmony_ci goto free_timers; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci tpdev = of_find_device_by_node(np); 34862306a36Sopenharmony_ci if (!tpdev) { 34962306a36Sopenharmony_ci ret = -ENODEV; 35062306a36Sopenharmony_ci dev_err(dev, "could not get timer platform device\n"); 35162306a36Sopenharmony_ci goto put_node; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci tpdata = dev_get_platdata(&tpdev->dev); 35562306a36Sopenharmony_ci put_device(&tpdev->dev); 35662306a36Sopenharmony_ci if (!tpdata) { 35762306a36Sopenharmony_ci ret = -EINVAL; 35862306a36Sopenharmony_ci dev_err(dev, "dmtimer pdata structure NULL\n"); 35962306a36Sopenharmony_ci goto put_node; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci timer_ops = tpdata->timer_ops; 36362306a36Sopenharmony_ci if (!timer_ops || !timer_ops->request_by_node || 36462306a36Sopenharmony_ci !timer_ops->set_source || !timer_ops->set_load || 36562306a36Sopenharmony_ci !timer_ops->free || !timer_ops->start || 36662306a36Sopenharmony_ci !timer_ops->stop || !timer_ops->get_irq || 36762306a36Sopenharmony_ci !timer_ops->write_status) { 36862306a36Sopenharmony_ci ret = -EINVAL; 36962306a36Sopenharmony_ci dev_err(dev, "device does not have required timer ops\n"); 37062306a36Sopenharmony_ci goto put_node; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci timers[i].irq = -1; 37462306a36Sopenharmony_ci timers[i].timer_ops = timer_ops; 37562306a36Sopenharmony_ci ret = omap_rproc_request_timer(dev, np, &timers[i]); 37662306a36Sopenharmony_ci if (ret) { 37762306a36Sopenharmony_ci dev_err(dev, "request for timer %p failed: %d\n", np, 37862306a36Sopenharmony_ci ret); 37962306a36Sopenharmony_ci goto put_node; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci of_node_put(np); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (i >= oproc->num_timers) { 38462306a36Sopenharmony_ci timers[i].irq = omap_rproc_get_timer_irq(&timers[i]); 38562306a36Sopenharmony_ci if (timers[i].irq < 0) { 38662306a36Sopenharmony_ci dev_err(dev, "get_irq for timer %p failed: %d\n", 38762306a36Sopenharmony_ci np, timers[i].irq); 38862306a36Sopenharmony_ci ret = -EBUSY; 38962306a36Sopenharmony_ci goto free_timers; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = request_irq(timers[i].irq, 39362306a36Sopenharmony_ci omap_rproc_watchdog_isr, IRQF_SHARED, 39462306a36Sopenharmony_ci "rproc-wdt", rproc); 39562306a36Sopenharmony_ci if (ret) { 39662306a36Sopenharmony_ci dev_err(dev, "error requesting irq for timer %p\n", 39762306a36Sopenharmony_ci np); 39862306a36Sopenharmony_ci omap_rproc_release_timer(&timers[i]); 39962306a36Sopenharmony_ci timers[i].odt = NULL; 40062306a36Sopenharmony_ci timers[i].timer_ops = NULL; 40162306a36Sopenharmony_ci timers[i].irq = -1; 40262306a36Sopenharmony_ci goto free_timers; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistart_timers: 40862306a36Sopenharmony_ci for (i = 0; i < num_timers; i++) { 40962306a36Sopenharmony_ci ret = omap_rproc_start_timer(&timers[i]); 41062306a36Sopenharmony_ci if (ret) { 41162306a36Sopenharmony_ci dev_err(dev, "start timer %p failed failed: %d\n", np, 41262306a36Sopenharmony_ci ret); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci if (ret) { 41762306a36Sopenharmony_ci while (i >= 0) { 41862306a36Sopenharmony_ci omap_rproc_stop_timer(&timers[i]); 41962306a36Sopenharmony_ci i--; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci goto put_node; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciput_node: 42662306a36Sopenharmony_ci if (configure) 42762306a36Sopenharmony_ci of_node_put(np); 42862306a36Sopenharmony_cifree_timers: 42962306a36Sopenharmony_ci while (i--) { 43062306a36Sopenharmony_ci if (i >= oproc->num_timers) 43162306a36Sopenharmony_ci free_irq(timers[i].irq, rproc); 43262306a36Sopenharmony_ci omap_rproc_release_timer(&timers[i]); 43362306a36Sopenharmony_ci timers[i].odt = NULL; 43462306a36Sopenharmony_ci timers[i].timer_ops = NULL; 43562306a36Sopenharmony_ci timers[i].irq = -1; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return ret; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/** 44262306a36Sopenharmony_ci * omap_rproc_disable_timers() - disable the timers for a remoteproc 44362306a36Sopenharmony_ci * @rproc: handle of a remote processor 44462306a36Sopenharmony_ci * @configure: boolean flag used to release the timer handle 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * This function is used primarily to disable the timers associated with 44762306a36Sopenharmony_ci * a remoteproc. The configure flag is provided to allow the driver 44862306a36Sopenharmony_ci * to either stop and release a timer (during device shutdown) or to just 44962306a36Sopenharmony_ci * stop a timer (during a suspend operation). 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * Return: 0 on success or no timers 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_cistatic int omap_rproc_disable_timers(struct rproc *rproc, bool configure) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci int i; 45662306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 45762306a36Sopenharmony_ci struct omap_rproc_timer *timers = oproc->timers; 45862306a36Sopenharmony_ci int num_timers = oproc->num_timers + oproc->num_wd_timers; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (!num_timers) 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci for (i = 0; i < num_timers; i++) { 46462306a36Sopenharmony_ci omap_rproc_stop_timer(&timers[i]); 46562306a36Sopenharmony_ci if (configure) { 46662306a36Sopenharmony_ci if (i >= oproc->num_timers) 46762306a36Sopenharmony_ci free_irq(timers[i].irq, rproc); 46862306a36Sopenharmony_ci omap_rproc_release_timer(&timers[i]); 46962306a36Sopenharmony_ci timers[i].odt = NULL; 47062306a36Sopenharmony_ci timers[i].timer_ops = NULL; 47162306a36Sopenharmony_ci timers[i].irq = -1; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/** 47962306a36Sopenharmony_ci * omap_rproc_mbox_callback() - inbound mailbox message handler 48062306a36Sopenharmony_ci * @client: mailbox client pointer used for requesting the mailbox channel 48162306a36Sopenharmony_ci * @data: mailbox payload 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * This handler is invoked by omap's mailbox driver whenever a mailbox 48462306a36Sopenharmony_ci * message is received. Usually, the mailbox payload simply contains 48562306a36Sopenharmony_ci * the index of the virtqueue that is kicked by the remote processor, 48662306a36Sopenharmony_ci * and we let remoteproc core handle it. 48762306a36Sopenharmony_ci * 48862306a36Sopenharmony_ci * In addition to virtqueue indices, we also have some out-of-band values 48962306a36Sopenharmony_ci * that indicates different events. Those values are deliberately very 49062306a36Sopenharmony_ci * big so they don't coincide with virtqueue indices. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_cistatic void omap_rproc_mbox_callback(struct mbox_client *client, void *data) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct omap_rproc *oproc = container_of(client, struct omap_rproc, 49562306a36Sopenharmony_ci client); 49662306a36Sopenharmony_ci struct device *dev = oproc->rproc->dev.parent; 49762306a36Sopenharmony_ci const char *name = oproc->rproc->name; 49862306a36Sopenharmony_ci u32 msg = (u32)data; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci dev_dbg(dev, "mbox msg: 0x%x\n", msg); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci switch (msg) { 50362306a36Sopenharmony_ci case RP_MBOX_CRASH: 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * remoteproc detected an exception, notify the rproc core. 50662306a36Sopenharmony_ci * The remoteproc core will handle the recovery. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci dev_err(dev, "omap rproc %s crashed\n", name); 50962306a36Sopenharmony_ci rproc_report_crash(oproc->rproc, RPROC_FATAL_ERROR); 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci case RP_MBOX_ECHO_REPLY: 51262306a36Sopenharmony_ci dev_info(dev, "received echo reply from %s\n", name); 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci case RP_MBOX_SUSPEND_ACK: 51562306a36Sopenharmony_ci case RP_MBOX_SUSPEND_CANCEL: 51662306a36Sopenharmony_ci oproc->suspend_acked = msg == RP_MBOX_SUSPEND_ACK; 51762306a36Sopenharmony_ci complete(&oproc->pm_comp); 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci default: 52062306a36Sopenharmony_ci if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG) 52162306a36Sopenharmony_ci return; 52262306a36Sopenharmony_ci if (msg > oproc->rproc->max_notifyid) { 52362306a36Sopenharmony_ci dev_dbg(dev, "dropping unknown message 0x%x", msg); 52462306a36Sopenharmony_ci return; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci /* msg contains the index of the triggered vring */ 52762306a36Sopenharmony_ci if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE) 52862306a36Sopenharmony_ci dev_dbg(dev, "no message was found in vqid %d\n", msg); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* kick a virtqueue */ 53362306a36Sopenharmony_cistatic void omap_rproc_kick(struct rproc *rproc, int vqid) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 53662306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 53762306a36Sopenharmony_ci int ret; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* wake up the rproc before kicking it */ 54062306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 54162306a36Sopenharmony_ci if (WARN_ON(ret < 0)) { 54262306a36Sopenharmony_ci dev_err(dev, "pm_runtime_get_sync() failed during kick, ret = %d\n", 54362306a36Sopenharmony_ci ret); 54462306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 54562306a36Sopenharmony_ci return; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* send the index of the triggered virtqueue in the mailbox payload */ 54962306a36Sopenharmony_ci ret = mbox_send_message(oproc->mbox, (void *)vqid); 55062306a36Sopenharmony_ci if (ret < 0) 55162306a36Sopenharmony_ci dev_err(dev, "failed to send mailbox message, status = %d\n", 55262306a36Sopenharmony_ci ret); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 55562306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/** 55962306a36Sopenharmony_ci * omap_rproc_write_dsp_boot_addr() - set boot address for DSP remote processor 56062306a36Sopenharmony_ci * @rproc: handle of a remote processor 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Set boot address for a supported DSP remote processor. 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * Return: 0 on success, or -EINVAL if boot address is not aligned properly 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_cistatic int omap_rproc_write_dsp_boot_addr(struct rproc *rproc) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 56962306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 57062306a36Sopenharmony_ci struct omap_rproc_boot_data *bdata = oproc->boot_data; 57162306a36Sopenharmony_ci u32 offset = bdata->boot_reg; 57262306a36Sopenharmony_ci u32 value; 57362306a36Sopenharmony_ci u32 mask; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (rproc->bootaddr & (SZ_1K - 1)) { 57662306a36Sopenharmony_ci dev_err(dev, "invalid boot address 0x%llx, must be aligned on a 1KB boundary\n", 57762306a36Sopenharmony_ci rproc->bootaddr); 57862306a36Sopenharmony_ci return -EINVAL; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci value = rproc->bootaddr >> bdata->boot_reg_shift; 58262306a36Sopenharmony_ci mask = ~(SZ_1K - 1) >> bdata->boot_reg_shift; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return regmap_update_bits(bdata->syscon, offset, mask, value); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/* 58862306a36Sopenharmony_ci * Power up the remote processor. 58962306a36Sopenharmony_ci * 59062306a36Sopenharmony_ci * This function will be invoked only after the firmware for this rproc 59162306a36Sopenharmony_ci * was loaded, parsed successfully, and all of its resource requirements 59262306a36Sopenharmony_ci * were met. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_cistatic int omap_rproc_start(struct rproc *rproc) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 59762306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 59862306a36Sopenharmony_ci int ret; 59962306a36Sopenharmony_ci struct mbox_client *client = &oproc->client; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (oproc->boot_data) { 60262306a36Sopenharmony_ci ret = omap_rproc_write_dsp_boot_addr(rproc); 60362306a36Sopenharmony_ci if (ret) 60462306a36Sopenharmony_ci return ret; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci client->dev = dev; 60862306a36Sopenharmony_ci client->tx_done = NULL; 60962306a36Sopenharmony_ci client->rx_callback = omap_rproc_mbox_callback; 61062306a36Sopenharmony_ci client->tx_block = false; 61162306a36Sopenharmony_ci client->knows_txdone = false; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci oproc->mbox = mbox_request_channel(client, 0); 61462306a36Sopenharmony_ci if (IS_ERR(oproc->mbox)) { 61562306a36Sopenharmony_ci ret = -EBUSY; 61662306a36Sopenharmony_ci dev_err(dev, "mbox_request_channel failed: %ld\n", 61762306a36Sopenharmony_ci PTR_ERR(oproc->mbox)); 61862306a36Sopenharmony_ci return ret; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * Ping the remote processor. this is only for sanity-sake; 62362306a36Sopenharmony_ci * there is no functional effect whatsoever. 62462306a36Sopenharmony_ci * 62562306a36Sopenharmony_ci * Note that the reply will _not_ arrive immediately: this message 62662306a36Sopenharmony_ci * will wait in the mailbox fifo until the remote processor is booted. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); 62962306a36Sopenharmony_ci if (ret < 0) { 63062306a36Sopenharmony_ci dev_err(dev, "mbox_send_message failed: %d\n", ret); 63162306a36Sopenharmony_ci goto put_mbox; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ret = omap_rproc_enable_timers(rproc, true); 63562306a36Sopenharmony_ci if (ret) { 63662306a36Sopenharmony_ci dev_err(dev, "omap_rproc_enable_timers failed: %d\n", ret); 63762306a36Sopenharmony_ci goto put_mbox; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci ret = reset_control_deassert(oproc->reset); 64162306a36Sopenharmony_ci if (ret) { 64262306a36Sopenharmony_ci dev_err(dev, "reset control deassert failed: %d\n", ret); 64362306a36Sopenharmony_ci goto disable_timers; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * remote processor is up, so update the runtime pm status and 64862306a36Sopenharmony_ci * enable the auto-suspend. The device usage count is incremented 64962306a36Sopenharmony_ci * manually for balancing it for auto-suspend 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci pm_runtime_set_active(dev); 65262306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 65362306a36Sopenharmony_ci pm_runtime_get_noresume(dev); 65462306a36Sopenharmony_ci pm_runtime_enable(dev); 65562306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 65662306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cidisable_timers: 66162306a36Sopenharmony_ci omap_rproc_disable_timers(rproc, true); 66262306a36Sopenharmony_ciput_mbox: 66362306a36Sopenharmony_ci mbox_free_channel(oproc->mbox); 66462306a36Sopenharmony_ci return ret; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* power off the remote processor */ 66862306a36Sopenharmony_cistatic int omap_rproc_stop(struct rproc *rproc) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 67162306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 67262306a36Sopenharmony_ci int ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* 67562306a36Sopenharmony_ci * cancel any possible scheduled runtime suspend by incrementing 67662306a36Sopenharmony_ci * the device usage count, and resuming the device. The remoteproc 67762306a36Sopenharmony_ci * also needs to be woken up if suspended, to avoid the remoteproc 67862306a36Sopenharmony_ci * OS to continue to remember any context that it has saved, and 67962306a36Sopenharmony_ci * avoid potential issues in misindentifying a subsequent device 68062306a36Sopenharmony_ci * reboot as a power restore boot 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 68362306a36Sopenharmony_ci if (ret < 0) { 68462306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci ret = reset_control_assert(oproc->reset); 68962306a36Sopenharmony_ci if (ret) 69062306a36Sopenharmony_ci goto out; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci ret = omap_rproc_disable_timers(rproc, true); 69362306a36Sopenharmony_ci if (ret) 69462306a36Sopenharmony_ci goto enable_device; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci mbox_free_channel(oproc->mbox); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * update the runtime pm states and status now that the remoteproc 70062306a36Sopenharmony_ci * has stopped 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci pm_runtime_disable(dev); 70362306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(dev); 70462306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 70562306a36Sopenharmony_ci pm_runtime_set_suspended(dev); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cienable_device: 71062306a36Sopenharmony_ci reset_control_deassert(oproc->reset); 71162306a36Sopenharmony_ciout: 71262306a36Sopenharmony_ci /* schedule the next auto-suspend */ 71362306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 71462306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 71562306a36Sopenharmony_ci return ret; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/** 71962306a36Sopenharmony_ci * omap_rproc_da_to_va() - internal memory translation helper 72062306a36Sopenharmony_ci * @rproc: remote processor to apply the address translation for 72162306a36Sopenharmony_ci * @da: device address to translate 72262306a36Sopenharmony_ci * @len: length of the memory buffer 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * Custom function implementing the rproc .da_to_va ops to provide address 72562306a36Sopenharmony_ci * translation (device address to kernel virtual address) for internal RAMs 72662306a36Sopenharmony_ci * present in a DSP or IPU device). The translated addresses can be used 72762306a36Sopenharmony_ci * either by the remoteproc core for loading, or by any rpmsg bus drivers. 72862306a36Sopenharmony_ci * 72962306a36Sopenharmony_ci * Return: translated virtual address in kernel memory space on success, 73062306a36Sopenharmony_ci * or NULL on failure. 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_cistatic void *omap_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 73562306a36Sopenharmony_ci int i; 73662306a36Sopenharmony_ci u32 offset; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (len <= 0) 73962306a36Sopenharmony_ci return NULL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (!oproc->num_mems) 74262306a36Sopenharmony_ci return NULL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci for (i = 0; i < oproc->num_mems; i++) { 74562306a36Sopenharmony_ci if (da >= oproc->mem[i].dev_addr && da + len <= 74662306a36Sopenharmony_ci oproc->mem[i].dev_addr + oproc->mem[i].size) { 74762306a36Sopenharmony_ci offset = da - oproc->mem[i].dev_addr; 74862306a36Sopenharmony_ci /* __force to make sparse happy with type conversion */ 74962306a36Sopenharmony_ci return (__force void *)(oproc->mem[i].cpu_addr + 75062306a36Sopenharmony_ci offset); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return NULL; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic const struct rproc_ops omap_rproc_ops = { 75862306a36Sopenharmony_ci .start = omap_rproc_start, 75962306a36Sopenharmony_ci .stop = omap_rproc_stop, 76062306a36Sopenharmony_ci .kick = omap_rproc_kick, 76162306a36Sopenharmony_ci .da_to_va = omap_rproc_da_to_va, 76262306a36Sopenharmony_ci}; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci#ifdef CONFIG_PM 76562306a36Sopenharmony_cistatic bool _is_rproc_in_standby(struct omap_rproc *oproc) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci return ti_clk_is_in_standby(oproc->fck); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/* 1 sec is long enough time to let the remoteproc side suspend the device */ 77162306a36Sopenharmony_ci#define DEF_SUSPEND_TIMEOUT 1000 77262306a36Sopenharmony_cistatic int _omap_rproc_suspend(struct rproc *rproc, bool auto_suspend) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 77562306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 77662306a36Sopenharmony_ci unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT); 77762306a36Sopenharmony_ci unsigned long ta = jiffies + to; 77862306a36Sopenharmony_ci u32 suspend_msg = auto_suspend ? 77962306a36Sopenharmony_ci RP_MBOX_SUSPEND_AUTO : RP_MBOX_SUSPEND_SYSTEM; 78062306a36Sopenharmony_ci int ret; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci reinit_completion(&oproc->pm_comp); 78362306a36Sopenharmony_ci oproc->suspend_acked = false; 78462306a36Sopenharmony_ci ret = mbox_send_message(oproc->mbox, (void *)suspend_msg); 78562306a36Sopenharmony_ci if (ret < 0) { 78662306a36Sopenharmony_ci dev_err(dev, "PM mbox_send_message failed: %d\n", ret); 78762306a36Sopenharmony_ci return ret; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci ret = wait_for_completion_timeout(&oproc->pm_comp, to); 79162306a36Sopenharmony_ci if (!oproc->suspend_acked) 79262306a36Sopenharmony_ci return -EBUSY; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * The remoteproc side is returning the ACK message before saving the 79662306a36Sopenharmony_ci * context, because the context saving is performed within a SYS/BIOS 79762306a36Sopenharmony_ci * function, and it cannot have any inter-dependencies against the IPC 79862306a36Sopenharmony_ci * layer. Also, as the SYS/BIOS needs to preserve properly the processor 79962306a36Sopenharmony_ci * register set, sending this ACK or signalling the completion of the 80062306a36Sopenharmony_ci * context save through a shared memory variable can never be the 80162306a36Sopenharmony_ci * absolute last thing to be executed on the remoteproc side, and the 80262306a36Sopenharmony_ci * MPU cannot use the ACK message as a sync point to put the remoteproc 80362306a36Sopenharmony_ci * into reset. The only way to ensure that the remote processor has 80462306a36Sopenharmony_ci * completed saving the context is to check that the module has reached 80562306a36Sopenharmony_ci * STANDBY state (after saving the context, the SYS/BIOS executes the 80662306a36Sopenharmony_ci * appropriate target-specific WFI instruction causing the module to 80762306a36Sopenharmony_ci * enter STANDBY). 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_ci while (!_is_rproc_in_standby(oproc)) { 81062306a36Sopenharmony_ci if (time_after(jiffies, ta)) 81162306a36Sopenharmony_ci return -ETIME; 81262306a36Sopenharmony_ci schedule(); 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci ret = reset_control_assert(oproc->reset); 81662306a36Sopenharmony_ci if (ret) { 81762306a36Sopenharmony_ci dev_err(dev, "reset assert during suspend failed %d\n", ret); 81862306a36Sopenharmony_ci return ret; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci ret = omap_rproc_disable_timers(rproc, false); 82262306a36Sopenharmony_ci if (ret) { 82362306a36Sopenharmony_ci dev_err(dev, "disabling timers during suspend failed %d\n", 82462306a36Sopenharmony_ci ret); 82562306a36Sopenharmony_ci goto enable_device; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* 82962306a36Sopenharmony_ci * IOMMUs would have to be disabled specifically for runtime suspend. 83062306a36Sopenharmony_ci * They are handled automatically through System PM callbacks for 83162306a36Sopenharmony_ci * regular system suspend 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_ci if (auto_suspend) { 83462306a36Sopenharmony_ci ret = omap_iommu_domain_deactivate(rproc->domain); 83562306a36Sopenharmony_ci if (ret) { 83662306a36Sopenharmony_ci dev_err(dev, "iommu domain deactivate failed %d\n", 83762306a36Sopenharmony_ci ret); 83862306a36Sopenharmony_ci goto enable_timers; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cienable_timers: 84562306a36Sopenharmony_ci /* ignore errors on re-enabling code */ 84662306a36Sopenharmony_ci omap_rproc_enable_timers(rproc, false); 84762306a36Sopenharmony_cienable_device: 84862306a36Sopenharmony_ci reset_control_deassert(oproc->reset); 84962306a36Sopenharmony_ci return ret; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic int _omap_rproc_resume(struct rproc *rproc, bool auto_suspend) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct device *dev = rproc->dev.parent; 85562306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 85662306a36Sopenharmony_ci int ret; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* 85962306a36Sopenharmony_ci * IOMMUs would have to be enabled specifically for runtime resume. 86062306a36Sopenharmony_ci * They would have been already enabled automatically through System 86162306a36Sopenharmony_ci * PM callbacks for regular system resume 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci if (auto_suspend) { 86462306a36Sopenharmony_ci ret = omap_iommu_domain_activate(rproc->domain); 86562306a36Sopenharmony_ci if (ret) { 86662306a36Sopenharmony_ci dev_err(dev, "omap_iommu activate failed %d\n", ret); 86762306a36Sopenharmony_ci goto out; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* boot address could be lost after suspend, so restore it */ 87262306a36Sopenharmony_ci if (oproc->boot_data) { 87362306a36Sopenharmony_ci ret = omap_rproc_write_dsp_boot_addr(rproc); 87462306a36Sopenharmony_ci if (ret) { 87562306a36Sopenharmony_ci dev_err(dev, "boot address restore failed %d\n", ret); 87662306a36Sopenharmony_ci goto suspend_iommu; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci ret = omap_rproc_enable_timers(rproc, false); 88162306a36Sopenharmony_ci if (ret) { 88262306a36Sopenharmony_ci dev_err(dev, "enabling timers during resume failed %d\n", ret); 88362306a36Sopenharmony_ci goto suspend_iommu; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci ret = reset_control_deassert(oproc->reset); 88762306a36Sopenharmony_ci if (ret) { 88862306a36Sopenharmony_ci dev_err(dev, "reset deassert during resume failed %d\n", ret); 88962306a36Sopenharmony_ci goto disable_timers; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cidisable_timers: 89562306a36Sopenharmony_ci omap_rproc_disable_timers(rproc, false); 89662306a36Sopenharmony_cisuspend_iommu: 89762306a36Sopenharmony_ci if (auto_suspend) 89862306a36Sopenharmony_ci omap_iommu_domain_deactivate(rproc->domain); 89962306a36Sopenharmony_ciout: 90062306a36Sopenharmony_ci return ret; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int __maybe_unused omap_rproc_suspend(struct device *dev) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct rproc *rproc = dev_get_drvdata(dev); 90662306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 90762306a36Sopenharmony_ci int ret = 0; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci mutex_lock(&rproc->lock); 91062306a36Sopenharmony_ci if (rproc->state == RPROC_OFFLINE) 91162306a36Sopenharmony_ci goto out; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (rproc->state == RPROC_SUSPENDED) 91462306a36Sopenharmony_ci goto out; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (rproc->state != RPROC_RUNNING) { 91762306a36Sopenharmony_ci ret = -EBUSY; 91862306a36Sopenharmony_ci goto out; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ret = _omap_rproc_suspend(rproc, false); 92262306a36Sopenharmony_ci if (ret) { 92362306a36Sopenharmony_ci dev_err(dev, "suspend failed %d\n", ret); 92462306a36Sopenharmony_ci goto out; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* 92862306a36Sopenharmony_ci * remoteproc is running at the time of system suspend, so remember 92962306a36Sopenharmony_ci * it so as to wake it up during system resume 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_ci oproc->need_resume = true; 93262306a36Sopenharmony_ci rproc->state = RPROC_SUSPENDED; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ciout: 93562306a36Sopenharmony_ci mutex_unlock(&rproc->lock); 93662306a36Sopenharmony_ci return ret; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic int __maybe_unused omap_rproc_resume(struct device *dev) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct rproc *rproc = dev_get_drvdata(dev); 94262306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 94362306a36Sopenharmony_ci int ret = 0; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci mutex_lock(&rproc->lock); 94662306a36Sopenharmony_ci if (rproc->state == RPROC_OFFLINE) 94762306a36Sopenharmony_ci goto out; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (rproc->state != RPROC_SUSPENDED) { 95062306a36Sopenharmony_ci ret = -EBUSY; 95162306a36Sopenharmony_ci goto out; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* 95562306a36Sopenharmony_ci * remoteproc was auto-suspended at the time of system suspend, 95662306a36Sopenharmony_ci * so no need to wake-up the processor (leave it in suspended 95762306a36Sopenharmony_ci * state, will be woken up during a subsequent runtime_resume) 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ci if (!oproc->need_resume) 96062306a36Sopenharmony_ci goto out; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ret = _omap_rproc_resume(rproc, false); 96362306a36Sopenharmony_ci if (ret) { 96462306a36Sopenharmony_ci dev_err(dev, "resume failed %d\n", ret); 96562306a36Sopenharmony_ci goto out; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci oproc->need_resume = false; 96962306a36Sopenharmony_ci rproc->state = RPROC_RUNNING; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 97262306a36Sopenharmony_ciout: 97362306a36Sopenharmony_ci mutex_unlock(&rproc->lock); 97462306a36Sopenharmony_ci return ret; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic int omap_rproc_runtime_suspend(struct device *dev) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct rproc *rproc = dev_get_drvdata(dev); 98062306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 98162306a36Sopenharmony_ci int ret; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci mutex_lock(&rproc->lock); 98462306a36Sopenharmony_ci if (rproc->state == RPROC_CRASHED) { 98562306a36Sopenharmony_ci dev_dbg(dev, "rproc cannot be runtime suspended when crashed!\n"); 98662306a36Sopenharmony_ci ret = -EBUSY; 98762306a36Sopenharmony_ci goto out; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (WARN_ON(rproc->state != RPROC_RUNNING)) { 99162306a36Sopenharmony_ci dev_err(dev, "rproc cannot be runtime suspended when not running!\n"); 99262306a36Sopenharmony_ci ret = -EBUSY; 99362306a36Sopenharmony_ci goto out; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* 99762306a36Sopenharmony_ci * do not even attempt suspend if the remote processor is not 99862306a36Sopenharmony_ci * idled for runtime auto-suspend 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci if (!_is_rproc_in_standby(oproc)) { 100162306a36Sopenharmony_ci ret = -EBUSY; 100262306a36Sopenharmony_ci goto abort; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci ret = _omap_rproc_suspend(rproc, true); 100662306a36Sopenharmony_ci if (ret) 100762306a36Sopenharmony_ci goto abort; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci rproc->state = RPROC_SUSPENDED; 101062306a36Sopenharmony_ci mutex_unlock(&rproc->lock); 101162306a36Sopenharmony_ci return 0; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ciabort: 101462306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 101562306a36Sopenharmony_ciout: 101662306a36Sopenharmony_ci mutex_unlock(&rproc->lock); 101762306a36Sopenharmony_ci return ret; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int omap_rproc_runtime_resume(struct device *dev) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct rproc *rproc = dev_get_drvdata(dev); 102362306a36Sopenharmony_ci int ret; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci mutex_lock(&rproc->lock); 102662306a36Sopenharmony_ci if (WARN_ON(rproc->state != RPROC_SUSPENDED)) { 102762306a36Sopenharmony_ci dev_err(dev, "rproc cannot be runtime resumed if not suspended! state=%d\n", 102862306a36Sopenharmony_ci rproc->state); 102962306a36Sopenharmony_ci ret = -EBUSY; 103062306a36Sopenharmony_ci goto out; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci ret = _omap_rproc_resume(rproc, true); 103462306a36Sopenharmony_ci if (ret) { 103562306a36Sopenharmony_ci dev_err(dev, "runtime resume failed %d\n", ret); 103662306a36Sopenharmony_ci goto out; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci rproc->state = RPROC_RUNNING; 104062306a36Sopenharmony_ciout: 104162306a36Sopenharmony_ci mutex_unlock(&rproc->lock); 104262306a36Sopenharmony_ci return ret; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci#endif /* CONFIG_PM */ 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic const struct omap_rproc_mem_data ipu_mems[] = { 104762306a36Sopenharmony_ci { .name = "l2ram", .dev_addr = 0x20000000 }, 104862306a36Sopenharmony_ci { }, 104962306a36Sopenharmony_ci}; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic const struct omap_rproc_mem_data dra7_dsp_mems[] = { 105262306a36Sopenharmony_ci { .name = "l2ram", .dev_addr = 0x800000 }, 105362306a36Sopenharmony_ci { .name = "l1pram", .dev_addr = 0xe00000 }, 105462306a36Sopenharmony_ci { .name = "l1dram", .dev_addr = 0xf00000 }, 105562306a36Sopenharmony_ci { }, 105662306a36Sopenharmony_ci}; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic const struct omap_rproc_dev_data omap4_dsp_dev_data = { 105962306a36Sopenharmony_ci .device_name = "dsp", 106062306a36Sopenharmony_ci}; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic const struct omap_rproc_dev_data omap4_ipu_dev_data = { 106362306a36Sopenharmony_ci .device_name = "ipu", 106462306a36Sopenharmony_ci .mems = ipu_mems, 106562306a36Sopenharmony_ci}; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic const struct omap_rproc_dev_data omap5_dsp_dev_data = { 106862306a36Sopenharmony_ci .device_name = "dsp", 106962306a36Sopenharmony_ci}; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic const struct omap_rproc_dev_data omap5_ipu_dev_data = { 107262306a36Sopenharmony_ci .device_name = "ipu", 107362306a36Sopenharmony_ci .mems = ipu_mems, 107462306a36Sopenharmony_ci}; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic const struct omap_rproc_dev_data dra7_dsp_dev_data = { 107762306a36Sopenharmony_ci .device_name = "dsp", 107862306a36Sopenharmony_ci .mems = dra7_dsp_mems, 107962306a36Sopenharmony_ci}; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic const struct omap_rproc_dev_data dra7_ipu_dev_data = { 108262306a36Sopenharmony_ci .device_name = "ipu", 108362306a36Sopenharmony_ci .mems = ipu_mems, 108462306a36Sopenharmony_ci}; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic const struct of_device_id omap_rproc_of_match[] = { 108762306a36Sopenharmony_ci { 108862306a36Sopenharmony_ci .compatible = "ti,omap4-dsp", 108962306a36Sopenharmony_ci .data = &omap4_dsp_dev_data, 109062306a36Sopenharmony_ci }, 109162306a36Sopenharmony_ci { 109262306a36Sopenharmony_ci .compatible = "ti,omap4-ipu", 109362306a36Sopenharmony_ci .data = &omap4_ipu_dev_data, 109462306a36Sopenharmony_ci }, 109562306a36Sopenharmony_ci { 109662306a36Sopenharmony_ci .compatible = "ti,omap5-dsp", 109762306a36Sopenharmony_ci .data = &omap5_dsp_dev_data, 109862306a36Sopenharmony_ci }, 109962306a36Sopenharmony_ci { 110062306a36Sopenharmony_ci .compatible = "ti,omap5-ipu", 110162306a36Sopenharmony_ci .data = &omap5_ipu_dev_data, 110262306a36Sopenharmony_ci }, 110362306a36Sopenharmony_ci { 110462306a36Sopenharmony_ci .compatible = "ti,dra7-dsp", 110562306a36Sopenharmony_ci .data = &dra7_dsp_dev_data, 110662306a36Sopenharmony_ci }, 110762306a36Sopenharmony_ci { 110862306a36Sopenharmony_ci .compatible = "ti,dra7-ipu", 110962306a36Sopenharmony_ci .data = &dra7_ipu_dev_data, 111062306a36Sopenharmony_ci }, 111162306a36Sopenharmony_ci { 111262306a36Sopenharmony_ci /* end */ 111362306a36Sopenharmony_ci }, 111462306a36Sopenharmony_ci}; 111562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_rproc_of_match); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic const char *omap_rproc_get_firmware(struct platform_device *pdev) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci const char *fw_name; 112062306a36Sopenharmony_ci int ret; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci ret = of_property_read_string(pdev->dev.of_node, "firmware-name", 112362306a36Sopenharmony_ci &fw_name); 112462306a36Sopenharmony_ci if (ret) 112562306a36Sopenharmony_ci return ERR_PTR(ret); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci return fw_name; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic int omap_rproc_get_boot_data(struct platform_device *pdev, 113162306a36Sopenharmony_ci struct rproc *rproc) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 113462306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 113562306a36Sopenharmony_ci const struct omap_rproc_dev_data *data; 113662306a36Sopenharmony_ci int ret; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci data = of_device_get_match_data(&pdev->dev); 113962306a36Sopenharmony_ci if (!data) 114062306a36Sopenharmony_ci return -ENODEV; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (!of_property_read_bool(np, "ti,bootreg")) 114362306a36Sopenharmony_ci return 0; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci oproc->boot_data = devm_kzalloc(&pdev->dev, sizeof(*oproc->boot_data), 114662306a36Sopenharmony_ci GFP_KERNEL); 114762306a36Sopenharmony_ci if (!oproc->boot_data) 114862306a36Sopenharmony_ci return -ENOMEM; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci oproc->boot_data->syscon = 115162306a36Sopenharmony_ci syscon_regmap_lookup_by_phandle(np, "ti,bootreg"); 115262306a36Sopenharmony_ci if (IS_ERR(oproc->boot_data->syscon)) { 115362306a36Sopenharmony_ci ret = PTR_ERR(oproc->boot_data->syscon); 115462306a36Sopenharmony_ci return ret; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (of_property_read_u32_index(np, "ti,bootreg", 1, 115862306a36Sopenharmony_ci &oproc->boot_data->boot_reg)) { 115962306a36Sopenharmony_ci dev_err(&pdev->dev, "couldn't get the boot register\n"); 116062306a36Sopenharmony_ci return -EINVAL; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci of_property_read_u32_index(np, "ti,bootreg", 2, 116462306a36Sopenharmony_ci &oproc->boot_data->boot_reg_shift); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci return 0; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic int omap_rproc_of_get_internal_memories(struct platform_device *pdev, 117062306a36Sopenharmony_ci struct rproc *rproc) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 117362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 117462306a36Sopenharmony_ci const struct omap_rproc_dev_data *data; 117562306a36Sopenharmony_ci struct resource *res; 117662306a36Sopenharmony_ci int num_mems; 117762306a36Sopenharmony_ci int i; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci data = of_device_get_match_data(dev); 118062306a36Sopenharmony_ci if (!data) 118162306a36Sopenharmony_ci return -ENODEV; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (!data->mems) 118462306a36Sopenharmony_ci return 0; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci num_mems = of_property_count_elems_of_size(dev->of_node, "reg", 118762306a36Sopenharmony_ci sizeof(u32)) / 2; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci oproc->mem = devm_kcalloc(dev, num_mems, sizeof(*oproc->mem), 119062306a36Sopenharmony_ci GFP_KERNEL); 119162306a36Sopenharmony_ci if (!oproc->mem) 119262306a36Sopenharmony_ci return -ENOMEM; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci for (i = 0; data->mems[i].name; i++) { 119562306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 119662306a36Sopenharmony_ci data->mems[i].name); 119762306a36Sopenharmony_ci if (!res) { 119862306a36Sopenharmony_ci dev_err(dev, "no memory defined for %s\n", 119962306a36Sopenharmony_ci data->mems[i].name); 120062306a36Sopenharmony_ci return -ENOMEM; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci oproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res); 120362306a36Sopenharmony_ci if (IS_ERR(oproc->mem[i].cpu_addr)) { 120462306a36Sopenharmony_ci dev_err(dev, "failed to parse and map %s memory\n", 120562306a36Sopenharmony_ci data->mems[i].name); 120662306a36Sopenharmony_ci return PTR_ERR(oproc->mem[i].cpu_addr); 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci oproc->mem[i].bus_addr = res->start; 120962306a36Sopenharmony_ci oproc->mem[i].dev_addr = data->mems[i].dev_addr; 121062306a36Sopenharmony_ci oproc->mem[i].size = resource_size(res); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci dev_dbg(dev, "memory %8s: bus addr %pa size 0x%x va %pK da 0x%x\n", 121362306a36Sopenharmony_ci data->mems[i].name, &oproc->mem[i].bus_addr, 121462306a36Sopenharmony_ci oproc->mem[i].size, oproc->mem[i].cpu_addr, 121562306a36Sopenharmony_ci oproc->mem[i].dev_addr); 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci oproc->num_mems = num_mems; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci#ifdef CONFIG_OMAP_REMOTEPROC_WATCHDOG 122362306a36Sopenharmony_cistatic int omap_rproc_count_wdog_timers(struct device *dev) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci struct device_node *np = dev->of_node; 122662306a36Sopenharmony_ci int ret; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci ret = of_count_phandle_with_args(np, "ti,watchdog-timers", NULL); 122962306a36Sopenharmony_ci if (ret <= 0) { 123062306a36Sopenharmony_ci dev_dbg(dev, "device does not have watchdog timers, status = %d\n", 123162306a36Sopenharmony_ci ret); 123262306a36Sopenharmony_ci ret = 0; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci return ret; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci#else 123862306a36Sopenharmony_cistatic int omap_rproc_count_wdog_timers(struct device *dev) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci#endif 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic int omap_rproc_of_get_timers(struct platform_device *pdev, 124562306a36Sopenharmony_ci struct rproc *rproc) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 124862306a36Sopenharmony_ci struct omap_rproc *oproc = rproc->priv; 124962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 125062306a36Sopenharmony_ci int num_timers; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci /* 125362306a36Sopenharmony_ci * Timer nodes are directly used in client nodes as phandles, so 125462306a36Sopenharmony_ci * retrieve the count using appropriate size 125562306a36Sopenharmony_ci */ 125662306a36Sopenharmony_ci oproc->num_timers = of_count_phandle_with_args(np, "ti,timers", NULL); 125762306a36Sopenharmony_ci if (oproc->num_timers <= 0) { 125862306a36Sopenharmony_ci dev_dbg(dev, "device does not have timers, status = %d\n", 125962306a36Sopenharmony_ci oproc->num_timers); 126062306a36Sopenharmony_ci oproc->num_timers = 0; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci oproc->num_wd_timers = omap_rproc_count_wdog_timers(dev); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci num_timers = oproc->num_timers + oproc->num_wd_timers; 126662306a36Sopenharmony_ci if (num_timers) { 126762306a36Sopenharmony_ci oproc->timers = devm_kcalloc(dev, num_timers, 126862306a36Sopenharmony_ci sizeof(*oproc->timers), 126962306a36Sopenharmony_ci GFP_KERNEL); 127062306a36Sopenharmony_ci if (!oproc->timers) 127162306a36Sopenharmony_ci return -ENOMEM; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci dev_dbg(dev, "device has %d tick timers and %d watchdog timers\n", 127462306a36Sopenharmony_ci oproc->num_timers, oproc->num_wd_timers); 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci return 0; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic int omap_rproc_probe(struct platform_device *pdev) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 128362306a36Sopenharmony_ci struct omap_rproc *oproc; 128462306a36Sopenharmony_ci struct rproc *rproc; 128562306a36Sopenharmony_ci const char *firmware; 128662306a36Sopenharmony_ci int ret; 128762306a36Sopenharmony_ci struct reset_control *reset; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (!np) { 129062306a36Sopenharmony_ci dev_err(&pdev->dev, "only DT-based devices are supported\n"); 129162306a36Sopenharmony_ci return -ENODEV; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci reset = devm_reset_control_array_get_exclusive(&pdev->dev); 129562306a36Sopenharmony_ci if (IS_ERR(reset)) 129662306a36Sopenharmony_ci return PTR_ERR(reset); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci firmware = omap_rproc_get_firmware(pdev); 129962306a36Sopenharmony_ci if (IS_ERR(firmware)) 130062306a36Sopenharmony_ci return PTR_ERR(firmware); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 130362306a36Sopenharmony_ci if (ret) { 130462306a36Sopenharmony_ci dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret); 130562306a36Sopenharmony_ci return ret; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops, 130962306a36Sopenharmony_ci firmware, sizeof(*oproc)); 131062306a36Sopenharmony_ci if (!rproc) 131162306a36Sopenharmony_ci return -ENOMEM; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci oproc = rproc->priv; 131462306a36Sopenharmony_ci oproc->rproc = rproc; 131562306a36Sopenharmony_ci oproc->reset = reset; 131662306a36Sopenharmony_ci /* All existing OMAP IPU and DSP processors have an MMU */ 131762306a36Sopenharmony_ci rproc->has_iommu = true; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci ret = omap_rproc_of_get_internal_memories(pdev, rproc); 132062306a36Sopenharmony_ci if (ret) 132162306a36Sopenharmony_ci goto free_rproc; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci ret = omap_rproc_get_boot_data(pdev, rproc); 132462306a36Sopenharmony_ci if (ret) 132562306a36Sopenharmony_ci goto free_rproc; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci ret = omap_rproc_of_get_timers(pdev, rproc); 132862306a36Sopenharmony_ci if (ret) 132962306a36Sopenharmony_ci goto free_rproc; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci init_completion(&oproc->pm_comp); 133262306a36Sopenharmony_ci oproc->autosuspend_delay = DEFAULT_AUTOSUSPEND_DELAY; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, "ti,autosuspend-delay-ms", 133562306a36Sopenharmony_ci &oproc->autosuspend_delay); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, oproc->autosuspend_delay); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci oproc->fck = devm_clk_get(&pdev->dev, 0); 134062306a36Sopenharmony_ci if (IS_ERR(oproc->fck)) { 134162306a36Sopenharmony_ci ret = PTR_ERR(oproc->fck); 134262306a36Sopenharmony_ci goto free_rproc; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci ret = of_reserved_mem_device_init(&pdev->dev); 134662306a36Sopenharmony_ci if (ret) { 134762306a36Sopenharmony_ci dev_warn(&pdev->dev, "device does not have specific CMA pool.\n"); 134862306a36Sopenharmony_ci dev_warn(&pdev->dev, "Typically this should be provided,\n"); 134962306a36Sopenharmony_ci dev_warn(&pdev->dev, "only omit if you know what you are doing.\n"); 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci platform_set_drvdata(pdev, rproc); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci ret = rproc_add(rproc); 135562306a36Sopenharmony_ci if (ret) 135662306a36Sopenharmony_ci goto release_mem; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return 0; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cirelease_mem: 136162306a36Sopenharmony_ci of_reserved_mem_device_release(&pdev->dev); 136262306a36Sopenharmony_cifree_rproc: 136362306a36Sopenharmony_ci rproc_free(rproc); 136462306a36Sopenharmony_ci return ret; 136562306a36Sopenharmony_ci} 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cistatic void omap_rproc_remove(struct platform_device *pdev) 136862306a36Sopenharmony_ci{ 136962306a36Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci rproc_del(rproc); 137262306a36Sopenharmony_ci rproc_free(rproc); 137362306a36Sopenharmony_ci of_reserved_mem_device_release(&pdev->dev); 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_cistatic const struct dev_pm_ops omap_rproc_pm_ops = { 137762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume) 137862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(omap_rproc_runtime_suspend, 137962306a36Sopenharmony_ci omap_rproc_runtime_resume, NULL) 138062306a36Sopenharmony_ci}; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cistatic struct platform_driver omap_rproc_driver = { 138362306a36Sopenharmony_ci .probe = omap_rproc_probe, 138462306a36Sopenharmony_ci .remove_new = omap_rproc_remove, 138562306a36Sopenharmony_ci .driver = { 138662306a36Sopenharmony_ci .name = "omap-rproc", 138762306a36Sopenharmony_ci .pm = &omap_rproc_pm_ops, 138862306a36Sopenharmony_ci .of_match_table = omap_rproc_of_match, 138962306a36Sopenharmony_ci }, 139062306a36Sopenharmony_ci}; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cimodule_platform_driver(omap_rproc_driver); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 139562306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP Remote Processor control driver"); 1396