From patchwork Thu Jul 18 16:43:48 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: R Sricharan X-Patchwork-Id: 2829714 Return-Path: X-Original-To: patchwork-linux-omap@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id DF001C0AB2 for ; Thu, 18 Jul 2013 16:45:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 46362201E5 for ; Thu, 18 Jul 2013 16:45:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D7D6C201D3 for ; Thu, 18 Jul 2013 16:45:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759231Ab3GRQpN (ORCPT ); Thu, 18 Jul 2013 12:45:13 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:33848 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1759008Ab3GRQo1 (ORCPT ); Thu, 18 Jul 2013 12:44:27 -0400 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id r6IGhxW1018499; Thu, 18 Jul 2013 11:43:59 -0500 Received: from DLEE71.ent.ti.com (dlee71.ent.ti.com [157.170.170.114]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id r6IGhxC8009681; Thu, 18 Jul 2013 11:43:59 -0500 Received: from dlelxv22.itg.ti.com (172.17.1.197) by DLEE71.ent.ti.com (157.170.170.114) with Microsoft SMTP Server id 14.2.342.3; Thu, 18 Jul 2013 11:43:58 -0500 Received: from localhost.localdomain (uda0393807.apr.dhcp.ti.com [172.24.145.242]) by dlelxv22.itg.ti.com (8.13.8/8.13.8) with ESMTP id r6IGhphF018646; Thu, 18 Jul 2013 11:43:55 -0500 From: Sricharan R To: , , , , CC: , , , , , Subject: [PATCH 1/3] misc: Add crossbar driver Date: Thu, 18 Jul 2013 22:13:48 +0530 Message-ID: <1374165830-6367-2-git-send-email-r.sricharan@ti.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1374165830-6367-1-git-send-email-r.sricharan@ti.com> References: <1374165830-6367-1-git-send-email-r.sricharan@ti.com> MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Spam-Status: No, score=-7.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, 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 Some socs have a large number of interrupts/dma requests to service the needs of its many peripherals and subsystems. All of the requests lines from the subsystems are not needed at the same time, so they have to be muxed to the controllers appropriately. In such places a interrupt/dma controllers are preceded by an IRQ/DMA CROSSBAR that provides flexibility in muxing the device requests to the controller inputs. The Peripheral irq/dma requests are connected to one crossbar's input and the output of the crossbar is connected to controller's input line. On POR, there are some mappings which are done by default. Those peripherals which do not have a mapping on POR, should be configured to route its requests using the crossbar. The drivers identifies every controller's crossbar as individual devices. The mappings can be specified from the DT crossbar nodes and those gets mapped during the crossbar device's probe. The mappings can also be specified by adding the crossbar lines to the peripheral device nodes and map it with crossbar_map/unmap apis. Signed-off-by: Sricharan R --- .../devicetree/bindings/arm/omap/crossbar.txt | 24 ++ drivers/misc/Kconfig | 8 + drivers/misc/Makefile | 1 + drivers/misc/crossbar.c | 258 ++++++++++++++++++++ include/linux/crossbar.h | 71 ++++++ 5 files changed, 362 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/omap/crossbar.txt create mode 100644 drivers/misc/crossbar.c create mode 100644 include/linux/crossbar.h diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt new file mode 100644 index 0000000..02a8a28 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -0,0 +1,24 @@ +* TI - IRQ/DMA Crossbar + +This version is an implementation of the Crossbar IRQ/DMA IP + +Required properties: +- compatible : Should be "ti,dra-crossbar" +- crossbar-name: Name of the controller to which crossbar output is routed +- reg: Contains crossbar register address range +- reg-width: Represents the width of the individual registers +- crossbar-lines: Default mappings.Should contain the crossbar-name + device name, int/dma request number, crossbar number, + register offset in the same order. + +Examples: + crossbar_mpu: mpuirq@4a002a48 { + compatible = "crossbar"; + crossbar-name = "mpu-irq"; + reg = <0x4a002a48 0x0130>; + reg-width = <16>; + crossbar-lines = "mpu-irq", "rtc-ss-alarm", <0x9f 0xd9 0x12c>, + "mpu-irq", "mcasp3-arevt", <0x9e 0x96 0x12a>, + "mpu-irq", "mcasp3-axevt", <0x9d 0x97 0x128>; + }; + diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c002d86..de89bff 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -527,6 +527,14 @@ config SRAM the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. +config CROSSBAR + bool "on-chip crossbar driver" + select REGMAP_MMIO + help + This driver is for IRQ/DMA crossbar devices which is responsible for + muxing the irq/dma requests from external peripherals to the corresponding + controller's inputs. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c235d5b..37ce1b8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o +obj-$(CONFIG_CROSSBAR) += crossbar.o diff --git a/drivers/misc/crossbar.c b/drivers/misc/crossbar.c new file mode 100644 index 0000000..c0a7e83 --- /dev/null +++ b/drivers/misc/crossbar.c @@ -0,0 +1,258 @@ +/* + * IRQ/DMA CROSSBAR DRIVER + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ + * Sricharan R + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +#include +#include + +static LIST_HEAD(cb_devlist); + +static struct regmap_config cb_regmap_config = { + .reg_bits = 32, +}; + +static unsigned cb_entry_read(struct cb_line *tmp, const void *cbs) +{ + unsigned index = 0; + + tmp->cb_name = cbs; + index = strlen(tmp->cb_name) + 1; + + tmp->dev_name = cbs + index; + index += strlen(tmp->dev_name) + 1; + + tmp->int_no = be32_to_cpup(cbs + index); + index += sizeof(tmp->int_no); + + tmp->cb_no = be32_to_cpup(cbs + index); + index += sizeof(tmp->cb_no); + + tmp->offset = be32_to_cpup(cbs + index); + index += sizeof(tmp->offset); + + return index; +} + +int crossbar_unmap(struct device_node *cbdev_node, unsigned index) +{ + const void *cbs; + unsigned size = 0, i = 0; + struct cb_line tmp; + struct cb_device *cbdev; + struct cb_entry *cbentry, *p; + + cbs = of_get_property(cbdev_node, "crossbar-lines", &size); + if (!cbs) + return -ENOENT; + + size = 0; + + while (i++ < index) + size += cb_entry_read(&tmp, cbs + size); + + cb_entry_read(&tmp, cbs + size); + + list_for_each_entry(cbdev, &cb_devlist, node) { + if (strcmp(cbdev->name, tmp.cb_name)) + continue; + + mutex_lock(&cbdev->cb_lock); + list_for_each_entry_safe(cbentry, p, &cbdev->cb_entries, + cb_list) { + if ((cbentry->line.cb_no == tmp.cb_no) && + (cbentry->line.int_no == tmp.int_no)) { + list_del(&cbentry->cb_list); + mutex_unlock(&cbdev->cb_lock); + dev_warn(cbdev->dev, + "unmapped int_no %x mapped to cb %x\n", + tmp.int_no, tmp.cb_no); + return 0; + } + } + mutex_unlock(&cbdev->cb_lock); + break; + } + + dev_warn(cbdev->dev, "%s cb entry %d not found\n", + __func__, tmp.cb_no); + return -ENOENT; +} +EXPORT_SYMBOL(crossbar_unmap); + +const int cb_map(struct cb_line cbl) +{ + struct cb_device *cbdev; + struct cb_entry *cbentry, *tmp; + unsigned val; + + /* Get corresponding device pointer */ + list_for_each_entry(cbdev, &cb_devlist, node) { + if (strcmp(cbdev->name, cbl.cb_name)) + continue; + + mutex_lock(&cbdev->cb_lock); + + /* Check for invalid and duplicate mapping */ + list_for_each_entry_safe(cbentry, tmp, &cbdev->cb_entries, + cb_list) { + if ((cbentry->line.cb_no == cbl.cb_no) && + (cbentry->line.int_no != cbl.int_no)) { + dev_warn(cbdev->dev, + "%s irq already mapped to irq no %d", + cbentry->line.dev_name, + cbentry->line.int_no); + mutex_unlock(&cbdev->cb_lock); + return -EINVAL; + } + if ((cbentry->line.cb_no == cbl.cb_no) && + (cbentry->line.int_no == cbl.int_no)) { + mutex_unlock(&cbdev->cb_lock); + return 0; + } + if ((cbentry->line.int_no == cbl.int_no) && + (cbentry->line.cb_no != cbl.cb_no)) { + dev_warn(cbdev->dev, + "%s irq replaced by %s irq\n", + cbentry->line.dev_name, + cbl.dev_name); + list_del(&(cbentry->cb_list)); + break; + } + } + + cbentry = devm_kzalloc(cbdev->dev, sizeof(struct cb_entry), + GFP_KERNEL); + cbentry->line = cbl; + list_add_tail(&(cbentry->cb_list), &cbdev->cb_entries); + + regmap_read(cbdev->cb_regmap, cbl.offset, &val); + + /* Print the replaced entry and map the new one */ + dev_warn(cbdev->dev, + "replacing irq %d mapped to cb input %d with cb input %d\n", + cbl.int_no, val, cbl.cb_no); + + regmap_write(cbdev->cb_regmap, cbl.offset, cbl.cb_no); + mutex_unlock(&cbdev->cb_lock); + return 0; + } + + dev_warn(cbdev->dev, "crossbar device %s not found", cbl.cb_name); + return -ENODEV; +} + +int crossbar_map(struct device_node *cbdev_node) +{ + const void *cbs; + unsigned size = 0, index = 0; + int err; + + cbs = of_get_property(cbdev_node, "crossbar-lines", &size); + if (!cbs) + return -ENOENT; + + while (index < size) { + struct cb_line tmp; + + index += cb_entry_read(&tmp, cbs + index); + + err = cb_map(tmp); + if (IS_ERR_VALUE(err)) + return err; + } + + return 0; +} +EXPORT_SYMBOL(crossbar_map); + +static int crossbar_probe(struct platform_device *pdev) +{ + struct cb_device *cbdev; + unsigned width; + struct device_node *cbdev_node = pdev->dev.of_node; + int err; + struct resource *res; + + cbdev = devm_kzalloc(&pdev->dev, sizeof(struct cb_device), GFP_KERNEL); + if (!cbdev) + return -ENOMEM; + + /* Get the device resources */ + of_property_read_string(cbdev_node, "crossbar-name", &(cbdev->name)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -ENOENT; + + cbdev->base = devm_ioremap_resource(&pdev->dev, res); + if (!cbdev->base) + return -ENOMEM; + + cbdev->dev = &pdev->dev; + + of_property_read_u32(cbdev_node, "reg-width", &width); + + cb_regmap_config.val_bits = width; + cb_regmap_config.reg_stride = width >> 3; + + cbdev->cb_regmap = devm_regmap_init_mmio(cbdev->dev, cbdev->base, + &cb_regmap_config); + + if (IS_ERR(cbdev->cb_regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + err = PTR_ERR(cbdev->cb_regmap); + return err; + } + + platform_set_drvdata(pdev, cbdev); + list_add_tail(&cbdev->node, &cb_devlist); + + /* INIT LIST HEAD */ + INIT_LIST_HEAD(&cbdev->cb_entries); + + mutex_init(&cbdev->cb_lock); + + /* map the cross bar entries passed as default from DT */ + err = crossbar_map(cbdev_node); + + return err; +} + +#ifdef CONFIG_OF +static const struct of_device_id crossbar_match[] = { + {.compatible = "crossbar", }, + {}, +}; +#endif + +static struct platform_driver crossbar_driver = { + .probe = crossbar_probe, + .driver = { + .name = "crossbar", + .owner = THIS_MODULE, + .of_match_table = crossbar_match, + }, +}; + +static int __init crossbar_init(void) +{ + return platform_driver_register(&crossbar_driver); +} +postcore_initcall(crossbar_init); diff --git a/include/linux/crossbar.h b/include/linux/crossbar.h new file mode 100644 index 0000000..27ca735 --- /dev/null +++ b/include/linux/crossbar.h @@ -0,0 +1,71 @@ +/* + * IRQ/DMA CROSSBAR DRIVER + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ + * Sricharan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * @base: base address of the crossbar device + * @dev: device ptr + * @name: name of the crossbar device + * @node: list node for the crossbar devices linked list + * @cb_entries: list of entries that belong to the crossbar + * @cb_lock: mutex + * @regmap pointer + */ +struct cb_device { + void __iomem *base; + struct device *dev; + const char *name; + struct list_head node; + struct list_head cb_entries; + struct mutex cb_lock; + struct regmap *cb_regmap; +}; + +/* + * @cb_name: name of crossbar target to which this line is mapped + * @dev_name: mapped device input request name + * @cb_no: crossbar device input number + * @int_no: request number to which this input should be routed + * @offset: register offset address + */ +struct cb_line { + const char *cb_name; + const char *dev_name; + unsigned cb_no; + unsigned int_no; + unsigned offset; +}; + +struct cb_entry { + struct cb_line line; + struct list_head cb_list; +}; + +int crossbar_map(struct device_node *cbdev_node); +int crossbar_unmap(struct device_node *cbdev_node, unsigned index);