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