254 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Ingenic JZ47xx remoteproc driver4 * 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 0x019#define REG_AUX_MSG_ACK 0x1020#define REG_AUX_MSG 0x1421#define REG_CORE_MSG_ACK 0x1822#define REG_CORE_MSG 0x1C23 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 30static bool auto_boot;31module_param(auto_boot, bool, 0400);32MODULE_PARM_DESC(auto_boot,33 "Auto-boot the remote processor [default=false]");34 35struct vpu_mem_map {36 const char *name;37 unsigned int da;38};39 40struct vpu_mem_info {41 const struct vpu_mem_map *map;42 unsigned long len;43 void __iomem *base;44};45 46static 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 structure54 * @irq: interrupt number55 * @clks: pointers to the VPU and AUX clocks56 * @aux_base: raw pointer to the AUX interface registers57 * @mem_info: array of struct vpu_mem_info, which contain the mapping info of58 * each of the external memories59 * @dev: private pointer to the device60 */61struct 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 69static 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 82static 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 91static 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 105static 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 117static 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 124static 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 143static 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 152static 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 166static 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 vpu->aux_base = devm_platform_ioremap_resource_byname(pdev, "aux");187 if (IS_ERR(vpu->aux_base)) {188 dev_err(dev, "Failed to ioremap\n");189 return PTR_ERR(vpu->aux_base);190 }191 192 for (i = 0; i < ARRAY_SIZE(vpu_mem_map); i++) {193 mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,194 vpu_mem_map[i].name);195 196 vpu->mem_info[i].base = devm_ioremap_resource(dev, mem);197 if (IS_ERR(vpu->mem_info[i].base)) {198 ret = PTR_ERR(vpu->mem_info[i].base);199 dev_err(dev, "Failed to ioremap\n");200 return ret;201 }202 203 vpu->mem_info[i].len = resource_size(mem);204 vpu->mem_info[i].map = &vpu_mem_map[i];205 }206 207 vpu->clks[0].id = "vpu";208 vpu->clks[1].id = "aux";209 210 ret = devm_clk_bulk_get(dev, ARRAY_SIZE(vpu->clks), vpu->clks);211 if (ret) {212 dev_err(dev, "Failed to get clocks\n");213 return ret;214 }215 216 vpu->irq = platform_get_irq(pdev, 0);217 if (vpu->irq < 0)218 return vpu->irq;219 220 ret = devm_request_irq(dev, vpu->irq, vpu_interrupt, IRQF_NO_AUTOEN,221 "VPU", rproc);222 if (ret < 0) {223 dev_err(dev, "Failed to request IRQ\n");224 return ret;225 }226 227 ret = devm_rproc_add(dev, rproc);228 if (ret) {229 dev_err(dev, "Failed to register remote processor\n");230 return ret;231 }232 233 return 0;234}235 236static const struct of_device_id ingenic_rproc_of_matches[] = {237 { .compatible = "ingenic,jz4770-vpu-rproc", },238 {}239};240MODULE_DEVICE_TABLE(of, ingenic_rproc_of_matches);241 242static struct platform_driver ingenic_rproc_driver = {243 .probe = ingenic_rproc_probe,244 .driver = {245 .name = "ingenic-vpu",246 .of_match_table = ingenic_rproc_of_matches,247 },248};249module_platform_driver(ingenic_rproc_driver);250 251MODULE_LICENSE("GPL");252MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");253MODULE_DESCRIPTION("Ingenic JZ47xx Remote Processor control driver");254