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