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