From patchwork Mon Jan 19 10:30:20 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Simek X-Patchwork-Id: 5657081 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 81BDF9F2ED for ; Mon, 19 Jan 2015 10:46:40 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 04EB120222 for ; Mon, 19 Jan 2015 10:46:39 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 70F0E20254 for ; Mon, 19 Jan 2015 10:46:37 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YD9oF-0005Q4-Bb; Mon, 19 Jan 2015 10:43:51 +0000 Received: from mail-wg0-f49.google.com ([74.125.82.49]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YD9bl-0001sS-NZ for linux-arm-kernel@lists.infradead.org; Mon, 19 Jan 2015 10:31:01 +0000 Received: by mail-wg0-f49.google.com with SMTP id l18so5321530wgh.8 for ; Mon, 19 Jan 2015 02:30:33 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:in-reply-to:references:content-type; bh=cwhdL5Q3cuYA3eMBIArIm4ZiU/vZp+8eC58RnfeX+Z8=; b=KOmvlefnT5xmFKSyp4at/ARWrPxIpVIu/yToTkr39EehtkRAsgy86TjpDo0DTaRm9S GYkfzCPSJijUaUGljDJQFRdFW7TIwQV4fHNjBs97Fsfk7oNDZQdCJsQvg7mGvXNq1GQ3 wjr6zT12L0N5L3WHpJSUfNtoPKGOdW3iwIcF39bqhuVfbV2VMUUpOD2g04h4TwP5Kgff N6mVfFhuxDtp+M1R6KJTwiM7dEdNzZHl4yetEg9Wya4faX3NF7j9tJdwSLHOr1Hz4sgz zW+U3fF/sv68N+Bu8tRjdnhISqnvUod+SRK8N7ARmQWYDEBdYNT0ZBTJSzj0LFfGirYU TUgw== X-Gm-Message-State: ALoCoQmGuxv2LOkoYRarwXvMOq0pV+2sIlq/Ns6tjLWE7XFI755GnrMYi3QWEzRxCC+Hm1xyEbgt X-Received: by 10.194.157.4 with SMTP id wi4mr57402546wjb.54.1421663433680; Mon, 19 Jan 2015 02:30:33 -0800 (PST) Received: from localhost (nat-35.starnet.cz. [178.255.168.35]) by mx.google.com with ESMTPSA id kn7sm16701343wjc.45.2015.01.19.02.30.32 (version=TLSv1.1 cipher=RC4-SHA bits=128/128); Mon, 19 Jan 2015 02:30:32 -0800 (PST) From: Michal Simek To: linux-kernel@vger.kernel.org, monstr@monstr.eu Subject: [PATCH 2/2] remoteproc: microblaze: Add support for microblaze on Zynq Date: Mon, 19 Jan 2015 11:30:20 +0100 Message-Id: <10276362c737fc9da863f1a07737fd91f7340242.1421662385.git.michal.simek@xilinx.com> X-Mailer: git-send-email 1.8.2.3 In-Reply-To: References: In-Reply-To: References: X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150119_023058_194904_A4A51683 X-CRM114-Status: GOOD ( 25.24 ) X-Spam-Score: 1.3 (+) Cc: Ohad Ben-Cohen , Antti Palosaari , Mauro Carvalho Chehab , Greg Kroah-Hartman , Joe Perches , devicetree@vger.kernel.org, Rob Herring , linux-arm-kernel@lists.infradead.org, Grant Likely , Andrew Morton , "David S. Miller" , =?UTF-8?q?S=C3=B6ren=20Brinkmann?= X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD,T_TVD_MIME_NO_HEADERS,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Xilinx Microblaze is placed in programmable logic on Xilinx Zynq architecture. Driver requires specific HW setting described in DT binding. Signed-off-by: Michal Simek --- MAINTAINERS | 1 + drivers/remoteproc/Kconfig | 11 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/mb_remoteproc.c | 425 +++++++++++++++++++++++++++++++++++++ 4 files changed, 438 insertions(+) create mode 100644 drivers/remoteproc/mb_remoteproc.c -- 1.8.2.3 diff --git a/MAINTAINERS b/MAINTAINERS index ab6610e6c610..b683990e0ca1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1583,6 +1583,7 @@ N: xilinx F: drivers/clocksource/cadence_ttc_timer.c F: drivers/i2c/busses/i2c-cadence.c F: drivers/mmc/host/sdhci-of-arasan.c +F: drivers/remoteproc/mb_remoteproc.c ARM SMMU DRIVER M: Will Deacon diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 5e343bab9458..3955f42e9e9c 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -64,4 +64,15 @@ config DA8XX_REMOTEPROC It's safe to say n here if you're not interested in multimedia offloading. +config MB_REMOTEPROC + tristate "Support Microblaze remoteproc" + depends on ARCH_ZYNQ && !DEBUG_SG + select GPIO_XILINX + select REMOTEPROC + select RPMSG + default m + help + Say y here to support Xilinx Microblaze remote processors + on the Xilinx Zynq. + endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index ac2ff75686d2..40e247ffac34 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -10,3 +10,4 @@ remoteproc-y += remoteproc_elf_loader.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o +obj-$(CONFIG_MB_REMOTEPROC) += mb_remoteproc.o diff --git a/drivers/remoteproc/mb_remoteproc.c b/drivers/remoteproc/mb_remoteproc.c new file mode 100644 index 000000000000..699301c65dfd --- /dev/null +++ b/drivers/remoteproc/mb_remoteproc.c @@ -0,0 +1,425 @@ +/* + * Microblaze Remote Processor driver + * + * Copyright (C) 2012 - 2015 Michal Simek + * Copyright (C) 2013 - 2015 Xilinx, Inc. + * Copyright (C) 2012 PetaLogix + * + * Based on origin OMAP Remote Processor driver + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "remoteproc_internal.h" + +/* Module parameter */ +static char *firmware; + +/* Private data */ +struct mb_rproc_pdata { + struct rproc *rproc; + u32 mem_start; + u32 mem_end; + int reset_gpio; + int mb_debug_gpio; + int ipi; + int vring0; + int vring1; + void __iomem *vbase; + const unsigned char *bootloader; +}; + +/* Store rproc for IPI handler */ +static struct platform_device *remoteprocdev; +static struct work_struct workqueue; + +static void handle_event(struct work_struct *work) +{ + struct mb_rproc_pdata *local = platform_get_drvdata(remoteprocdev); + + flush_cache_all(); + outer_flush_range(local->mem_start, local->mem_end); + + if (rproc_vq_interrupt(local->rproc, 0) == IRQ_NONE) + dev_info(&remoteprocdev->dev, "no message found in vqid 0\n"); +} + +static irqreturn_t ipi_kick(int irq, void *dev_id) +{ + dev_dbg(&remoteprocdev->dev, "KICK Linux because of pending message\n"); + schedule_work(&workqueue); + dev_dbg(&remoteprocdev->dev, "KICK Linux handled\n"); + + return IRQ_HANDLED; +} + +static int mb_rproc_start(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct platform_device *pdev = to_platform_device(dev); + struct mb_rproc_pdata *local = platform_get_drvdata(pdev); + const struct firmware *fw; + int ret; + + dev_info(dev, "%s\n", __func__); + INIT_WORK(&workqueue, handle_event); + + flush_cache_all(); + outer_flush_range(local->mem_start, local->mem_end); + + remoteprocdev = pdev; + + ret = request_firmware(&fw, local->bootloader, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "request_firmware failed\n"); + return ret; + } + /* Copy bootloader to memory */ + memcpy(local->vbase, fw->data, fw->size); + release_firmware(fw); + + /* Just for sure synchronize memories */ + dsb(); + + /* Release Microblaze from reset */ + gpio_set_value(local->reset_gpio, 0); + + return 0; +} + +/* kick a firmware */ +static void mb_rproc_kick(struct rproc *rproc, int vqid) +{ + struct device *dev = rproc->dev.parent; + struct platform_device *pdev = to_platform_device(dev); + struct mb_rproc_pdata *local = platform_get_drvdata(pdev); + + dev_dbg(dev, "KICK Firmware to start send messages vqid %d\n", vqid); + + flush_cache_all(); + outer_flush_all(); + + /* Send swirq to firmware */ + gpio_set_value(local->vring0, 0); + gpio_set_value(local->vring1, 0); + dsb(); + + if (!vqid) { + udelay(500); + gpio_set_value(local->vring0, 1); + dsb(); + } else { + udelay(100); + gpio_set_value(local->vring1, 1); + dsb(); + } +} + +/* power off the remote processor */ +static int mb_rproc_stop(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct platform_device *pdev = to_platform_device(dev); + struct mb_rproc_pdata *local = platform_get_drvdata(pdev); + + /* Setup MB to the state where all memory transactions are done */ + gpio_set_value(local->mb_debug_gpio, 1); + dsb(); /* Be sure that this write has been done */ + /* + * This should be enough to ensure one CLK as + * it is written in MB ref guide + */ + gpio_set_value(local->mb_debug_gpio, 0); + + udelay(1000); /* Wait some time to finish all mem transactions */ + + /* Add Microblaze to reset state */ + gpio_set_value(local->reset_gpio, 1); + + /* No reason to wait that operations where done */ + return 0; +} + +static struct rproc_ops mb_rproc_ops = { + .start = mb_rproc_start, + .stop = mb_rproc_stop, + .kick = mb_rproc_kick, +}; + +/* Just to detect bug if interrupt forwarding is broken */ +static irqreturn_t mb_remoteproc_interrupt(int irq, void *dev_id) +{ + struct device *dev = dev_id; + + dev_err(dev, "GIC IRQ %d is not forwarded correctly\n", irq); + + return IRQ_HANDLED; +} + +static int mb_remoteproc_probe(struct platform_device *pdev) +{ + const unsigned char *prop; + struct platform_device *bram_pdev; + struct device_node *bram_dev; + struct resource *res; /* IO mem resources */ + int ret = 0; + int count = 0; + struct mb_rproc_pdata *local; + + local = devm_kzalloc(&pdev->dev, sizeof(*local), GFP_KERNEL); + if (!local) + return -ENOMEM; + + platform_set_drvdata(pdev, local); + + /* Declare memory for firmware */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "invalid address\n"); + return -ENODEV; + } + + local->mem_start = res->start; + local->mem_end = res->end; + + /* Alloc phys addr from 0 to max_addr for firmware */ + ret = dma_declare_coherent_memory(&pdev->dev, local->mem_start, + local->mem_start, local->mem_end - local->mem_start + 1, + DMA_MEMORY_IO); + if (!ret) { + dev_err(&pdev->dev, "dma_declare_coherent_memory failed\n"); + return -ENOMEM; + } + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret); + goto dma_mask_fault; + } + + /* Alloc IRQ based on DTS to be sure that no other driver will use it */ + while (1) { + int irq; + /* Allocating shared IRQs will ensure that any module will + * use these IRQs */ + irq = platform_get_irq(pdev, count++); + if (irq == -ENXIO || irq == -EINVAL) + break; + ret = devm_request_irq(&pdev->dev, irq, mb_remoteproc_interrupt, + 0, dev_name(&pdev->dev), &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already allocated\n", irq); + goto dma_mask_fault; + } + + dev_info(&pdev->dev, "%d: Alloc irq: %d\n", count, irq); + } + + /* Find out reset gpio and keep microblaze in reset */ + local->reset_gpio = of_get_named_gpio(pdev->dev.of_node, "reset", 0); + if (local->reset_gpio < 0) { + dev_err(&pdev->dev, "reset-gpio property not found\n"); + ret = local->reset_gpio; + goto dma_mask_fault; + } + ret = devm_gpio_request_one(&pdev->dev, local->reset_gpio, + GPIOF_OUT_INIT_HIGH, "mb_reset"); + if (ret) { + dev_err(&pdev->dev, "Please specify gpio reset addr\n"); + goto dma_mask_fault; + } + + /* Find out reset gpio and keep microblaze in reset */ + local->mb_debug_gpio = of_get_named_gpio(pdev->dev.of_node, "debug", 0); + if (local->mb_debug_gpio < 0) { + dev_err(&pdev->dev, "mb-debug-gpio property not found\n"); + ret = local->mb_debug_gpio; + goto dma_mask_fault; + } + ret = devm_gpio_request_one(&pdev->dev, local->mb_debug_gpio, + GPIOF_OUT_INIT_LOW, "mb_debug"); + if (ret) { + dev_err(&pdev->dev, "Please specify gpio debug pin\n"); + goto dma_mask_fault; + } + + /* IPI number for getting irq from firmware */ + local->ipi = of_get_named_gpio(pdev->dev.of_node, "ipino", 0); + if (local->ipi < 0) { + dev_err(&pdev->dev, "ipi-gpio property not found\n"); + ret = local->ipi; + goto dma_mask_fault; + } + ret = devm_gpio_request_one(&pdev->dev, local->ipi, GPIOF_IN, "mb_ipi"); + if (ret) { + dev_err(&pdev->dev, "Please specify gpio reset addr\n"); + goto dma_mask_fault; + } + ret = devm_request_irq(&pdev->dev, gpio_to_irq(local->ipi), + ipi_kick, IRQF_SHARED|IRQF_TRIGGER_RISING, + dev_name(&pdev->dev), local); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already allocated\n", local->ipi); + goto dma_mask_fault; + } + + /* Find out vring0 pin */ + local->vring0 = of_get_named_gpio(pdev->dev.of_node, "vring0", 0); + if (local->vring0 < 0) { + dev_err(&pdev->dev, "reset-gpio property not found\n"); + ret = local->vring0; + goto dma_mask_fault; + } + ret = devm_gpio_request_one(&pdev->dev, local->vring0, + GPIOF_DIR_OUT, "mb_vring0"); + if (ret) { + dev_err(&pdev->dev, "Please specify gpio reset addr\n"); + goto dma_mask_fault; + } + + /* Find out vring1 pin */ + local->vring1 = of_get_named_gpio(pdev->dev.of_node, "vring1", 0); + if (local->vring1 < 0) { + dev_err(&pdev->dev, "reset-gpio property not found\n"); + ret = local->vring1; + goto dma_mask_fault; + } + ret = devm_gpio_request_one(&pdev->dev, local->vring1, + GPIOF_DIR_OUT, "mb_vring1"); + if (ret) { + dev_err(&pdev->dev, "Please specify gpio reset addr\n"); + goto dma_mask_fault; + } + + /* Allocate bram device */ + bram_dev = of_parse_phandle(pdev->dev.of_node, "bram", 0); + if (!bram_dev) { + dev_err(&pdev->dev, "Please specify bram connection\n"); + ret = -ENODEV; + goto dma_mask_fault; + } + bram_pdev = of_find_device_by_node(bram_dev); + if (!bram_pdev) { + dev_err(&pdev->dev, "BRAM device hasn't found\n"); + ret = -ENODEV; + goto dma_mask_fault; + } + res = platform_get_resource(bram_pdev, IORESOURCE_MEM, 0); + local->vbase = devm_ioremap_resource(&pdev->dev, res); + if (!local->vbase) { + ret = -ENODEV; + goto dma_mask_fault; + } + + /* Load simple bootloader to bram */ + local->bootloader = of_get_property(pdev->dev.of_node, + "bram-firmware", NULL); + if (!local->bootloader) { + dev_err(&pdev->dev, "Please specify BRAM firmware\n"); + ret = -ENODEV; + goto dma_mask_fault; + } + + dev_info(&pdev->dev, "Using microblaze BRAM bootloader: %s\n", + local->bootloader); + + /* Module param firmware first */ + if (firmware) + prop = firmware; + else + prop = of_get_property(pdev->dev.of_node, "firmware", NULL); + + if (prop) { + dev_info(&pdev->dev, "Using firmware: %s\n", prop); + local->rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), + &mb_rproc_ops, prop, sizeof(struct rproc)); + if (!local->rproc) { + dev_err(&pdev->dev, "rproc allocation failed\n"); + ret = -ENODEV; + goto dma_mask_fault; + } + + ret = rproc_add(local->rproc); + if (ret) { + dev_err(&pdev->dev, "rproc registration failed\n"); + rproc_put(local->rproc); + goto dma_mask_fault; + } + return 0; + } + + ret = -ENODEV; + +dma_mask_fault: + dma_release_declared_memory(&pdev->dev); + + return ret; +} + +static int mb_remoteproc_remove(struct platform_device *pdev) +{ + struct mb_rproc_pdata *local = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "%s\n", __func__); + + dma_release_declared_memory(&pdev->dev); + + rproc_del(local->rproc); + rproc_put(local->rproc); + + return 0; +} + +/* Match table for OF platform binding */ +static struct of_device_id mb_remoteproc_match[] = { + { .compatible = "xlnx,mb_remoteproc", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, mb_remoteproc_match); + +static struct platform_driver mb_remoteproc_driver = { + .probe = mb_remoteproc_probe, + .remove = mb_remoteproc_remove, + .driver = { + .name = "mb_remoteproc", + .of_match_table = mb_remoteproc_match, + }, +}; +module_platform_driver(mb_remoteproc_driver); + +module_param(firmware, charp, 0); +MODULE_PARM_DESC(firmware, "Override the firmware image name. Default value in DTS."); + +MODULE_AUTHOR("Michal Simek