333 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * SLIM core rproc driver4 *5 * Copyright (C) 2016 STMicroelectronics6 *7 * Author: Peter Griffin <peter.griffin@linaro.org>8 */9 10#include <linux/clk.h>11#include <linux/err.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/remoteproc.h>17#include <linux/remoteproc/st_slim_rproc.h>18#include "remoteproc_internal.h"19 20/* SLIM core registers */21#define SLIM_ID_OFST 0x022#define SLIM_VER_OFST 0x423 24#define SLIM_EN_OFST 0x825#define SLIM_EN_RUN BIT(0)26 27#define SLIM_CLK_GATE_OFST 0xC28#define SLIM_CLK_GATE_DIS BIT(0)29#define SLIM_CLK_GATE_RESET BIT(2)30 31#define SLIM_SLIM_PC_OFST 0x2032 33/* DMEM registers */34#define SLIM_REV_ID_OFST 0x035#define SLIM_REV_ID_MIN_MASK GENMASK(15, 8)36#define SLIM_REV_ID_MIN(id) ((id & SLIM_REV_ID_MIN_MASK) >> 8)37#define SLIM_REV_ID_MAJ_MASK GENMASK(23, 16)38#define SLIM_REV_ID_MAJ(id) ((id & SLIM_REV_ID_MAJ_MASK) >> 16)39 40 41/* peripherals registers */42#define SLIM_STBUS_SYNC_OFST 0xF8843#define SLIM_STBUS_SYNC_DIS BIT(0)44 45#define SLIM_INT_SET_OFST 0xFD446#define SLIM_INT_CLR_OFST 0xFD847#define SLIM_INT_MASK_OFST 0xFDC48 49#define SLIM_CMD_CLR_OFST 0xFC850#define SLIM_CMD_MASK_OFST 0xFCC51 52static const char *mem_names[ST_SLIM_MEM_MAX] = {53 [ST_SLIM_DMEM] = "dmem",54 [ST_SLIM_IMEM] = "imem",55};56 57static int slim_clk_get(struct st_slim_rproc *slim_rproc, struct device *dev)58{59 int clk, err;60 61 for (clk = 0; clk < ST_SLIM_MAX_CLK; clk++) {62 slim_rproc->clks[clk] = of_clk_get(dev->of_node, clk);63 if (IS_ERR(slim_rproc->clks[clk])) {64 err = PTR_ERR(slim_rproc->clks[clk]);65 if (err == -EPROBE_DEFER)66 goto err_put_clks;67 slim_rproc->clks[clk] = NULL;68 break;69 }70 }71 72 return 0;73 74err_put_clks:75 while (--clk >= 0)76 clk_put(slim_rproc->clks[clk]);77 78 return err;79}80 81static void slim_clk_disable(struct st_slim_rproc *slim_rproc)82{83 int clk;84 85 for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)86 clk_disable_unprepare(slim_rproc->clks[clk]);87}88 89static int slim_clk_enable(struct st_slim_rproc *slim_rproc)90{91 int clk, ret;92 93 for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) {94 ret = clk_prepare_enable(slim_rproc->clks[clk]);95 if (ret)96 goto err_disable_clks;97 }98 99 return 0;100 101err_disable_clks:102 while (--clk >= 0)103 clk_disable_unprepare(slim_rproc->clks[clk]);104 105 return ret;106}107 108/*109 * Remoteproc slim specific device handlers110 */111static int slim_rproc_start(struct rproc *rproc)112{113 struct device *dev = &rproc->dev;114 struct st_slim_rproc *slim_rproc = rproc->priv;115 unsigned long hw_id, hw_ver, fw_rev;116 u32 val;117 118 /* disable CPU pipeline clock & reset CPU pipeline */119 val = SLIM_CLK_GATE_DIS | SLIM_CLK_GATE_RESET;120 writel(val, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);121 122 /* disable SLIM core STBus sync */123 writel(SLIM_STBUS_SYNC_DIS, slim_rproc->peri + SLIM_STBUS_SYNC_OFST);124 125 /* enable cpu pipeline clock */126 writel(!SLIM_CLK_GATE_DIS,127 slim_rproc->slimcore + SLIM_CLK_GATE_OFST);128 129 /* clear int & cmd mailbox */130 writel(~0U, slim_rproc->peri + SLIM_INT_CLR_OFST);131 writel(~0U, slim_rproc->peri + SLIM_CMD_CLR_OFST);132 133 /* enable all channels cmd & int */134 writel(~0U, slim_rproc->peri + SLIM_INT_MASK_OFST);135 writel(~0U, slim_rproc->peri + SLIM_CMD_MASK_OFST);136 137 /* enable cpu */138 writel(SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);139 140 hw_id = readl_relaxed(slim_rproc->slimcore + SLIM_ID_OFST);141 hw_ver = readl_relaxed(slim_rproc->slimcore + SLIM_VER_OFST);142 143 fw_rev = readl(slim_rproc->mem[ST_SLIM_DMEM].cpu_addr +144 SLIM_REV_ID_OFST);145 146 dev_info(dev, "fw rev:%ld.%ld on SLIM %ld.%ld\n",147 SLIM_REV_ID_MAJ(fw_rev), SLIM_REV_ID_MIN(fw_rev),148 hw_id, hw_ver);149 150 return 0;151}152 153static int slim_rproc_stop(struct rproc *rproc)154{155 struct st_slim_rproc *slim_rproc = rproc->priv;156 u32 val;157 158 /* mask all (cmd & int) channels */159 writel(0UL, slim_rproc->peri + SLIM_INT_MASK_OFST);160 writel(0UL, slim_rproc->peri + SLIM_CMD_MASK_OFST);161 162 /* disable cpu pipeline clock */163 writel(SLIM_CLK_GATE_DIS, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);164 165 writel(!SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);166 167 val = readl(slim_rproc->slimcore + SLIM_EN_OFST);168 if (val & SLIM_EN_RUN)169 dev_warn(&rproc->dev, "Failed to disable SLIM");170 171 dev_dbg(&rproc->dev, "slim stopped\n");172 173 return 0;174}175 176static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)177{178 struct st_slim_rproc *slim_rproc = rproc->priv;179 void *va = NULL;180 int i;181 182 for (i = 0; i < ST_SLIM_MEM_MAX; i++) {183 if (da != slim_rproc->mem[i].bus_addr)184 continue;185 186 if (len <= slim_rproc->mem[i].size) {187 /* __force to make sparse happy with type conversion */188 va = (__force void *)slim_rproc->mem[i].cpu_addr;189 break;190 }191 }192 193 dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%pK\n",194 da, len, va);195 196 return va;197}198 199static const struct rproc_ops slim_rproc_ops = {200 .start = slim_rproc_start,201 .stop = slim_rproc_stop,202 .da_to_va = slim_rproc_da_to_va,203 .get_boot_addr = rproc_elf_get_boot_addr,204 .load = rproc_elf_load_segments,205 .sanity_check = rproc_elf_sanity_check,206};207 208/**209 * st_slim_rproc_alloc() - allocate and initialise slim rproc210 * @pdev: Pointer to the platform_device struct211 * @fw_name: Name of firmware for rproc to use212 *213 * Function for allocating and initialising a slim rproc for use by214 * device drivers whose IP is based around the SLIM core. It215 * obtains and enables any clocks required by the SLIM core and also216 * ioremaps the various IO.217 *218 * Return: st_slim_rproc pointer or PTR_ERR() on error.219 */220 221struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev,222 char *fw_name)223{224 struct device *dev = &pdev->dev;225 struct st_slim_rproc *slim_rproc;226 struct device_node *np = dev->of_node;227 struct rproc *rproc;228 struct resource *res;229 int err, i;230 231 if (!fw_name)232 return ERR_PTR(-EINVAL);233 234 if (!of_device_is_compatible(np, "st,slim-rproc"))235 return ERR_PTR(-EINVAL);236 237 rproc = rproc_alloc(dev, np->name, &slim_rproc_ops,238 fw_name, sizeof(*slim_rproc));239 if (!rproc)240 return ERR_PTR(-ENOMEM);241 242 rproc->has_iommu = false;243 244 slim_rproc = rproc->priv;245 slim_rproc->rproc = rproc;246 247 /* get imem and dmem */248 for (i = 0; i < ARRAY_SIZE(mem_names); i++) {249 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,250 mem_names[i]);251 252 slim_rproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res);253 if (IS_ERR(slim_rproc->mem[i].cpu_addr)) {254 dev_err(&pdev->dev, "devm_ioremap_resource failed\n");255 err = PTR_ERR(slim_rproc->mem[i].cpu_addr);256 goto err;257 }258 slim_rproc->mem[i].bus_addr = res->start;259 slim_rproc->mem[i].size = resource_size(res);260 }261 262 slim_rproc->slimcore = devm_platform_ioremap_resource_byname(pdev, "slimcore");263 if (IS_ERR(slim_rproc->slimcore)) {264 dev_err(&pdev->dev, "failed to ioremap slimcore IO\n");265 err = PTR_ERR(slim_rproc->slimcore);266 goto err;267 }268 269 slim_rproc->peri = devm_platform_ioremap_resource_byname(pdev, "peripherals");270 if (IS_ERR(slim_rproc->peri)) {271 dev_err(&pdev->dev, "failed to ioremap peripherals IO\n");272 err = PTR_ERR(slim_rproc->peri);273 goto err;274 }275 276 err = slim_clk_get(slim_rproc, dev);277 if (err)278 goto err;279 280 err = slim_clk_enable(slim_rproc);281 if (err) {282 dev_err(dev, "Failed to enable clocks\n");283 goto err_clk_put;284 }285 286 /* Register as a remoteproc device */287 err = rproc_add(rproc);288 if (err) {289 dev_err(dev, "registration of slim remoteproc failed\n");290 goto err_clk_dis;291 }292 293 return slim_rproc;294 295err_clk_dis:296 slim_clk_disable(slim_rproc);297err_clk_put:298 for (i = 0; i < ST_SLIM_MAX_CLK && slim_rproc->clks[i]; i++)299 clk_put(slim_rproc->clks[i]);300err:301 rproc_free(rproc);302 return ERR_PTR(err);303}304EXPORT_SYMBOL(st_slim_rproc_alloc);305 306/**307 * st_slim_rproc_put() - put slim rproc resources308 * @slim_rproc: Pointer to the st_slim_rproc struct309 *310 * Function for calling respective _put() functions on slim_rproc resources.311 *312 */313void st_slim_rproc_put(struct st_slim_rproc *slim_rproc)314{315 int clk;316 317 if (!slim_rproc)318 return;319 320 slim_clk_disable(slim_rproc);321 322 for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)323 clk_put(slim_rproc->clks[clk]);324 325 rproc_del(slim_rproc->rproc);326 rproc_free(slim_rproc->rproc);327}328EXPORT_SYMBOL(st_slim_rproc_put);329 330MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");331MODULE_DESCRIPTION("STMicroelectronics SLIM core rproc driver");332MODULE_LICENSE("GPL v2");333