18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ST's Remote Processor Control Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 STMicroelectronics - All Rights Reserved 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Ludovic Barre <ludovic.barre@st.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h> 168c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci#include <linux/of_reserved_mem.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/regmap.h> 248c2ecf20Sopenharmony_ci#include <linux/remoteproc.h> 258c2ecf20Sopenharmony_ci#include <linux/reset.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "remoteproc_internal.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define ST_RPROC_VQ0 0 308c2ecf20Sopenharmony_ci#define ST_RPROC_VQ1 1 318c2ecf20Sopenharmony_ci#define ST_RPROC_MAX_VRING 2 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MBOX_RX 0 348c2ecf20Sopenharmony_ci#define MBOX_TX 1 358c2ecf20Sopenharmony_ci#define MBOX_MAX 2 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct st_rproc_config { 388c2ecf20Sopenharmony_ci bool sw_reset; 398c2ecf20Sopenharmony_ci bool pwr_reset; 408c2ecf20Sopenharmony_ci unsigned long bootaddr_mask; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct st_rproc { 448c2ecf20Sopenharmony_ci struct st_rproc_config *config; 458c2ecf20Sopenharmony_ci struct reset_control *sw_reset; 468c2ecf20Sopenharmony_ci struct reset_control *pwr_reset; 478c2ecf20Sopenharmony_ci struct clk *clk; 488c2ecf20Sopenharmony_ci u32 clk_rate; 498c2ecf20Sopenharmony_ci struct regmap *boot_base; 508c2ecf20Sopenharmony_ci u32 boot_offset; 518c2ecf20Sopenharmony_ci struct mbox_chan *mbox_chan[ST_RPROC_MAX_VRING * MBOX_MAX]; 528c2ecf20Sopenharmony_ci struct mbox_client mbox_client_vq0; 538c2ecf20Sopenharmony_ci struct mbox_client mbox_client_vq1; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void st_rproc_mbox_callback(struct device *dev, u32 msg) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct rproc *rproc = dev_get_drvdata(dev); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (rproc_vq_interrupt(rproc, msg) == IRQ_NONE) 618c2ecf20Sopenharmony_ci dev_dbg(dev, "no message was found in vqid %d\n", msg); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic 658c2ecf20Sopenharmony_civoid st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci st_rproc_mbox_callback(mbox_client->dev, 0); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic 718c2ecf20Sopenharmony_civoid st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci st_rproc_mbox_callback(mbox_client->dev, 1); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void st_rproc_kick(struct rproc *rproc, int vqid) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct st_rproc *ddata = rproc->priv; 798c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* send the index of the triggered virtqueue in the mailbox payload */ 838c2ecf20Sopenharmony_ci if (WARN_ON(vqid >= ST_RPROC_MAX_VRING)) 848c2ecf20Sopenharmony_ci return; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = mbox_send_message(ddata->mbox_chan[vqid * MBOX_MAX + MBOX_TX], 878c2ecf20Sopenharmony_ci (void *)&vqid); 888c2ecf20Sopenharmony_ci if (ret < 0) 898c2ecf20Sopenharmony_ci dev_err(dev, "failed to send message via mbox: %d\n", ret); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int st_rproc_mem_alloc(struct rproc *rproc, 938c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 968c2ecf20Sopenharmony_ci void *va; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci va = ioremap_wc(mem->dma, mem->len); 998c2ecf20Sopenharmony_ci if (!va) { 1008c2ecf20Sopenharmony_ci dev_err(dev, "Unable to map memory region: %pa+%zx\n", 1018c2ecf20Sopenharmony_ci &mem->dma, mem->len); 1028c2ecf20Sopenharmony_ci return -ENOMEM; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Update memory entry va */ 1068c2ecf20Sopenharmony_ci mem->va = va; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int st_rproc_mem_release(struct rproc *rproc, 1128c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci iounmap(mem->va); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 1228c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 1238c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem; 1248c2ecf20Sopenharmony_ci struct reserved_mem *rmem; 1258c2ecf20Sopenharmony_ci struct of_phandle_iterator it; 1268c2ecf20Sopenharmony_ci int index = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); 1298c2ecf20Sopenharmony_ci while (of_phandle_iterator_next(&it) == 0) { 1308c2ecf20Sopenharmony_ci rmem = of_reserved_mem_lookup(it.node); 1318c2ecf20Sopenharmony_ci if (!rmem) { 1328c2ecf20Sopenharmony_ci of_node_put(it.node); 1338c2ecf20Sopenharmony_ci dev_err(dev, "unable to acquire memory-region\n"); 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* No need to map vdev buffer */ 1388c2ecf20Sopenharmony_ci if (strcmp(it.node->name, "vdev0buffer")) { 1398c2ecf20Sopenharmony_ci /* Register memory region */ 1408c2ecf20Sopenharmony_ci mem = rproc_mem_entry_init(dev, NULL, 1418c2ecf20Sopenharmony_ci (dma_addr_t)rmem->base, 1428c2ecf20Sopenharmony_ci rmem->size, rmem->base, 1438c2ecf20Sopenharmony_ci st_rproc_mem_alloc, 1448c2ecf20Sopenharmony_ci st_rproc_mem_release, 1458c2ecf20Sopenharmony_ci it.node->name); 1468c2ecf20Sopenharmony_ci } else { 1478c2ecf20Sopenharmony_ci /* Register reserved memory for vdev buffer allocation */ 1488c2ecf20Sopenharmony_ci mem = rproc_of_resm_mem_entry_init(dev, index, 1498c2ecf20Sopenharmony_ci rmem->size, 1508c2ecf20Sopenharmony_ci rmem->base, 1518c2ecf20Sopenharmony_ci it.node->name); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!mem) { 1558c2ecf20Sopenharmony_ci of_node_put(it.node); 1568c2ecf20Sopenharmony_ci return -ENOMEM; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci rproc_add_carveout(rproc, mem); 1608c2ecf20Sopenharmony_ci index++; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return rproc_elf_load_rsc_table(rproc, fw); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int st_rproc_start(struct rproc *rproc) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct st_rproc *ddata = rproc->priv; 1698c2ecf20Sopenharmony_ci int err; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci regmap_update_bits(ddata->boot_base, ddata->boot_offset, 1728c2ecf20Sopenharmony_ci ddata->config->bootaddr_mask, rproc->bootaddr); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci err = clk_enable(ddata->clk); 1758c2ecf20Sopenharmony_ci if (err) { 1768c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Failed to enable clock\n"); 1778c2ecf20Sopenharmony_ci return err; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (ddata->config->sw_reset) { 1818c2ecf20Sopenharmony_ci err = reset_control_deassert(ddata->sw_reset); 1828c2ecf20Sopenharmony_ci if (err) { 1838c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Failed to deassert S/W Reset\n"); 1848c2ecf20Sopenharmony_ci goto sw_reset_fail; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (ddata->config->pwr_reset) { 1898c2ecf20Sopenharmony_ci err = reset_control_deassert(ddata->pwr_reset); 1908c2ecf20Sopenharmony_ci if (err) { 1918c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Failed to deassert Power Reset\n"); 1928c2ecf20Sopenharmony_ci goto pwr_reset_fail; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci dev_info(&rproc->dev, "Started from 0x%llx\n", rproc->bootaddr); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cipwr_reset_fail: 2028c2ecf20Sopenharmony_ci if (ddata->config->pwr_reset) 2038c2ecf20Sopenharmony_ci reset_control_assert(ddata->sw_reset); 2048c2ecf20Sopenharmony_cisw_reset_fail: 2058c2ecf20Sopenharmony_ci clk_disable(ddata->clk); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return err; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int st_rproc_stop(struct rproc *rproc) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct st_rproc *ddata = rproc->priv; 2138c2ecf20Sopenharmony_ci int sw_err = 0, pwr_err = 0; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (ddata->config->sw_reset) { 2168c2ecf20Sopenharmony_ci sw_err = reset_control_assert(ddata->sw_reset); 2178c2ecf20Sopenharmony_ci if (sw_err) 2188c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Failed to assert S/W Reset\n"); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (ddata->config->pwr_reset) { 2228c2ecf20Sopenharmony_ci pwr_err = reset_control_assert(ddata->pwr_reset); 2238c2ecf20Sopenharmony_ci if (pwr_err) 2248c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "Failed to assert Power Reset\n"); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci clk_disable(ddata->clk); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return sw_err ?: pwr_err; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic const struct rproc_ops st_rproc_ops = { 2338c2ecf20Sopenharmony_ci .kick = st_rproc_kick, 2348c2ecf20Sopenharmony_ci .start = st_rproc_start, 2358c2ecf20Sopenharmony_ci .stop = st_rproc_stop, 2368c2ecf20Sopenharmony_ci .parse_fw = st_rproc_parse_fw, 2378c2ecf20Sopenharmony_ci .load = rproc_elf_load_segments, 2388c2ecf20Sopenharmony_ci .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, 2398c2ecf20Sopenharmony_ci .sanity_check = rproc_elf_sanity_check, 2408c2ecf20Sopenharmony_ci .get_boot_addr = rproc_elf_get_boot_addr, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci * Fetch state of the processor: 0 is off, 1 is on. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic int st_rproc_state(struct platform_device *pdev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 2498c2ecf20Sopenharmony_ci struct st_rproc *ddata = rproc->priv; 2508c2ecf20Sopenharmony_ci int reset_sw = 0, reset_pwr = 0; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (ddata->config->sw_reset) 2538c2ecf20Sopenharmony_ci reset_sw = reset_control_status(ddata->sw_reset); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (ddata->config->pwr_reset) 2568c2ecf20Sopenharmony_ci reset_pwr = reset_control_status(ddata->pwr_reset); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (reset_sw < 0 || reset_pwr < 0) 2598c2ecf20Sopenharmony_ci return -EINVAL; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return !reset_sw && !reset_pwr; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic const struct st_rproc_config st40_rproc_cfg = { 2658c2ecf20Sopenharmony_ci .sw_reset = true, 2668c2ecf20Sopenharmony_ci .pwr_reset = true, 2678c2ecf20Sopenharmony_ci .bootaddr_mask = GENMASK(28, 1), 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct st_rproc_config st231_rproc_cfg = { 2718c2ecf20Sopenharmony_ci .sw_reset = true, 2728c2ecf20Sopenharmony_ci .pwr_reset = false, 2738c2ecf20Sopenharmony_ci .bootaddr_mask = GENMASK(31, 6), 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct of_device_id st_rproc_match[] = { 2778c2ecf20Sopenharmony_ci { .compatible = "st,st40-rproc", .data = &st40_rproc_cfg }, 2788c2ecf20Sopenharmony_ci { .compatible = "st,st231-rproc", .data = &st231_rproc_cfg }, 2798c2ecf20Sopenharmony_ci {}, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, st_rproc_match); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int st_rproc_parse_dt(struct platform_device *pdev) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2868c2ecf20Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 2878c2ecf20Sopenharmony_ci struct st_rproc *ddata = rproc->priv; 2888c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 2898c2ecf20Sopenharmony_ci int err; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (ddata->config->sw_reset) { 2928c2ecf20Sopenharmony_ci ddata->sw_reset = devm_reset_control_get_exclusive(dev, 2938c2ecf20Sopenharmony_ci "sw_reset"); 2948c2ecf20Sopenharmony_ci if (IS_ERR(ddata->sw_reset)) { 2958c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get S/W Reset\n"); 2968c2ecf20Sopenharmony_ci return PTR_ERR(ddata->sw_reset); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (ddata->config->pwr_reset) { 3018c2ecf20Sopenharmony_ci ddata->pwr_reset = devm_reset_control_get_exclusive(dev, 3028c2ecf20Sopenharmony_ci "pwr_reset"); 3038c2ecf20Sopenharmony_ci if (IS_ERR(ddata->pwr_reset)) { 3048c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get Power Reset\n"); 3058c2ecf20Sopenharmony_ci return PTR_ERR(ddata->pwr_reset); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ddata->clk = devm_clk_get(dev, NULL); 3108c2ecf20Sopenharmony_ci if (IS_ERR(ddata->clk)) { 3118c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get clock\n"); 3128c2ecf20Sopenharmony_ci return PTR_ERR(ddata->clk); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "clock-frequency", &ddata->clk_rate); 3168c2ecf20Sopenharmony_ci if (err) { 3178c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock frequency\n"); 3188c2ecf20Sopenharmony_ci return err; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ddata->boot_base = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); 3228c2ecf20Sopenharmony_ci if (IS_ERR(ddata->boot_base)) { 3238c2ecf20Sopenharmony_ci dev_err(dev, "Boot base not found\n"); 3248c2ecf20Sopenharmony_ci return PTR_ERR(ddata->boot_base); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci err = of_property_read_u32_index(np, "st,syscfg", 1, 3288c2ecf20Sopenharmony_ci &ddata->boot_offset); 3298c2ecf20Sopenharmony_ci if (err) { 3308c2ecf20Sopenharmony_ci dev_err(dev, "Boot offset not found\n"); 3318c2ecf20Sopenharmony_ci return -EINVAL; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci err = clk_prepare(ddata->clk); 3358c2ecf20Sopenharmony_ci if (err) 3368c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock\n"); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return err; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int st_rproc_probe(struct platform_device *pdev) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3448c2ecf20Sopenharmony_ci const struct of_device_id *match; 3458c2ecf20Sopenharmony_ci struct st_rproc *ddata; 3468c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 3478c2ecf20Sopenharmony_ci struct rproc *rproc; 3488c2ecf20Sopenharmony_ci struct mbox_chan *chan; 3498c2ecf20Sopenharmony_ci int enabled; 3508c2ecf20Sopenharmony_ci int ret, i; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci match = of_match_device(st_rproc_match, dev); 3538c2ecf20Sopenharmony_ci if (!match || !match->data) { 3548c2ecf20Sopenharmony_ci dev_err(dev, "No device match found\n"); 3558c2ecf20Sopenharmony_ci return -ENODEV; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci rproc = rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata)); 3598c2ecf20Sopenharmony_ci if (!rproc) 3608c2ecf20Sopenharmony_ci return -ENOMEM; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci rproc->has_iommu = false; 3638c2ecf20Sopenharmony_ci ddata = rproc->priv; 3648c2ecf20Sopenharmony_ci ddata->config = (struct st_rproc_config *)match->data; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rproc); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = st_rproc_parse_dt(pdev); 3698c2ecf20Sopenharmony_ci if (ret) 3708c2ecf20Sopenharmony_ci goto free_rproc; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci enabled = st_rproc_state(pdev); 3738c2ecf20Sopenharmony_ci if (enabled < 0) { 3748c2ecf20Sopenharmony_ci ret = enabled; 3758c2ecf20Sopenharmony_ci goto free_clk; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (enabled) { 3798c2ecf20Sopenharmony_ci atomic_inc(&rproc->power); 3808c2ecf20Sopenharmony_ci rproc->state = RPROC_RUNNING; 3818c2ecf20Sopenharmony_ci } else { 3828c2ecf20Sopenharmony_ci clk_set_rate(ddata->clk, ddata->clk_rate); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (of_get_property(np, "mbox-names", NULL)) { 3868c2ecf20Sopenharmony_ci ddata->mbox_client_vq0.dev = dev; 3878c2ecf20Sopenharmony_ci ddata->mbox_client_vq0.tx_done = NULL; 3888c2ecf20Sopenharmony_ci ddata->mbox_client_vq0.tx_block = false; 3898c2ecf20Sopenharmony_ci ddata->mbox_client_vq0.knows_txdone = false; 3908c2ecf20Sopenharmony_ci ddata->mbox_client_vq0.rx_callback = st_rproc_mbox_callback_vq0; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ddata->mbox_client_vq1.dev = dev; 3938c2ecf20Sopenharmony_ci ddata->mbox_client_vq1.tx_done = NULL; 3948c2ecf20Sopenharmony_ci ddata->mbox_client_vq1.tx_block = false; 3958c2ecf20Sopenharmony_ci ddata->mbox_client_vq1.knows_txdone = false; 3968c2ecf20Sopenharmony_ci ddata->mbox_client_vq1.rx_callback = st_rproc_mbox_callback_vq1; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * To control a co-processor without IPC mechanism. 4008c2ecf20Sopenharmony_ci * This driver can be used without mbox and rpmsg. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_rx"); 4038c2ecf20Sopenharmony_ci if (IS_ERR(chan)) { 4048c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "failed to request mbox chan 0\n"); 4058c2ecf20Sopenharmony_ci ret = PTR_ERR(chan); 4068c2ecf20Sopenharmony_ci goto free_clk; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_RX] = chan; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_tx"); 4118c2ecf20Sopenharmony_ci if (IS_ERR(chan)) { 4128c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "failed to request mbox chan 0\n"); 4138c2ecf20Sopenharmony_ci ret = PTR_ERR(chan); 4148c2ecf20Sopenharmony_ci goto free_mbox; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_TX] = chan; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_rx"); 4198c2ecf20Sopenharmony_ci if (IS_ERR(chan)) { 4208c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "failed to request mbox chan 1\n"); 4218c2ecf20Sopenharmony_ci ret = PTR_ERR(chan); 4228c2ecf20Sopenharmony_ci goto free_mbox; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_RX] = chan; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_tx"); 4278c2ecf20Sopenharmony_ci if (IS_ERR(chan)) { 4288c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "failed to request mbox chan 1\n"); 4298c2ecf20Sopenharmony_ci ret = PTR_ERR(chan); 4308c2ecf20Sopenharmony_ci goto free_mbox; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_TX] = chan; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci ret = rproc_add(rproc); 4368c2ecf20Sopenharmony_ci if (ret) 4378c2ecf20Sopenharmony_ci goto free_mbox; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cifree_mbox: 4428c2ecf20Sopenharmony_ci for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) 4438c2ecf20Sopenharmony_ci mbox_free_channel(ddata->mbox_chan[i]); 4448c2ecf20Sopenharmony_cifree_clk: 4458c2ecf20Sopenharmony_ci clk_unprepare(ddata->clk); 4468c2ecf20Sopenharmony_cifree_rproc: 4478c2ecf20Sopenharmony_ci rproc_free(rproc); 4488c2ecf20Sopenharmony_ci return ret; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int st_rproc_remove(struct platform_device *pdev) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 4548c2ecf20Sopenharmony_ci struct st_rproc *ddata = rproc->priv; 4558c2ecf20Sopenharmony_ci int i; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci rproc_del(rproc); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci clk_disable_unprepare(ddata->clk); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) 4628c2ecf20Sopenharmony_ci mbox_free_channel(ddata->mbox_chan[i]); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci rproc_free(rproc); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic struct platform_driver st_rproc_driver = { 4708c2ecf20Sopenharmony_ci .probe = st_rproc_probe, 4718c2ecf20Sopenharmony_ci .remove = st_rproc_remove, 4728c2ecf20Sopenharmony_ci .driver = { 4738c2ecf20Sopenharmony_ci .name = "st-rproc", 4748c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(st_rproc_match), 4758c2ecf20Sopenharmony_ci }, 4768c2ecf20Sopenharmony_ci}; 4778c2ecf20Sopenharmony_cimodule_platform_driver(st_rproc_driver); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST Remote Processor Control Driver"); 4808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>"); 4818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 482