Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Ingenic JZ47xx remoteproc driver
0004  * Copyright 2019, Paul Cercueil <paul@crapouillou.net>
0005  */
0006 
0007 #include <linux/bitops.h>
0008 #include <linux/clk.h>
0009 #include <linux/err.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/io.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/remoteproc.h>
0015 
0016 #include "remoteproc_internal.h"
0017 
0018 #define REG_AUX_CTRL        0x0
0019 #define REG_AUX_MSG_ACK     0x10
0020 #define REG_AUX_MSG     0x14
0021 #define REG_CORE_MSG_ACK    0x18
0022 #define REG_CORE_MSG        0x1C
0023 
0024 #define AUX_CTRL_SLEEP      BIT(31)
0025 #define AUX_CTRL_MSG_IRQ_EN BIT(3)
0026 #define AUX_CTRL_NMI_RESETS BIT(2)
0027 #define AUX_CTRL_NMI        BIT(1)
0028 #define AUX_CTRL_SW_RESET   BIT(0)
0029 
0030 static bool auto_boot;
0031 module_param(auto_boot, bool, 0400);
0032 MODULE_PARM_DESC(auto_boot,
0033          "Auto-boot the remote processor [default=false]");
0034 
0035 struct vpu_mem_map {
0036     const char *name;
0037     unsigned int da;
0038 };
0039 
0040 struct vpu_mem_info {
0041     const struct vpu_mem_map *map;
0042     unsigned long len;
0043     void __iomem *base;
0044 };
0045 
0046 static const struct vpu_mem_map vpu_mem_map[] = {
0047     { "tcsm0", 0x132b0000 },
0048     { "tcsm1", 0xf4000000 },
0049     { "sram",  0x132f0000 },
0050 };
0051 
0052 /**
0053  * struct vpu - Ingenic VPU remoteproc private structure
0054  * @irq: interrupt number
0055  * @clks: pointers to the VPU and AUX clocks
0056  * @aux_base: raw pointer to the AUX interface registers
0057  * @mem_info: array of struct vpu_mem_info, which contain the mapping info of
0058  *            each of the external memories
0059  * @dev: private pointer to the device
0060  */
0061 struct vpu {
0062     int irq;
0063     struct clk_bulk_data clks[2];
0064     void __iomem *aux_base;
0065     struct vpu_mem_info mem_info[ARRAY_SIZE(vpu_mem_map)];
0066     struct device *dev;
0067 };
0068 
0069 static int ingenic_rproc_prepare(struct rproc *rproc)
0070 {
0071     struct vpu *vpu = rproc->priv;
0072     int ret;
0073 
0074     /* The clocks must be enabled for the firmware to be loaded in TCSM */
0075     ret = clk_bulk_prepare_enable(ARRAY_SIZE(vpu->clks), vpu->clks);
0076     if (ret)
0077         dev_err(vpu->dev, "Unable to start clocks: %d\n", ret);
0078 
0079     return ret;
0080 }
0081 
0082 static int ingenic_rproc_unprepare(struct rproc *rproc)
0083 {
0084     struct vpu *vpu = rproc->priv;
0085 
0086     clk_bulk_disable_unprepare(ARRAY_SIZE(vpu->clks), vpu->clks);
0087 
0088     return 0;
0089 }
0090 
0091 static int ingenic_rproc_start(struct rproc *rproc)
0092 {
0093     struct vpu *vpu = rproc->priv;
0094     u32 ctrl;
0095 
0096     enable_irq(vpu->irq);
0097 
0098     /* Reset the AUX and enable message IRQ */
0099     ctrl = AUX_CTRL_NMI_RESETS | AUX_CTRL_NMI | AUX_CTRL_MSG_IRQ_EN;
0100     writel(ctrl, vpu->aux_base + REG_AUX_CTRL);
0101 
0102     return 0;
0103 }
0104 
0105 static int ingenic_rproc_stop(struct rproc *rproc)
0106 {
0107     struct vpu *vpu = rproc->priv;
0108 
0109     disable_irq(vpu->irq);
0110 
0111     /* Keep AUX in reset mode */
0112     writel(AUX_CTRL_SW_RESET, vpu->aux_base + REG_AUX_CTRL);
0113 
0114     return 0;
0115 }
0116 
0117 static void ingenic_rproc_kick(struct rproc *rproc, int vqid)
0118 {
0119     struct vpu *vpu = rproc->priv;
0120 
0121     writel(vqid, vpu->aux_base + REG_CORE_MSG);
0122 }
0123 
0124 static void *ingenic_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
0125 {
0126     struct vpu *vpu = rproc->priv;
0127     void __iomem *va = NULL;
0128     unsigned int i;
0129 
0130     for (i = 0; i < ARRAY_SIZE(vpu_mem_map); i++) {
0131         const struct vpu_mem_info *info = &vpu->mem_info[i];
0132         const struct vpu_mem_map *map = info->map;
0133 
0134         if (da >= map->da && (da + len) < (map->da + info->len)) {
0135             va = info->base + (da - map->da);
0136             break;
0137         }
0138     }
0139 
0140     return (__force void *)va;
0141 }
0142 
0143 static const struct rproc_ops ingenic_rproc_ops = {
0144     .prepare = ingenic_rproc_prepare,
0145     .unprepare = ingenic_rproc_unprepare,
0146     .start = ingenic_rproc_start,
0147     .stop = ingenic_rproc_stop,
0148     .kick = ingenic_rproc_kick,
0149     .da_to_va = ingenic_rproc_da_to_va,
0150 };
0151 
0152 static irqreturn_t vpu_interrupt(int irq, void *data)
0153 {
0154     struct rproc *rproc = data;
0155     struct vpu *vpu = rproc->priv;
0156     u32 vring;
0157 
0158     vring = readl(vpu->aux_base + REG_AUX_MSG);
0159 
0160     /* Ack the interrupt */
0161     writel(0, vpu->aux_base + REG_AUX_MSG_ACK);
0162 
0163     return rproc_vq_interrupt(rproc, vring);
0164 }
0165 
0166 static int ingenic_rproc_probe(struct platform_device *pdev)
0167 {
0168     struct device *dev = &pdev->dev;
0169     struct resource *mem;
0170     struct rproc *rproc;
0171     struct vpu *vpu;
0172     unsigned int i;
0173     int ret;
0174 
0175     rproc = devm_rproc_alloc(dev, "ingenic-vpu",
0176                  &ingenic_rproc_ops, NULL, sizeof(*vpu));
0177     if (!rproc)
0178         return -ENOMEM;
0179 
0180     rproc->auto_boot = auto_boot;
0181 
0182     vpu = rproc->priv;
0183     vpu->dev = &pdev->dev;
0184     platform_set_drvdata(pdev, vpu);
0185 
0186     mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aux");
0187     vpu->aux_base = devm_ioremap_resource(dev, mem);
0188     if (IS_ERR(vpu->aux_base)) {
0189         dev_err(dev, "Failed to ioremap\n");
0190         return PTR_ERR(vpu->aux_base);
0191     }
0192 
0193     for (i = 0; i < ARRAY_SIZE(vpu_mem_map); i++) {
0194         mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
0195                            vpu_mem_map[i].name);
0196 
0197         vpu->mem_info[i].base = devm_ioremap_resource(dev, mem);
0198         if (IS_ERR(vpu->mem_info[i].base)) {
0199             ret = PTR_ERR(vpu->mem_info[i].base);
0200             dev_err(dev, "Failed to ioremap\n");
0201             return ret;
0202         }
0203 
0204         vpu->mem_info[i].len = resource_size(mem);
0205         vpu->mem_info[i].map = &vpu_mem_map[i];
0206     }
0207 
0208     vpu->clks[0].id = "vpu";
0209     vpu->clks[1].id = "aux";
0210 
0211     ret = devm_clk_bulk_get(dev, ARRAY_SIZE(vpu->clks), vpu->clks);
0212     if (ret) {
0213         dev_err(dev, "Failed to get clocks\n");
0214         return ret;
0215     }
0216 
0217     vpu->irq = platform_get_irq(pdev, 0);
0218     if (vpu->irq < 0)
0219         return vpu->irq;
0220 
0221     ret = devm_request_irq(dev, vpu->irq, vpu_interrupt, IRQF_NO_AUTOEN,
0222                    "VPU", rproc);
0223     if (ret < 0) {
0224         dev_err(dev, "Failed to request IRQ\n");
0225         return ret;
0226     }
0227 
0228     ret = devm_rproc_add(dev, rproc);
0229     if (ret) {
0230         dev_err(dev, "Failed to register remote processor\n");
0231         return ret;
0232     }
0233 
0234     return 0;
0235 }
0236 
0237 static const struct of_device_id ingenic_rproc_of_matches[] = {
0238     { .compatible = "ingenic,jz4770-vpu-rproc", },
0239     {}
0240 };
0241 MODULE_DEVICE_TABLE(of, ingenic_rproc_of_matches);
0242 
0243 static struct platform_driver ingenic_rproc_driver = {
0244     .probe = ingenic_rproc_probe,
0245     .driver = {
0246         .name = "ingenic-vpu",
0247         .of_match_table = ingenic_rproc_of_matches,
0248     },
0249 };
0250 module_platform_driver(ingenic_rproc_driver);
0251 
0252 MODULE_LICENSE("GPL");
0253 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
0254 MODULE_DESCRIPTION("Ingenic JZ47xx Remote Processor control driver");