Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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