linux/drivers/remoteproc/ingenic_rproc.c
<<
>>
Prefs
   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
  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 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 */
  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        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
 238static const struct of_device_id ingenic_rproc_of_matches[] = {
 239        { .compatible = "ingenic,jz4770-vpu-rproc", },
 240        {}
 241};
 242MODULE_DEVICE_TABLE(of, ingenic_rproc_of_matches);
 243
 244static 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};
 251module_platform_driver(ingenic_rproc_driver);
 252
 253MODULE_LICENSE("GPL");
 254MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
 255MODULE_DESCRIPTION("Ingenic JZ47xx Remote Processor control driver");
 256