From patchwork Tue Nov 24 09:39:19 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masahiro Yamada X-Patchwork-Id: 7689091 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id AA335BF90C for ; Tue, 24 Nov 2015 09:45:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5676B20549 for ; Tue, 24 Nov 2015 09:45:53 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E42BD20547 for ; Tue, 24 Nov 2015 09:45:51 +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 1a1A8o-0003fr-J6; Tue, 24 Nov 2015 09:44:02 +0000 Received: from conuserg010.nifty.com ([202.248.44.36] helo=conuserg010-v.nifty.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1a1A4k-0008Fu-JQ for linux-arm-kernel@lists.infradead.org; Tue, 24 Nov 2015 09:40:01 +0000 Received: from beagle.diag.org (p14090-ipngnfx01kyoto.kyoto.ocn.ne.jp [153.142.97.90]) (authenticated) by conuserg010-v.nifty.com with ESMTP id tAO9d6GU026852; Tue, 24 Nov 2015 18:39:08 +0900 X-Nifty-SrcIP: [153.142.97.90] From: Masahiro Yamada To: arm@kernel.org Subject: [PATCH 1/4] bus: uniphier-system-bus: add UniPhier System Bus Controller driver Date: Tue, 24 Nov 2015 18:39:19 +0900 Message-Id: <1448357962-12259-2-git-send-email-yamada.masahiro@socionext.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1448357962-12259-1-git-send-email-yamada.masahiro@socionext.com> References: <1448357962-12259-1-git-send-email-yamada.masahiro@socionext.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151124_013951_233047_08C74525 X-CRM114-Status: GOOD ( 27.04 ) X-Spam-Score: -1.2 (-) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, Pawel Moll , Ian Campbell , linux-kernel@vger.kernel.org, Masahiro Yamada , Rob Herring , Kumar Gala , linux-arm-kernel@lists.infradead.org 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.8 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, 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 The UniPhier System Bus is an external bus where on-board devices are connected to the UniPhier SoC. This driver parses the "ranges" property of the System Bus and set up the registers of the System Bus Controller for the correct bus routing. Signed-off-by: Masahiro Yamada --- .../bindings/arm/uniphier/uniphier-system-bus.txt | 70 +++++ MAINTAINERS | 1 + drivers/bus/Kconfig | 9 + drivers/bus/Makefile | 1 + drivers/bus/uniphier-system-bus.c | 284 +++++++++++++++++++++ 5 files changed, 365 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt create mode 100644 drivers/bus/uniphier-system-bus.c diff --git a/Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt b/Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt new file mode 100644 index 0000000..a50db0a --- /dev/null +++ b/Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt @@ -0,0 +1,70 @@ +UniPhier System Bus +------------------- + +The UniPhier System Bus is an external bus where on-board devices are connected +to the UniPhier SoC. It it a simple parallel bus with address, data, and some +control signals. It supports up to 8 banks (chip selects). + +Required properties for System Bus: +- compatible: should be "socionext,uniphier-system-bus", "simple-bus". +- #address-cells: should be equal to or grater than 2. The first cell is the + bank number (chip select). The rest is the base address within the bank that + should be mapped onto the parent bus (usually AMBA). + +Optional properties for System Bus: +- #size-cells: should be the same as that of the parent bus, if exists. The + value of the parent bus is assumed, if not specified. + + +UniPhier System Bus Controller +------------------------------ + +The UniPhier System Bus Controller is a hardware block with registers that +controls the System Bus accessing; how each bank is mapped onto the parent bus, +various timing parameters of the bus access, etc. + +Required properties for System Bus Controller: +- compatible: should be "socionext,uniphier-system-bus-controller". +- reg: offsets and lengths of the register sets for the device. It should + contain 2 regions: base & control register, misc register, in this order. + + +Example +------- + system_bus: system-bus { + compatible = "socionext,uniphier-system-bus", "simple-bus"; + #address-cells = <2>; + #size-cells = <1>; + ranges = <1 0x00000000 0x42000000 0x02000000 + 5 0x00000000 0x48000000 0x01000000>; + + eth: ethernet@1,01f00000 { + compatible = "smsc,lan9115"; + reg = <1 0x01f00000 0x1000>; + phy-mode = "mii"; + reg-io-width = <4>; + }; + + serial: uart@5,00200000 { + compatible = "ns16550a"; + reg = <5 0x00200000 0x20>; + clock-frequency = <12288000>; + reg-shift = <1>; + }; + }; + + system-bus-controller@58c00000 { + compatible = "socionext,uniphier-system-bus-controller"; + reg = <0x58c00000 0x400>, <0x59800000 0x2000>; + system-bus = <&system_bus>; + }; + +In this example, the range 0x00000000-0x02000000 of bank 1 (CS1) is mapped to +the range 0x42000000-0x44000000 of the parent bus. +Likewise, the range 0x00000000-0x01000000 of bank 5 (CS5) is mapped to the +range 0x48000000-0x49000000 of the parent bus. + +The Ethernet device is connected at the address 0x01f00000 of CS1, and visible +at the address 0x43f00000 in the parent bus. The UART device is connected at +the address 0x00200000 of CS5, and visible at the address 0x48200000 in the +parent bus. diff --git a/MAINTAINERS b/MAINTAINERS index 050d0e7..e849a38 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1642,6 +1642,7 @@ F: arch/arm/boot/dts/uniphier* F: arch/arm/include/asm/hardware/cache-uniphier.h F: arch/arm/mach-uniphier/ F: arch/arm/mm/cache-uniphier.c +F: drivers/bus/uniphier-system-bus.c F: drivers/i2c/busses/i2c-uniphier* F: drivers/pinctrl/uniphier/ F: drivers/tty/serial/8250/8250_uniphier.c diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 116b363..5a772fc 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -131,6 +131,15 @@ config SUNXI_RSB with various RSB based devices, such as AXP223, AXP8XX PMICs, and AC100/AC200 ICs. +config UNIPHIER_SYSTEM_BUS + tristate "UniPhier System Bus Controller" + depends on ARCH_UNIPHIER + default y + help + Driver to set up the System Bus Controller on UniPhier SoCs. + This is needed to use on-board devices connected to the System Bus + (external bus) of UniPhier SoCs. + config VEXPRESS_CONFIG bool "Versatile Express configuration bus" default y if ARCH_VEXPRESS diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index fcb9f97..ccff007 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -17,4 +17,5 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o +obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c new file mode 100644 index 0000000..40cbf9e --- /dev/null +++ b/drivers/bus/uniphier-system-bus.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2015 Masahiro Yamada + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define UNIPHIER_SBC_NR_BANKS 8 /* number of banks (chip select) */ + +#define UNIPHIER_SBC_BASE 0x100 /* base address of bank0 space */ +#define UNIPHIER_SBC_BASE_BE BIT(0) /* bank_enable */ +#define UNIPHIER_SBC_CTRL0 0x200 /* timing parameter 0 of bank0 */ +#define UNIPHIER_SBC_CTRL1 0x204 /* timing parameter 1 of bank0 */ +#define UNIPHIER_SBC_CTRL2 0x208 /* timing parameter 2 of bank0 */ +#define UNIPHIER_SBC_CTRL3 0x20c /* timing parameter 3 of bank0 */ +#define UNIPHIER_SBC_CTRL4 0x300 /* timing parameter 4 of bank0 */ + +#define UNIPHIER_SBC_STRIDE 0x10 /* register stride to next bank */ + +struct uniphier_sbc_bank { + u32 base; + u32 end; +}; + +struct uniphier_sbc_priv { + struct device *dev; + void __iomem *membase; + struct uniphier_sbc_bank bank[UNIPHIER_SBC_NR_BANKS]; +}; + +static void uniphier_sbc_set_reg(struct uniphier_sbc_priv *priv) +{ + void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE; + u32 base, end, mask, val; + int i; + + for (i = 0; i < ARRAY_SIZE(priv->bank); i++) { + base = priv->bank[i].base; + end = priv->bank[i].end; + + if (base == end) + continue; + + mask = base ^ (end - 1); + + val = base & 0xfffe0000; + val |= (~mask >> 16) & 0xfffe; + val |= UNIPHIER_SBC_BASE_BE; + + dev_dbg(priv->dev, "SBC_BASE[%d] = 0x%08x\n", i, val); + + writel(val, base_reg + UNIPHIER_SBC_STRIDE * i); + } +} + +static void uniphier_sbc_check_boot_swap(struct uniphier_sbc_priv *priv) +{ + void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE; + int is_swapped; + + is_swapped = !(readl(base_reg) & UNIPHIER_SBC_BASE_BE); + + dev_dbg(priv->dev, "Boot Swap: %s\n", is_swapped ? "on" : "off"); + + if (is_swapped) + swap(priv->bank[0], priv->bank[1]); +} + +static int uniphier_sbc_get_cells(struct device_node *np, int *child_addrc, + int *addrc, int *sizec) +{ + u32 cells; + int ret; + + *addrc = of_n_addr_cells(np); + *sizec = of_n_size_cells(np); + + ret = of_property_read_u32(np, "#address-cells", &cells); + if (ret) + return ret; + + *child_addrc = cells; + if (*child_addrc <= 1) + return -EINVAL; + + ret = of_property_read_u32(np, "#size-cells", &cells); + if (!ret && cells != *sizec) + return -EINVAL; + + return 0; +} + +static int uniphier_sbc_add_bank(struct uniphier_sbc_priv *priv, int bank, + u64 child_addr, u64 addr, u64 size) +{ + u64 end, mask; + + dev_dbg(priv->dev, + "range found: bank = %d, caddr = %08llx, addr = %08llx, size = %08llx\n", + bank, child_addr, addr, size); + + if (bank >= ARRAY_SIZE(priv->bank)) { + dev_err(priv->dev, "unsupported bank number %d\n", bank); + return -EINVAL; + } + + if (priv->bank[bank].base || priv->bank[bank].end) { + dev_err(priv->dev, + "range for bank %d has already been specified\n", bank); + return -EINVAL; + } + + if (addr > U32_MAX) { + dev_err(priv->dev, "base address %llx is too high\n", addr); + return -EINVAL; + } + + end = addr + size; + + if (child_addr > addr) { + dev_err(priv->dev, + "base %llx cannot be mapped to %llx of parent\n", + child_addr, addr); + return -EINVAL; + } + addr -= child_addr; + + addr = round_down(addr, 0x00020000); + end = round_up(end, 0x00020000); + + if (end > U32_MAX) { + dev_err(priv->dev, "end address %llx is too high\n", end); + return -EINVAL; + } + mask = addr ^ (end - 1); + mask = roundup_pow_of_two(mask); + + addr = round_down(addr, mask); + end = round_up(end, mask); + + priv->bank[bank].base = addr; + priv->bank[bank].end = end; + + dev_dbg(priv->dev, "range added: bank = %d, addr = %08x, end = %08x\n", + bank, priv->bank[bank].base, priv->bank[bank].end); + + return 0; +} + +static int uniphier_sbc_sort_cmp(const void *a, const void *b) +{ + return ((struct uniphier_sbc_bank *)a)->base + - ((struct uniphier_sbc_bank *)b)->base; +} + +static int uniphier_sbc_check_overlap(struct uniphier_sbc_priv tmp) +{ + int i; + + sort(&tmp.bank, ARRAY_SIZE(tmp.bank), sizeof(tmp.bank[0]), + uniphier_sbc_sort_cmp, NULL); + + for (i = 0; i < ARRAY_SIZE(tmp.bank) - 1; i++) + if (tmp.bank[i].end > tmp.bank[i + 1].base) { + dev_err(tmp.dev, + "region overlap between %08x-%08x and %08x-%08x\n", + tmp.bank[i].base, tmp.bank[i].end, + tmp.bank[i + 1].base, tmp.bank[i + 1].end); + return -EINVAL; + } + + return 0; +} + +static int uniphier_sbc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_sbc_priv *priv; + struct resource *regs; + struct device_node *bus_np; + int child_addrc, addrc, sizec, bank; + u64 child_addr, addr, size; + const __be32 *ranges; + int rlen, rone, ret; + + bus_np = of_find_compatible_node(NULL, NULL, + "socionext,uniphier-system-bus"); + if (!bus_np) + return 0; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->membase = devm_ioremap_resource(dev, regs); + if (IS_ERR(priv->membase)) { + ret = PTR_ERR(priv->membase); + goto out; + } + + priv->dev = dev; + + ret = uniphier_sbc_get_cells(bus_np, &child_addrc, &addrc, &sizec); + if (ret) { + dev_err(dev, "wrong #address-cells or #size-cells for bus\n"); + goto out; + } + + ranges = of_get_property(bus_np, "ranges", &rlen); + if (!ranges) { + ret = -ENOENT; + goto out; + } + + rlen /= sizeof(*ranges); + rone = child_addrc + addrc + sizec; + + for (; rlen >= rone; rlen -= rone, ranges += rone) { + bank = be32_to_cpup(ranges); + child_addr = of_read_number(ranges + 1, child_addrc - 1); + addr = of_translate_address(bus_np, ranges + child_addrc); + if (addr == OF_BAD_ADDR) { + ret = -EINVAL; + goto out; + } + size = of_read_number(ranges + child_addrc + addrc, sizec); + + ret = uniphier_sbc_add_bank(priv, bank, child_addr, addr, size); + if (ret) + goto out; + } + + ret = uniphier_sbc_check_overlap(*priv); + if (ret) + goto out; + + uniphier_sbc_check_boot_swap(priv); + + uniphier_sbc_set_reg(priv); + +out: + of_node_put(bus_np); + + return ret; +} + + + +static const struct of_device_id uniphier_sbc_match[] = { + { .compatible = "socionext,uniphier-system-bus-controller" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_sbc_match); + +static struct platform_driver uniphier_sbc_driver = { + .probe = uniphier_sbc_probe, + .driver = { + .name = "system-bus-controller", + .of_match_table = uniphier_sbc_match, + }, +}; +module_platform_driver(uniphier_sbc_driver); + +MODULE_AUTHOR("Masahiro Yamada "); +MODULE_DESCRIPTION("UniPhier System Bus Controller driver"); +MODULE_LICENSE("GPL");