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 }; 208 209 /* 210 * Firmware handler operations: sanity, boot address, load ... 211 */ 212 213 static struct resource_table empty_rsc_tbl = { 214 .ver = 1, 215 .num = 0, 216 }; 217 218 static struct resource_table *slim_rproc_find_rsc_table(struct rproc *rproc, 219 const struct firmware *fw, 220 int *tablesz) 221 { 222 *tablesz = sizeof(empty_rsc_tbl); 223 return &empty_rsc_tbl; 224 } 225 226 static struct rproc_fw_ops slim_rproc_fw_ops = { 227 .find_rsc_table = slim_rproc_find_rsc_table, 228 }; 229 230 /** 231 * st_slim_rproc_alloc() - allocate and initialise slim rproc 232 * @pdev: Pointer to the platform_device struct 233 * @fw_name: Name of firmware for rproc to use 234 * 235 * Function for allocating and initialising a slim rproc for use by 236 * device drivers whose IP is based around the SLIM core. It 237 * obtains and enables any clocks required by the SLIM core and also 238 * ioremaps the various IO. 239 * 240 * Returns st_slim_rproc pointer or PTR_ERR() on error. 241 */ 242 243 struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev, 244 char *fw_name) 245 { 246 struct device *dev = &pdev->dev; 247 struct st_slim_rproc *slim_rproc; 248 struct device_node *np = dev->of_node; 249 struct rproc *rproc; 250 struct resource *res; 251 int err, i; 252 const struct rproc_fw_ops *elf_ops; 253 254 if (!fw_name) 255 return ERR_PTR(-EINVAL); 256 257 if (!of_device_is_compatible(np, "st,slim-rproc")) 258 return ERR_PTR(-EINVAL); 259 260 rproc = rproc_alloc(dev, np->name, &slim_rproc_ops, 261 fw_name, sizeof(*slim_rproc)); 262 if (!rproc) 263 return ERR_PTR(-ENOMEM); 264 265 rproc->has_iommu = false; 266 267 slim_rproc = rproc->priv; 268 slim_rproc->rproc = rproc; 269 270 elf_ops = rproc->fw_ops; 271 /* Use some generic elf ops */ 272 slim_rproc_fw_ops.load = elf_ops->load; 273 slim_rproc_fw_ops.sanity_check = elf_ops->sanity_check; 274 275 rproc->fw_ops = &slim_rproc_fw_ops; 276 277 /* get imem and dmem */ 278 for (i = 0; i < ARRAY_SIZE(mem_names); i++) { 279 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 280 mem_names[i]); 281 282 slim_rproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res); 283 if (IS_ERR(slim_rproc->mem[i].cpu_addr)) { 284 dev_err(&pdev->dev, "devm_ioremap_resource failed\n"); 285 err = PTR_ERR(slim_rproc->mem[i].cpu_addr); 286 goto err; 287 } 288 slim_rproc->mem[i].bus_addr = res->start; 289 slim_rproc->mem[i].size = resource_size(res); 290 } 291 292 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slimcore"); 293 slim_rproc->slimcore = devm_ioremap_resource(dev, res); 294 if (IS_ERR(slim_rproc->slimcore)) { 295 dev_err(&pdev->dev, "failed to ioremap slimcore IO\n"); 296 err = PTR_ERR(slim_rproc->slimcore); 297 goto err; 298 } 299 300 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "peripherals"); 301 slim_rproc->peri = devm_ioremap_resource(dev, res); 302 if (IS_ERR(slim_rproc->peri)) { 303 dev_err(&pdev->dev, "failed to ioremap peripherals IO\n"); 304 err = PTR_ERR(slim_rproc->peri); 305 goto err; 306 } 307 308 err = slim_clk_get(slim_rproc, dev); 309 if (err) 310 goto err; 311 312 err = slim_clk_enable(slim_rproc); 313 if (err) { 314 dev_err(dev, "Failed to enable clocks\n"); 315 goto err_clk_put; 316 } 317 318 /* Register as a remoteproc device */ 319 err = rproc_add(rproc); 320 if (err) { 321 dev_err(dev, "registration of slim remoteproc failed\n"); 322 goto err_clk_dis; 323 } 324 325 return slim_rproc; 326 327 err_clk_dis: 328 slim_clk_disable(slim_rproc); 329 err_clk_put: 330 for (i = 0; i < ST_SLIM_MAX_CLK && slim_rproc->clks[i]; i++) 331 clk_put(slim_rproc->clks[i]); 332 err: 333 rproc_free(rproc); 334 return ERR_PTR(err); 335 } 336 EXPORT_SYMBOL(st_slim_rproc_alloc); 337 338 /** 339 * st_slim_rproc_put() - put slim rproc resources 340 * @slim_rproc: Pointer to the st_slim_rproc struct 341 * 342 * Function for calling respective _put() functions on slim_rproc resources. 343 * 344 */ 345 void st_slim_rproc_put(struct st_slim_rproc *slim_rproc) 346 { 347 int clk; 348 349 if (!slim_rproc) 350 return; 351 352 slim_clk_disable(slim_rproc); 353 354 for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) 355 clk_put(slim_rproc->clks[clk]); 356 357 rproc_del(slim_rproc->rproc); 358 rproc_free(slim_rproc->rproc); 359 } 360 EXPORT_SYMBOL(st_slim_rproc_put); 361 362 MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); 363 MODULE_DESCRIPTION("STMicroelectronics SLIM core rproc driver"); 364 MODULE_LICENSE("GPL v2"); 365