18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TI AMx3 Wakeup M3 Remote Processor driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Texas Instruments, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Dave Gerlach <d-gerlach@ti.com> 88c2ecf20Sopenharmony_ci * Suman Anna <s-anna@ti.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/of_device.h> 168c2ecf20Sopenharmony_ci#include <linux/of_address.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 198c2ecf20Sopenharmony_ci#include <linux/remoteproc.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/platform_data/wkup_m3.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "remoteproc_internal.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define WKUPM3_MEM_MAX 2 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/** 288c2ecf20Sopenharmony_ci * struct wkup_m3_mem - WkupM3 internal memory structure 298c2ecf20Sopenharmony_ci * @cpu_addr: MPU virtual address of the memory region 308c2ecf20Sopenharmony_ci * @bus_addr: Bus address used to access the memory region 318c2ecf20Sopenharmony_ci * @dev_addr: Device address from Wakeup M3 view 328c2ecf20Sopenharmony_ci * @size: Size of the memory region 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistruct wkup_m3_mem { 358c2ecf20Sopenharmony_ci void __iomem *cpu_addr; 368c2ecf20Sopenharmony_ci phys_addr_t bus_addr; 378c2ecf20Sopenharmony_ci u32 dev_addr; 388c2ecf20Sopenharmony_ci size_t size; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/** 428c2ecf20Sopenharmony_ci * struct wkup_m3_rproc - WkupM3 remote processor state 438c2ecf20Sopenharmony_ci * @rproc: rproc handle 448c2ecf20Sopenharmony_ci * @pdev: pointer to platform device 458c2ecf20Sopenharmony_ci * @mem: WkupM3 memory information 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistruct wkup_m3_rproc { 488c2ecf20Sopenharmony_ci struct rproc *rproc; 498c2ecf20Sopenharmony_ci struct platform_device *pdev; 508c2ecf20Sopenharmony_ci struct wkup_m3_mem mem[WKUPM3_MEM_MAX]; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int wkup_m3_rproc_start(struct rproc *rproc) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct wkup_m3_rproc *wkupm3 = rproc->priv; 568c2ecf20Sopenharmony_ci struct platform_device *pdev = wkupm3->pdev; 578c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 588c2ecf20Sopenharmony_ci struct wkup_m3_platform_data *pdata = dev_get_platdata(dev); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (pdata->deassert_reset(pdev, pdata->reset_name)) { 618c2ecf20Sopenharmony_ci dev_err(dev, "Unable to reset wkup_m3!\n"); 628c2ecf20Sopenharmony_ci return -ENODEV; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int wkup_m3_rproc_stop(struct rproc *rproc) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct wkup_m3_rproc *wkupm3 = rproc->priv; 718c2ecf20Sopenharmony_ci struct platform_device *pdev = wkupm3->pdev; 728c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 738c2ecf20Sopenharmony_ci struct wkup_m3_platform_data *pdata = dev_get_platdata(dev); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (pdata->assert_reset(pdev, pdata->reset_name)) { 768c2ecf20Sopenharmony_ci dev_err(dev, "Unable to assert reset of wkup_m3!\n"); 778c2ecf20Sopenharmony_ci return -ENODEV; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct wkup_m3_rproc *wkupm3 = rproc->priv; 868c2ecf20Sopenharmony_ci void *va = NULL; 878c2ecf20Sopenharmony_ci int i; 888c2ecf20Sopenharmony_ci u32 offset; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (len == 0) 918c2ecf20Sopenharmony_ci return NULL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci for (i = 0; i < WKUPM3_MEM_MAX; i++) { 948c2ecf20Sopenharmony_ci if (da >= wkupm3->mem[i].dev_addr && da + len <= 958c2ecf20Sopenharmony_ci wkupm3->mem[i].dev_addr + wkupm3->mem[i].size) { 968c2ecf20Sopenharmony_ci offset = da - wkupm3->mem[i].dev_addr; 978c2ecf20Sopenharmony_ci /* __force to make sparse happy with type conversion */ 988c2ecf20Sopenharmony_ci va = (__force void *)(wkupm3->mem[i].cpu_addr + offset); 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return va; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const struct rproc_ops wkup_m3_rproc_ops = { 1078c2ecf20Sopenharmony_ci .start = wkup_m3_rproc_start, 1088c2ecf20Sopenharmony_ci .stop = wkup_m3_rproc_stop, 1098c2ecf20Sopenharmony_ci .da_to_va = wkup_m3_rproc_da_to_va, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic const struct of_device_id wkup_m3_rproc_of_match[] = { 1138c2ecf20Sopenharmony_ci { .compatible = "ti,am3352-wkup-m3", }, 1148c2ecf20Sopenharmony_ci { .compatible = "ti,am4372-wkup-m3", }, 1158c2ecf20Sopenharmony_ci {}, 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, wkup_m3_rproc_of_match); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int wkup_m3_rproc_probe(struct platform_device *pdev) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1228c2ecf20Sopenharmony_ci struct wkup_m3_platform_data *pdata = dev->platform_data; 1238c2ecf20Sopenharmony_ci /* umem always needs to be processed first */ 1248c2ecf20Sopenharmony_ci const char *mem_names[WKUPM3_MEM_MAX] = { "umem", "dmem" }; 1258c2ecf20Sopenharmony_ci struct wkup_m3_rproc *wkupm3; 1268c2ecf20Sopenharmony_ci const char *fw_name; 1278c2ecf20Sopenharmony_ci struct rproc *rproc; 1288c2ecf20Sopenharmony_ci struct resource *res; 1298c2ecf20Sopenharmony_ci const __be32 *addrp; 1308c2ecf20Sopenharmony_ci u32 l4_offset = 0; 1318c2ecf20Sopenharmony_ci u64 size; 1328c2ecf20Sopenharmony_ci int ret; 1338c2ecf20Sopenharmony_ci int i; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!(pdata && pdata->deassert_reset && pdata->assert_reset && 1368c2ecf20Sopenharmony_ci pdata->reset_name)) { 1378c2ecf20Sopenharmony_ci dev_err(dev, "Platform data missing!\n"); 1388c2ecf20Sopenharmony_ci return -ENODEV; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ret = of_property_read_string(dev->of_node, "ti,pm-firmware", 1428c2ecf20Sopenharmony_ci &fw_name); 1438c2ecf20Sopenharmony_ci if (ret) { 1448c2ecf20Sopenharmony_ci dev_err(dev, "No firmware filename given\n"); 1458c2ecf20Sopenharmony_ci return -ENODEV; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 1498c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 1508c2ecf20Sopenharmony_ci if (ret < 0) { 1518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); 1528c2ecf20Sopenharmony_ci goto err; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops, 1568c2ecf20Sopenharmony_ci fw_name, sizeof(*wkupm3)); 1578c2ecf20Sopenharmony_ci if (!rproc) { 1588c2ecf20Sopenharmony_ci ret = -ENOMEM; 1598c2ecf20Sopenharmony_ci goto err; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci rproc->auto_boot = false; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci wkupm3 = rproc->priv; 1658c2ecf20Sopenharmony_ci wkupm3->rproc = rproc; 1668c2ecf20Sopenharmony_ci wkupm3->pdev = pdev; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mem_names); i++) { 1698c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 1708c2ecf20Sopenharmony_ci mem_names[i]); 1718c2ecf20Sopenharmony_ci wkupm3->mem[i].cpu_addr = devm_ioremap_resource(dev, res); 1728c2ecf20Sopenharmony_ci if (IS_ERR(wkupm3->mem[i].cpu_addr)) { 1738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "devm_ioremap_resource failed for resource %d\n", 1748c2ecf20Sopenharmony_ci i); 1758c2ecf20Sopenharmony_ci ret = PTR_ERR(wkupm3->mem[i].cpu_addr); 1768c2ecf20Sopenharmony_ci goto err; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci wkupm3->mem[i].bus_addr = res->start; 1798c2ecf20Sopenharmony_ci wkupm3->mem[i].size = resource_size(res); 1808c2ecf20Sopenharmony_ci addrp = of_get_address(dev->of_node, i, &size, NULL); 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * The wkupm3 has umem at address 0 in its view, so the device 1838c2ecf20Sopenharmony_ci * addresses for each memory region is computed as a relative 1848c2ecf20Sopenharmony_ci * offset of the bus address for umem, and therefore needs to be 1858c2ecf20Sopenharmony_ci * processed first. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci if (!strcmp(mem_names[i], "umem")) 1888c2ecf20Sopenharmony_ci l4_offset = be32_to_cpu(*addrp); 1898c2ecf20Sopenharmony_ci wkupm3->mem[i].dev_addr = be32_to_cpu(*addrp) - l4_offset; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci dev_set_drvdata(dev, rproc); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ret = rproc_add(rproc); 1958c2ecf20Sopenharmony_ci if (ret) { 1968c2ecf20Sopenharmony_ci dev_err(dev, "rproc_add failed\n"); 1978c2ecf20Sopenharmony_ci goto err_put_rproc; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cierr_put_rproc: 2038c2ecf20Sopenharmony_ci rproc_free(rproc); 2048c2ecf20Sopenharmony_cierr: 2058c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 2068c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int wkup_m3_rproc_remove(struct platform_device *pdev) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct rproc *rproc = platform_get_drvdata(pdev); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci rproc_del(rproc); 2158c2ecf20Sopenharmony_ci rproc_free(rproc); 2168c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 2178c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2238c2ecf20Sopenharmony_cistatic int wkup_m3_rpm_suspend(struct device *dev) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci return -EBUSY; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int wkup_m3_rpm_resume(struct device *dev) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci#endif 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic const struct dev_pm_ops wkup_m3_rproc_pm_ops = { 2358c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL) 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct platform_driver wkup_m3_rproc_driver = { 2398c2ecf20Sopenharmony_ci .probe = wkup_m3_rproc_probe, 2408c2ecf20Sopenharmony_ci .remove = wkup_m3_rproc_remove, 2418c2ecf20Sopenharmony_ci .driver = { 2428c2ecf20Sopenharmony_ci .name = "wkup_m3_rproc", 2438c2ecf20Sopenharmony_ci .of_match_table = wkup_m3_rproc_of_match, 2448c2ecf20Sopenharmony_ci .pm = &wkup_m3_rproc_pm_ops, 2458c2ecf20Sopenharmony_ci }, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cimodule_platform_driver(wkup_m3_rproc_driver); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI Wakeup M3 remote processor control driver"); 2528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); 253