113be5432SRobert Tivy /* 213be5432SRobert Tivy * Remote processor machine-specific module for DA8XX 313be5432SRobert Tivy * 413be5432SRobert Tivy * Copyright (C) 2013 Texas Instruments, Inc. 513be5432SRobert Tivy * 613be5432SRobert Tivy * This program is free software; you can redistribute it and/or 713be5432SRobert Tivy * modify it under the terms of the GNU General Public License 813be5432SRobert Tivy * version 2 as published by the Free Software Foundation. 913be5432SRobert Tivy */ 1013be5432SRobert Tivy 1113be5432SRobert Tivy #include <linux/bitops.h> 1213be5432SRobert Tivy #include <linux/clk.h> 1313be5432SRobert Tivy #include <linux/err.h> 1413be5432SRobert Tivy #include <linux/interrupt.h> 1513be5432SRobert Tivy #include <linux/io.h> 1613be5432SRobert Tivy #include <linux/irq.h> 1713be5432SRobert Tivy #include <linux/kernel.h> 1813be5432SRobert Tivy #include <linux/module.h> 1913be5432SRobert Tivy #include <linux/platform_device.h> 2013be5432SRobert Tivy #include <linux/remoteproc.h> 2113be5432SRobert Tivy 2213be5432SRobert Tivy #include <mach/clock.h> /* for davinci_clk_reset_assert/deassert() */ 2313be5432SRobert Tivy 2413be5432SRobert Tivy #include "remoteproc_internal.h" 2513be5432SRobert Tivy 2613be5432SRobert Tivy static char *da8xx_fw_name; 2713be5432SRobert Tivy module_param(da8xx_fw_name, charp, S_IRUGO); 2813be5432SRobert Tivy MODULE_PARM_DESC(da8xx_fw_name, 2913be5432SRobert Tivy "\n\t\tName of DSP firmware file in /lib/firmware" 3013be5432SRobert Tivy " (if not specified defaults to 'rproc-dsp-fw')"); 3113be5432SRobert Tivy 3213be5432SRobert Tivy /* 3313be5432SRobert Tivy * OMAP-L138 Technical References: 3413be5432SRobert Tivy * http://www.ti.com/product/omap-l138 3513be5432SRobert Tivy */ 3613be5432SRobert Tivy #define SYSCFG_CHIPSIG0 BIT(0) 3713be5432SRobert Tivy #define SYSCFG_CHIPSIG1 BIT(1) 3813be5432SRobert Tivy #define SYSCFG_CHIPSIG2 BIT(2) 3913be5432SRobert Tivy #define SYSCFG_CHIPSIG3 BIT(3) 4013be5432SRobert Tivy #define SYSCFG_CHIPSIG4 BIT(4) 4113be5432SRobert Tivy 4213be5432SRobert Tivy /** 4313be5432SRobert Tivy * struct da8xx_rproc - da8xx remote processor instance state 4413be5432SRobert Tivy * @rproc: rproc handle 4513be5432SRobert Tivy * @dsp_clk: placeholder for platform's DSP clk 4613be5432SRobert Tivy * @ack_fxn: chip-specific ack function for ack'ing irq 4713be5432SRobert Tivy * @irq_data: ack_fxn function parameter 4813be5432SRobert Tivy * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR) 4913be5432SRobert Tivy * @bootreg: virt ptr to DSP boot address register (HOST1CFG) 5013be5432SRobert Tivy * @irq: irq # used by this instance 5113be5432SRobert Tivy */ 5213be5432SRobert Tivy struct da8xx_rproc { 5313be5432SRobert Tivy struct rproc *rproc; 5413be5432SRobert Tivy struct clk *dsp_clk; 5513be5432SRobert Tivy void (*ack_fxn)(struct irq_data *data); 5613be5432SRobert Tivy struct irq_data *irq_data; 5713be5432SRobert Tivy void __iomem *chipsig; 5813be5432SRobert Tivy void __iomem *bootreg; 5913be5432SRobert Tivy int irq; 6013be5432SRobert Tivy }; 6113be5432SRobert Tivy 6213be5432SRobert Tivy /** 6313be5432SRobert Tivy * handle_event() - inbound virtqueue message workqueue function 6413be5432SRobert Tivy * 6513be5432SRobert Tivy * This function is registered as a kernel thread and is scheduled by the 6613be5432SRobert Tivy * kernel handler. 6713be5432SRobert Tivy */ 6813be5432SRobert Tivy static irqreturn_t handle_event(int irq, void *p) 6913be5432SRobert Tivy { 7013be5432SRobert Tivy struct rproc *rproc = (struct rproc *)p; 7113be5432SRobert Tivy 7213be5432SRobert Tivy /* Process incoming buffers on all our vrings */ 7313be5432SRobert Tivy rproc_vq_interrupt(rproc, 0); 7413be5432SRobert Tivy rproc_vq_interrupt(rproc, 1); 7513be5432SRobert Tivy 7613be5432SRobert Tivy return IRQ_HANDLED; 7713be5432SRobert Tivy } 7813be5432SRobert Tivy 7913be5432SRobert Tivy /** 8013be5432SRobert Tivy * da8xx_rproc_callback() - inbound virtqueue message handler 8113be5432SRobert Tivy * 8213be5432SRobert Tivy * This handler is invoked directly by the kernel whenever the remote 8313be5432SRobert Tivy * core (DSP) has modified the state of a virtqueue. There is no 8413be5432SRobert Tivy * "payload" message indicating the virtqueue index as is the case with 8513be5432SRobert Tivy * mailbox-based implementations on OMAP4. As such, this handler "polls" 8613be5432SRobert Tivy * each known virtqueue index for every invocation. 8713be5432SRobert Tivy */ 8813be5432SRobert Tivy static irqreturn_t da8xx_rproc_callback(int irq, void *p) 8913be5432SRobert Tivy { 9013be5432SRobert Tivy struct rproc *rproc = (struct rproc *)p; 9113be5432SRobert Tivy struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; 9213be5432SRobert Tivy u32 chipsig; 9313be5432SRobert Tivy 9413be5432SRobert Tivy chipsig = readl(drproc->chipsig); 9513be5432SRobert Tivy if (chipsig & SYSCFG_CHIPSIG0) { 9613be5432SRobert Tivy /* Clear interrupt level source */ 9713be5432SRobert Tivy writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4); 9813be5432SRobert Tivy 9913be5432SRobert Tivy /* 10013be5432SRobert Tivy * ACK intr to AINTC. 10113be5432SRobert Tivy * 10213be5432SRobert Tivy * It has already been ack'ed by the kernel before calling 10313be5432SRobert Tivy * this function, but since the ARM<->DSP interrupts in the 10413be5432SRobert Tivy * CHIPSIG register are "level" instead of "pulse" variety, 10513be5432SRobert Tivy * we need to ack it after taking down the level else we'll 10613be5432SRobert Tivy * be called again immediately after returning. 10713be5432SRobert Tivy */ 10813be5432SRobert Tivy drproc->ack_fxn(drproc->irq_data); 10913be5432SRobert Tivy 11013be5432SRobert Tivy return IRQ_WAKE_THREAD; 11113be5432SRobert Tivy } 11213be5432SRobert Tivy 11313be5432SRobert Tivy return IRQ_HANDLED; 11413be5432SRobert Tivy } 11513be5432SRobert Tivy 11613be5432SRobert Tivy static int da8xx_rproc_start(struct rproc *rproc) 11713be5432SRobert Tivy { 11813be5432SRobert Tivy struct device *dev = rproc->dev.parent; 11913be5432SRobert Tivy struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; 12013be5432SRobert Tivy struct clk *dsp_clk = drproc->dsp_clk; 12113be5432SRobert Tivy 12213be5432SRobert Tivy /* hw requires the start (boot) address be on 1KB boundary */ 12313be5432SRobert Tivy if (rproc->bootaddr & 0x3ff) { 12413be5432SRobert Tivy dev_err(dev, "invalid boot address: must be aligned to 1KB\n"); 12513be5432SRobert Tivy 12613be5432SRobert Tivy return -EINVAL; 12713be5432SRobert Tivy } 12813be5432SRobert Tivy 12913be5432SRobert Tivy writel(rproc->bootaddr, drproc->bootreg); 13013be5432SRobert Tivy 13113be5432SRobert Tivy clk_enable(dsp_clk); 13213be5432SRobert Tivy davinci_clk_reset_deassert(dsp_clk); 13313be5432SRobert Tivy 13413be5432SRobert Tivy return 0; 13513be5432SRobert Tivy } 13613be5432SRobert Tivy 13713be5432SRobert Tivy static int da8xx_rproc_stop(struct rproc *rproc) 13813be5432SRobert Tivy { 13913be5432SRobert Tivy struct da8xx_rproc *drproc = rproc->priv; 14013be5432SRobert Tivy 14113be5432SRobert Tivy clk_disable(drproc->dsp_clk); 14213be5432SRobert Tivy 14313be5432SRobert Tivy return 0; 14413be5432SRobert Tivy } 14513be5432SRobert Tivy 14613be5432SRobert Tivy /* kick a virtqueue */ 14713be5432SRobert Tivy static void da8xx_rproc_kick(struct rproc *rproc, int vqid) 14813be5432SRobert Tivy { 14913be5432SRobert Tivy struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; 15013be5432SRobert Tivy 15113be5432SRobert Tivy /* Interupt remote proc */ 15213be5432SRobert Tivy writel(SYSCFG_CHIPSIG2, drproc->chipsig); 15313be5432SRobert Tivy } 15413be5432SRobert Tivy 15513be5432SRobert Tivy static struct rproc_ops da8xx_rproc_ops = { 15613be5432SRobert Tivy .start = da8xx_rproc_start, 15713be5432SRobert Tivy .stop = da8xx_rproc_stop, 15813be5432SRobert Tivy .kick = da8xx_rproc_kick, 15913be5432SRobert Tivy }; 16013be5432SRobert Tivy 16113be5432SRobert Tivy static int reset_assert(struct device *dev) 16213be5432SRobert Tivy { 16313be5432SRobert Tivy struct clk *dsp_clk; 16413be5432SRobert Tivy 16513be5432SRobert Tivy dsp_clk = clk_get(dev, NULL); 16613be5432SRobert Tivy if (IS_ERR(dsp_clk)) { 16713be5432SRobert Tivy dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); 16813be5432SRobert Tivy return PTR_RET(dsp_clk); 16913be5432SRobert Tivy } 17013be5432SRobert Tivy 17113be5432SRobert Tivy davinci_clk_reset_assert(dsp_clk); 17213be5432SRobert Tivy clk_put(dsp_clk); 17313be5432SRobert Tivy 17413be5432SRobert Tivy return 0; 17513be5432SRobert Tivy } 17613be5432SRobert Tivy 17713be5432SRobert Tivy static int da8xx_rproc_probe(struct platform_device *pdev) 17813be5432SRobert Tivy { 17913be5432SRobert Tivy struct device *dev = &pdev->dev; 18013be5432SRobert Tivy struct da8xx_rproc *drproc; 18113be5432SRobert Tivy struct rproc *rproc; 18213be5432SRobert Tivy struct irq_data *irq_data; 18313be5432SRobert Tivy struct resource *bootreg_res; 18413be5432SRobert Tivy struct resource *chipsig_res; 18513be5432SRobert Tivy struct clk *dsp_clk; 18613be5432SRobert Tivy void __iomem *chipsig; 18713be5432SRobert Tivy void __iomem *bootreg; 18813be5432SRobert Tivy int irq; 18913be5432SRobert Tivy int ret; 19013be5432SRobert Tivy 19113be5432SRobert Tivy irq = platform_get_irq(pdev, 0); 19213be5432SRobert Tivy if (irq < 0) { 19313be5432SRobert Tivy dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq); 19413be5432SRobert Tivy return irq; 19513be5432SRobert Tivy } 19613be5432SRobert Tivy 19713be5432SRobert Tivy irq_data = irq_get_irq_data(irq); 19813be5432SRobert Tivy if (!irq_data) { 19913be5432SRobert Tivy dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq); 20013be5432SRobert Tivy return -EINVAL; 20113be5432SRobert Tivy } 20213be5432SRobert Tivy 20313be5432SRobert Tivy bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 20413be5432SRobert Tivy if (!bootreg_res) { 20513be5432SRobert Tivy dev_err(dev, 20613be5432SRobert Tivy "platform_get_resource(IORESOURCE_MEM, 0): NULL\n"); 20713be5432SRobert Tivy return -EADDRNOTAVAIL; 20813be5432SRobert Tivy } 20913be5432SRobert Tivy 21013be5432SRobert Tivy chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 21113be5432SRobert Tivy if (!chipsig_res) { 21213be5432SRobert Tivy dev_err(dev, 21313be5432SRobert Tivy "platform_get_resource(IORESOURCE_MEM, 1): NULL\n"); 21413be5432SRobert Tivy return -EADDRNOTAVAIL; 21513be5432SRobert Tivy } 21613be5432SRobert Tivy 21713be5432SRobert Tivy bootreg = devm_ioremap_resource(dev, bootreg_res); 21813be5432SRobert Tivy if (IS_ERR(bootreg)) 21913be5432SRobert Tivy return PTR_ERR(bootreg); 22013be5432SRobert Tivy 22113be5432SRobert Tivy chipsig = devm_ioremap_resource(dev, chipsig_res); 22213be5432SRobert Tivy if (IS_ERR(chipsig)) 22313be5432SRobert Tivy return PTR_ERR(chipsig); 22413be5432SRobert Tivy 22513be5432SRobert Tivy dsp_clk = devm_clk_get(dev, NULL); 22613be5432SRobert Tivy if (IS_ERR(dsp_clk)) { 22713be5432SRobert Tivy dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); 22813be5432SRobert Tivy 22913be5432SRobert Tivy return PTR_ERR(dsp_clk); 23013be5432SRobert Tivy } 23113be5432SRobert Tivy 23213be5432SRobert Tivy rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name, 23313be5432SRobert Tivy sizeof(*drproc)); 23413be5432SRobert Tivy if (!rproc) 23513be5432SRobert Tivy return -ENOMEM; 23613be5432SRobert Tivy 23713be5432SRobert Tivy drproc = rproc->priv; 23813be5432SRobert Tivy drproc->rproc = rproc; 23913be5432SRobert Tivy 24013be5432SRobert Tivy platform_set_drvdata(pdev, rproc); 24113be5432SRobert Tivy 24213be5432SRobert Tivy /* everything the ISR needs is now setup, so hook it up */ 24313be5432SRobert Tivy ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback, 24413be5432SRobert Tivy handle_event, 0, "da8xx-remoteproc", 24513be5432SRobert Tivy rproc); 24613be5432SRobert Tivy if (ret) { 24713be5432SRobert Tivy dev_err(dev, "devm_request_threaded_irq error: %d\n", ret); 24813be5432SRobert Tivy goto free_rproc; 24913be5432SRobert Tivy } 25013be5432SRobert Tivy 25113be5432SRobert Tivy /* 25213be5432SRobert Tivy * rproc_add() can end up enabling the DSP's clk with the DSP 25313be5432SRobert Tivy * *not* in reset, but da8xx_rproc_start() needs the DSP to be 25413be5432SRobert Tivy * held in reset at the time it is called. 25513be5432SRobert Tivy */ 25613be5432SRobert Tivy ret = reset_assert(dev); 25713be5432SRobert Tivy if (ret) 25813be5432SRobert Tivy goto free_rproc; 25913be5432SRobert Tivy 26013be5432SRobert Tivy drproc->chipsig = chipsig; 26113be5432SRobert Tivy drproc->bootreg = bootreg; 26213be5432SRobert Tivy drproc->ack_fxn = irq_data->chip->irq_ack; 26313be5432SRobert Tivy drproc->irq_data = irq_data; 26413be5432SRobert Tivy drproc->irq = irq; 26513be5432SRobert Tivy drproc->dsp_clk = dsp_clk; 26613be5432SRobert Tivy 26713be5432SRobert Tivy ret = rproc_add(rproc); 26813be5432SRobert Tivy if (ret) { 26913be5432SRobert Tivy dev_err(dev, "rproc_add failed: %d\n", ret); 27013be5432SRobert Tivy goto free_rproc; 27113be5432SRobert Tivy } 27213be5432SRobert Tivy 27313be5432SRobert Tivy return 0; 27413be5432SRobert Tivy 27513be5432SRobert Tivy free_rproc: 27613be5432SRobert Tivy rproc_put(rproc); 27713be5432SRobert Tivy 27813be5432SRobert Tivy return ret; 27913be5432SRobert Tivy } 28013be5432SRobert Tivy 28113be5432SRobert Tivy static int da8xx_rproc_remove(struct platform_device *pdev) 28213be5432SRobert Tivy { 28313be5432SRobert Tivy struct device *dev = &pdev->dev; 28413be5432SRobert Tivy struct rproc *rproc = platform_get_drvdata(pdev); 28513be5432SRobert Tivy struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; 28613be5432SRobert Tivy 28713be5432SRobert Tivy /* 28813be5432SRobert Tivy * It's important to place the DSP in reset before going away, 28913be5432SRobert Tivy * since a subsequent insmod of this module may enable the DSP's 29013be5432SRobert Tivy * clock before its program/boot-address has been loaded and 29113be5432SRobert Tivy * before this module's probe has had a chance to reset the DSP. 29213be5432SRobert Tivy * Without the reset, the DSP can lockup permanently when it 29313be5432SRobert Tivy * begins executing garbage. 29413be5432SRobert Tivy */ 29513be5432SRobert Tivy reset_assert(dev); 29613be5432SRobert Tivy 29713be5432SRobert Tivy /* 29813be5432SRobert Tivy * The devm subsystem might end up releasing things before 29913be5432SRobert Tivy * freeing the irq, thus allowing an interrupt to sneak in while 30013be5432SRobert Tivy * the device is being removed. This should prevent that. 30113be5432SRobert Tivy */ 30213be5432SRobert Tivy disable_irq(drproc->irq); 30313be5432SRobert Tivy 30413be5432SRobert Tivy devm_clk_put(dev, drproc->dsp_clk); 30513be5432SRobert Tivy 30613be5432SRobert Tivy rproc_del(rproc); 30713be5432SRobert Tivy rproc_put(rproc); 30813be5432SRobert Tivy 30913be5432SRobert Tivy return 0; 31013be5432SRobert Tivy } 31113be5432SRobert Tivy 31213be5432SRobert Tivy static struct platform_driver da8xx_rproc_driver = { 31313be5432SRobert Tivy .probe = da8xx_rproc_probe, 31413be5432SRobert Tivy .remove = da8xx_rproc_remove, 31513be5432SRobert Tivy .driver = { 31613be5432SRobert Tivy .name = "davinci-rproc", 31713be5432SRobert Tivy .owner = THIS_MODULE, 31813be5432SRobert Tivy }, 31913be5432SRobert Tivy }; 32013be5432SRobert Tivy 32113be5432SRobert Tivy module_platform_driver(da8xx_rproc_driver); 32213be5432SRobert Tivy 32313be5432SRobert Tivy MODULE_LICENSE("GPL v2"); 32413be5432SRobert Tivy MODULE_DESCRIPTION("DA8XX Remote Processor control driver"); 325