brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · 1b78d8d Raw
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