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