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 57 static const char *mem_names[ST_SLIM_MEM_MAX] = { 58 [ST_SLIM_DMEM] = "dmem", 59 [ST_SLIM_IMEM] = "imem", 60 }; 61 62 static 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 79 err_put_clks: 80 while (--clk >= 0) 81 clk_put(slim_rproc->clks[clk]); 82 83 return err; 84 } 85 86 static 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 94 static 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 106 err_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 */ 116 static 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 158 static 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 181 static 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 203 static 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 225 struct 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 301 err_clk_dis: 302 slim_clk_disable(slim_rproc); 303 err_clk_put: 304 for (i = 0; i < ST_SLIM_MAX_CLK && slim_rproc->clks[i]; i++) 305 clk_put(slim_rproc->clks[i]); 306 err: 307 rproc_free(rproc); 308 return ERR_PTR(err); 309 } 310 EXPORT_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 */ 319 void 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 } 334 EXPORT_SYMBOL(st_slim_rproc_put); 335 336 MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); 337 MODULE_DESCRIPTION("STMicroelectronics SLIM core rproc driver"); 338 MODULE_LICENSE("GPL v2"); 339