0001
0002
0003
0004
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
0054
0055
0056
0057
0058
0059
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
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
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
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
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");