1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Ingenic JZ47xx remoteproc driver 4 * Copyright 2019, Paul Cercueil <paul@crapouillou.net> 5 */ 6 7 #include <linux/bitops.h> 8 #include <linux/clk.h> 9 #include <linux/err.h> 10 #include <linux/interrupt.h> 11 #include <linux/io.h> 12 #include <linux/module.h> 13 #include <linux/platform_device.h> 14 #include <linux/pm_runtime.h> 15 #include <linux/remoteproc.h> 16 17 #include "remoteproc_internal.h" 18 19 #define REG_AUX_CTRL 0x0 20 #define REG_AUX_MSG_ACK 0x10 21 #define REG_AUX_MSG 0x14 22 #define REG_CORE_MSG_ACK 0x18 23 #define REG_CORE_MSG 0x1C 24 25 #define AUX_CTRL_SLEEP BIT(31) 26 #define AUX_CTRL_MSG_IRQ_EN BIT(3) 27 #define AUX_CTRL_NMI_RESETS BIT(2) 28 #define AUX_CTRL_NMI BIT(1) 29 #define AUX_CTRL_SW_RESET BIT(0) 30 31 struct vpu_mem_map { 32 const char *name; 33 unsigned int da; 34 }; 35 36 struct vpu_mem_info { 37 const struct vpu_mem_map *map; 38 unsigned long len; 39 void __iomem *base; 40 }; 41 42 static const struct vpu_mem_map vpu_mem_map[] = { 43 { "tcsm0", 0x132b0000 }, 44 { "tcsm1", 0xf4000000 }, 45 { "sram", 0x132f0000 }, 46 }; 47 48 /** 49 * struct vpu - Ingenic VPU remoteproc private structure 50 * @irq: interrupt number 51 * @clks: pointers to the VPU and AUX clocks 52 * @aux_base: raw pointer to the AUX interface registers 53 * @mem_info: array of struct vpu_mem_info, which contain the mapping info of 54 * each of the external memories 55 * @dev: private pointer to the device 56 */ 57 struct vpu { 58 int irq; 59 struct clk_bulk_data clks[2]; 60 void __iomem *aux_base; 61 struct vpu_mem_info mem_info[ARRAY_SIZE(vpu_mem_map)]; 62 struct device *dev; 63 }; 64 65 static int ingenic_rproc_start(struct rproc *rproc) 66 { 67 struct vpu *vpu = rproc->priv; 68 u32 ctrl; 69 70 enable_irq(vpu->irq); 71 72 /* Reset the AUX and enable message IRQ */ 73 ctrl = AUX_CTRL_NMI_RESETS | AUX_CTRL_NMI | AUX_CTRL_MSG_IRQ_EN; 74 writel(ctrl, vpu->aux_base + REG_AUX_CTRL); 75 76 return 0; 77 } 78 79 static int ingenic_rproc_stop(struct rproc *rproc) 80 { 81 struct vpu *vpu = rproc->priv; 82 83 disable_irq(vpu->irq); 84 85 /* Keep AUX in reset mode */ 86 writel(AUX_CTRL_SW_RESET, vpu->aux_base + REG_AUX_CTRL); 87 88 return 0; 89 } 90 91 static void ingenic_rproc_kick(struct rproc *rproc, int vqid) 92 { 93 struct vpu *vpu = rproc->priv; 94 95 writel(vqid, vpu->aux_base + REG_CORE_MSG); 96 } 97 98 static void *ingenic_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) 99 { 100 struct vpu *vpu = rproc->priv; 101 void __iomem *va = NULL; 102 unsigned int i; 103 104 for (i = 0; i < ARRAY_SIZE(vpu_mem_map); i++) { 105 const struct vpu_mem_info *info = &vpu->mem_info[i]; 106 const struct vpu_mem_map *map = info->map; 107 108 if (da >= map->da && (da + len) < (map->da + info->len)) { 109 va = info->base + (da - map->da); 110 break; 111 } 112 } 113 114 return (__force void *)va; 115 } 116 117 static struct rproc_ops ingenic_rproc_ops = { 118 .start = ingenic_rproc_start, 119 .stop = ingenic_rproc_stop, 120 .kick = ingenic_rproc_kick, 121 .da_to_va = ingenic_rproc_da_to_va, 122 }; 123 124 static irqreturn_t vpu_interrupt(int irq, void *data) 125 { 126 struct rproc *rproc = data; 127 struct vpu *vpu = rproc->priv; 128 u32 vring; 129 130 vring = readl(vpu->aux_base + REG_AUX_MSG); 131 132 /* Ack the interrupt */ 133 writel(0, vpu->aux_base + REG_AUX_MSG_ACK); 134 135 return rproc_vq_interrupt(rproc, vring); 136 } 137 138 static void ingenic_rproc_disable_clks(void *data) 139 { 140 struct vpu *vpu = data; 141 142 pm_runtime_resume(vpu->dev); 143 pm_runtime_disable(vpu->dev); 144 145 clk_bulk_disable_unprepare(ARRAY_SIZE(vpu->clks), vpu->clks); 146 } 147 148 static int ingenic_rproc_probe(struct platform_device *pdev) 149 { 150 struct device *dev = &pdev->dev; 151 struct resource *mem; 152 struct rproc *rproc; 153 struct vpu *vpu; 154 unsigned int i; 155 int ret; 156 157 rproc = devm_rproc_alloc(dev, "ingenic-vpu", 158 &ingenic_rproc_ops, NULL, sizeof(*vpu)); 159 if (!rproc) 160 return -ENOMEM; 161 162 vpu = rproc->priv; 163 vpu->dev = &pdev->dev; 164 platform_set_drvdata(pdev, vpu); 165 166 mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aux"); 167 vpu->aux_base = devm_ioremap_resource(dev, mem); 168 if (IS_ERR(vpu->aux_base)) { 169 dev_err(dev, "Failed to ioremap\n"); 170 return PTR_ERR(vpu->aux_base); 171 } 172 173 for (i = 0; i < ARRAY_SIZE(vpu_mem_map); i++) { 174 mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, 175 vpu_mem_map[i].name); 176 177 vpu->mem_info[i].base = devm_ioremap_resource(dev, mem); 178 if (IS_ERR(vpu->mem_info[i].base)) { 179 ret = PTR_ERR(vpu->mem_info[i].base); 180 dev_err(dev, "Failed to ioremap\n"); 181 return ret; 182 } 183 184 vpu->mem_info[i].len = resource_size(mem); 185 vpu->mem_info[i].map = &vpu_mem_map[i]; 186 } 187 188 vpu->clks[0].id = "vpu"; 189 vpu->clks[1].id = "aux"; 190 191 ret = devm_clk_bulk_get(dev, ARRAY_SIZE(vpu->clks), vpu->clks); 192 if (ret) { 193 dev_err(dev, "Failed to get clocks\n"); 194 return ret; 195 } 196 197 vpu->irq = platform_get_irq(pdev, 0); 198 if (vpu->irq < 0) 199 return vpu->irq; 200 201 ret = devm_request_irq(dev, vpu->irq, vpu_interrupt, 0, "VPU", rproc); 202 if (ret < 0) { 203 dev_err(dev, "Failed to request IRQ\n"); 204 return ret; 205 } 206 207 disable_irq(vpu->irq); 208 209 /* The clocks must be enabled for the firmware to be loaded in TCSM */ 210 ret = clk_bulk_prepare_enable(ARRAY_SIZE(vpu->clks), vpu->clks); 211 if (ret) { 212 dev_err(dev, "Unable to start clocks\n"); 213 return ret; 214 } 215 216 pm_runtime_irq_safe(dev); 217 pm_runtime_set_active(dev); 218 pm_runtime_enable(dev); 219 pm_runtime_get_sync(dev); 220 pm_runtime_use_autosuspend(dev); 221 222 ret = devm_add_action_or_reset(dev, ingenic_rproc_disable_clks, vpu); 223 if (ret) { 224 dev_err(dev, "Unable to register action\n"); 225 goto out_pm_put; 226 } 227 228 ret = devm_rproc_add(dev, rproc); 229 if (ret) { 230 dev_err(dev, "Failed to register remote processor\n"); 231 goto out_pm_put; 232 } 233 234 out_pm_put: 235 pm_runtime_put_autosuspend(dev); 236 237 return ret; 238 } 239 240 static const struct of_device_id ingenic_rproc_of_matches[] = { 241 { .compatible = "ingenic,jz4770-vpu-rproc", }, 242 {} 243 }; 244 MODULE_DEVICE_TABLE(of, ingenic_rproc_of_matches); 245 246 static int __maybe_unused ingenic_rproc_suspend(struct device *dev) 247 { 248 struct vpu *vpu = dev_get_drvdata(dev); 249 250 clk_bulk_disable(ARRAY_SIZE(vpu->clks), vpu->clks); 251 252 return 0; 253 } 254 255 static int __maybe_unused ingenic_rproc_resume(struct device *dev) 256 { 257 struct vpu *vpu = dev_get_drvdata(dev); 258 259 return clk_bulk_enable(ARRAY_SIZE(vpu->clks), vpu->clks); 260 } 261 262 static const struct dev_pm_ops __maybe_unused ingenic_rproc_pm = { 263 SET_RUNTIME_PM_OPS(ingenic_rproc_suspend, ingenic_rproc_resume, NULL) 264 }; 265 266 static struct platform_driver ingenic_rproc_driver = { 267 .probe = ingenic_rproc_probe, 268 .driver = { 269 .name = "ingenic-vpu", 270 #ifdef CONFIG_PM 271 .pm = &ingenic_rproc_pm, 272 #endif 273 .of_match_table = ingenic_rproc_of_matches, 274 }, 275 }; 276 module_platform_driver(ingenic_rproc_driver); 277 278 MODULE_LICENSE("GPL"); 279 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 280 MODULE_DESCRIPTION("Ingenic JZ47xx Remote Processor control driver"); 281