From patchwork Fri Apr 5 21:11:56 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ezequiel Garcia X-Patchwork-Id: 2400421 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) by patchwork1.kernel.org (Postfix) with ESMTP id 1A4A83FD8C for ; Fri, 5 Apr 2013 21:14:00 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UODxC-0006px-HS; Fri, 05 Apr 2013 21:13:48 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UODx9-0001Qb-SE; Fri, 05 Apr 2013 21:13:43 +0000 Received: from mail.free-electrons.com ([94.23.35.102]) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UODx5-0001QH-Tl for linux-arm-kernel@lists.infradead.org; Fri, 05 Apr 2013 21:13:41 +0000 Received: by mail.free-electrons.com (Postfix, from userid 106) id 1DC4E82E; Fri, 5 Apr 2013 23:13:39 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT, URIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.3.2 Received: from localhost.localdomain (20.222.3.200.ros.express.com.ar [200.3.222.20]) by mail.free-electrons.com (Postfix) with ESMTPA id BB25E81D; Fri, 5 Apr 2013 23:13:34 +0200 (CEST) From: Ezequiel Garcia To: Subject: [PATCH v2 1/5] drivers: memory: Introduce Marvell EBU Device Bus driver Date: Fri, 5 Apr 2013 18:11:56 -0300 Message-Id: <1365196320-15084-2-git-send-email-ezequiel.garcia@free-electrons.com> X-Mailer: git-send-email 1.8.1.5 In-Reply-To: <1365196320-15084-1-git-send-email-ezequiel.garcia@free-electrons.com> References: <1365196320-15084-1-git-send-email-ezequiel.garcia@free-electrons.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130405_171340_150735_276605F8 X-CRM114-Status: GOOD ( 27.94 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- 1.7 KHOP_BIG_TO_CC Sent to 10+ recipients instaed of Bcc or a list 0.7 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) -2.4 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Lior Amsalem , Thomas Petazzoni , Jason Cooper , Arnd Bergmann , Maen Suleiman , Javier Martinez Canillas , Jason Gunthorpe , olof@lixom.net, Andrew Lunn , Jon Hunter , Ezequiel Garcia , Gregory Clement , Florian Fainelli , sebastian.hesselbarth@gmail.com X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 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 Marvell EBU SoCs such as Armada 370/XP, Orion5x (88f5xxx) and Discovery (mv78xx0) supports a Device Bus controller to access several kinds of memories and I/O devices (NOR, NAND, SRAM, FPGA). This commit adds a driver to handle this controller. So far only Armada 370, Armada XP SoCs are supported. The driver must be registered through a device tree node; as explained in the binding document. For each child node in the device tree, this driver will: * set timing parameters * register a child device * setup an address decoding window, using the mbus driver Keep in mind the address decoding window setup is only a temporary hack. This code will be removed from this devbus driver as soon as a proper device tree binding for the mbus driver is added. Signed-off-by: Ezequiel Garcia --- .../bindings/memory-controllers/mvebu-devbus.txt | 151 ++++++++++ drivers/memory/Kconfig | 10 + drivers/memory/Makefile | 1 + drivers/memory/mvebu-devbus.c | 330 +++++++++++++++++++++ 4 files changed, 492 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt create mode 100644 drivers/memory/mvebu-devbus.c diff --git a/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt new file mode 100644 index 0000000..5b78940 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt @@ -0,0 +1,151 @@ +Device tree bindings for MVEBU Device Bus controllers + +The Device Bus controller available in some Marvell's SoC allows to control +different types of standard memory and I/O devices such as NOR, NAND, and FPGA. +The actual devices are instantiated from the child nodes of a Device Bus node. + +Required properties: + + - compatible: Should be set to one of the following: + + marvell,armada370-devbus + marvell,armadaxp-devbus + marvell,mv78xx0-devbus + + - reg: A resource specifier for the register space + (see the example below) + - #address-cells: Must be set to 2 to allow memory address translation + - #size-cells: Must be set to 1 to allow CS address passing + - ranges: Must be set up to reflect the memory layout with four + integer values for each chip-select line in use: + 0 + +Mandatory timing properties for child nodes: + +Read parameters: + + - devbus,turn-off-ps: Defines the time during which the controller does not + drive the AD bus after the completion of a device read. + This prevents contentions on the Device Bus after a read + cycle from a slow device. + + - devbus,dev-width: 0x0 = 8-bit + 0x1 = 16-bit + 0x2 = 32-bit + 0x3 = Reserved + + - devbus,badr-skew-ps: Defines the time delay from from A[2:0] toggle, + to read data sample. This parameter is useful for + synchronous pipelined devices, where the address + precedes the read data by one or two cycles. + + - devbus,acc-first-ps: Defines the time delay from the negation of + ALE[0] to the cycle that the first read data is sampled + by the controller. + + - devbus,acc-next-ps: Defines the time delay between the cycle that + samples data N and the cycle that samples data N+1 + (in burst accesses). + + - devbus,rd-setup-ps: Defines the time delay between DEV_CSn assertion to + DEV_OEn assertion. If set to 0 (default), + DEV_OEn and DEV_CSn are asserted at the same cycle. + This parameter has no affect on parameter + (no affect on first data sample). Set + to a value smaller than . + + - devbus,rd-hold-ps: Defines the time between the last data sample to the + de-assertion of DEV_CSn. If set to 0 (default), + DEV_OEn and DEV_CSn are de-asserted at the same cycle + (the cycle of the last data sample). + This parameter has no affect on DEV_OEn de-assertion. + DEV_OEn is always de-asserted the next cycle after + last data sampled. Also this parameter has no + affect on parameter. + Set to a value smaller than . + +Write parameters: + + - devbus,ale-wr-ps: Defines the time delay from the ALE[0] negation cycle + to the DEV_WEn assertion. + + - devbus,wr-low-ps: Defines the time during which DEV_WEn is active. + A[2:0] and Data are kept valid as long as DEV_WEn + is active. This parameter defines the setup time of + address and data to DEV_WEn rise. + + - devbus,wr-high-ps: Defines the time during which DEV_WEn is kept + inactive (high) between data beats of a burst write. + DEV_A[2:0] and Data are kept valid (do not toggle) for + - ps. + This parameter defines the hold time of address and + data after DEV_WEn rise. + + - devbus,sync-enable: Synchronous device enable. + 1: True + 0: False + +An example for an Armada XP GP board, with a 16 MiB NOR device as child +is showed below. Note that the Device Bus driver is in charge of allocating +the mbus address decoding window for each of its child devices. +The window is created using the chip select specified in the child +device node together with the base address and size specified in the ranges +property. For instance, in the example below the allocated decoding window +will start at base address 0xf0000000, with a size 0x1000000 (16 MiB) +for chip select 0 (a.k.a DEV_BOOTCS). + +This address window handling is done in this mvebu-devbus only as a temporary +solution. It will be removed when the support for mbus device tree binding is +added. + +The reg property must specify the chip select as: + + 0: DEV_BOOTCS + 1: DEV_CS0 + 2: DEV_CS1 + 3: DEV_CS2 + 4: DEV_CS3 + +Example: + + device-bus@d0010400 { + status = "okay"; + ranges = <0 0 0xf0000000 0x1000000>; /* CS0 @addr 0xf0000000, size 0x1000000 */ + #address-cells = <2>; + #size-cells = <1>; + + nor@0 { + compatible = "cfi-flash"; + + /* CS0, 16 MiB */ + reg = <0 0 0x1000000>; + bank-width = <2>; + #address-cells = <1>; + #size-cells = <1>; + + /* Device Bus parameters are required */ + devbus,dev-width = <1>; + + /* Read parameters */ + devbus,turn-off-ps = <60000>; + devbus,badr-skew-ps = <0>; + devbus,acc-first-ps = <124000>; + devbus,acc-next-ps = <248000>; + devbus,rd-setup-ps = <0>; + devbus,rd-hold-ps = <0>; + + /* + * We split the 16 MiB in two partitions, + * just as an example. + */ + partition@0 { + label = "First"; + reg = <0 0x800000>; + }; + + partition@800000 { + label = "Second"; + reg = <0x800000 0x800000>; + }; + }; + }; diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 067f311..9a81ab3 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -20,6 +20,16 @@ config TI_EMIF parameters and other settings during frequency, voltage and temperature changes +config MVEBU_DEVBUS + tristate "Marvell EBU Device Bus Controller" + default y + depends on PLAT_ORION && OF + help + This driver is for the Device Bus controller available in some + Marvell EBU SoCs such as Discovery (mv78xx0), Orion (88f5xxx) and + Armada 370 and Armada XP. This controller allows to handle flash + devices such as NOR, NAND, SRAM, and FPGA. + config TEGRA20_MC bool "Tegra20 Memory Controller(MC) driver" default y diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 9cce5d7..969d923 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -6,5 +6,6 @@ ifeq ($(CONFIG_DDR),y) obj-$(CONFIG_OF) += of_memory.o endif obj-$(CONFIG_TI_EMIF) += emif.o +obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c new file mode 100644 index 0000000..2a4c455 --- /dev/null +++ b/drivers/memory/mvebu-devbus.c @@ -0,0 +1,330 @@ +/* + * Marvell EBU SoC Device Bus Controller + * (memory controller for NOR/NAND/SRAM/FPGA devices) + * + * Copyright (C) 2013 Marvell + * + * 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 version 2 of the License. + * + * 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define DEV_WIDTH_BIT 30 +#define BADR_SKEW_BIT 28 +#define RD_HOLD_BIT 23 +#define ACC_NEXT_BIT 17 +#define RD_SETUP_BIT 12 +#define ACC_FIRST_BIT 6 + +#define SYNC_ENABLE_BIT 24 +#define WR_HIGH_BIT 16 +#define WR_LOW_BIT 8 + +#define READ_PARAM_OFFSET(cs) (0x0 + (cs << 3)) +#define WRITE_PARAM_OFFSET(cs) (0x4 + (cs << 3)) + +#define DEVBUS_MAXCHILDS_NR 5 + +static const char * const devbus_wins[] = { + "devbus-boot", + "devbus-cs0", + "devbus-cs1", + "devbus-cs2", + "devbus-cs3", +}; + +struct devbus_read_params { + u32 dev_width; + u32 badr_skew; + u32 turn_off; + u32 acc_first; + u32 acc_next; + u32 rd_setup; + u32 rd_hold; +}; + +struct devbus_write_params { + u32 sync_enable; + u32 wr_high; + u32 wr_low; + u32 ale_wr; +}; + +struct devbus_child { + struct platform_device *pdev; + struct resource mem_res; +}; + +struct devbus { + struct device *dev; + void __iomem *base; + unsigned long tick_ps; + + struct devbus_child childs[DEVBUS_MAXCHILDS_NR]; +}; + +static void get_timing_param_ps(struct devbus *devbus, + struct device_node *node, + const char *name, + u32 *ticks) +{ + u32 time_ps; + + of_property_read_u32(node, name, &time_ps); + + *ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps; + + dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n", + name, time_ps, *ticks); +} + +static void devbus_set_timing_params(struct devbus *devbus, + struct device_node *node, + int cs) +{ + struct devbus_read_params r; + struct devbus_write_params w; + u32 value; + + dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n", + devbus->tick_ps); + + of_property_read_u32(node, "devbus,dev-width", &r.dev_width); + + /* Get read timings */ + get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", &r.badr_skew); + get_timing_param_ps(devbus, node, "devbus,turn-off-ps", &r.turn_off); + get_timing_param_ps(devbus, node, "devbus,acc-first-ps", &r.acc_first); + get_timing_param_ps(devbus, node, "devbus,acc-next-ps", &r.acc_next); + get_timing_param_ps(devbus, node, "devbus,rd-setup-ps", &r.rd_setup); + get_timing_param_ps(devbus, node, "devbus,rd-hold-ps", &r.rd_hold); + + /* Get write timings */ + get_timing_param_ps(devbus, node, "devbus,ale-wr-ps", &w.ale_wr); + get_timing_param_ps(devbus, node, "devbus,wr-low-ps", &w.wr_low); + get_timing_param_ps(devbus, node, "devbus,wr-high-ps", &w.wr_high); + + of_property_read_u32(node, "devbus,sync-enable", &w.sync_enable); + + /* Set read timings */ + value = r.dev_width << DEV_WIDTH_BIT | + r.badr_skew << BADR_SKEW_BIT | + r.rd_hold << RD_HOLD_BIT | + r.acc_next << ACC_NEXT_BIT | + r.rd_setup << RD_SETUP_BIT | + r.acc_first << ACC_FIRST_BIT | + r.turn_off; + + dev_dbg(devbus->dev, "CS%d read parameters register 0x%p = 0x%x\n", + cs, devbus->base + READ_PARAM_OFFSET(cs), + value); + + writel(value, devbus->base + READ_PARAM_OFFSET(cs)); + + /* Set write timings */ + value = w.sync_enable << SYNC_ENABLE_BIT | + w.wr_low << WR_LOW_BIT | + w.wr_high << WR_HIGH_BIT | + w.ale_wr; + + dev_dbg(devbus->dev, "CS%d write parameters register: 0x%p = 0x%x\n", + cs, devbus->base + WRITE_PARAM_OFFSET(cs), + value); + + writel(value, devbus->base + WRITE_PARAM_OFFSET(cs)); +} + +static int devbus_probe_nor_child(struct devbus *devbus, + struct platform_device *pdev, + struct device_node *node) +{ + struct device *dev = &pdev->dev; + struct devbus_child *child; + int err; + int bank_width; + u32 cs; + + /* Read chip select and size */ + if (of_property_read_u32(node, "reg", &cs) < 0) { + dev_err(dev, "%s has no 'reg' property\n", node->full_name); + return -ENODEV; + } + + /* Sanity checks */ + if (cs >= DEVBUS_MAXCHILDS_NR) + return -ENODEV; + + if (devbus->childs[cs].pdev) + return -EBUSY; + + child = &devbus->childs[cs]; + + if (of_address_to_resource(node, 0, &child->mem_res)) { + dev_err(dev, "%s has malformed 'reg' property\n", + node->full_name); + return -ENODEV; + } + + err = of_property_read_u32(node, "bank-width", &bank_width); + if (err) { + dev_err(dev, "error %d reading bank-width property\n", err); + return err; + } + + /* + * Allocate an address window for this device. + * If the device probing fails, then we won't be able to + * remove the allocated address decoding window. + * + * FIXME: This is only a temporary hack! We need to do this here + * because we still don't have device tree bindings for mbus. + * Once that support is added, we will declare these address windows + * statically in the device tree, and remove the window configuration + * from here. + */ + err = mvebu_mbus_add_window(devbus_wins[cs], child->mem_res.start, + resource_size(&child->mem_res)); + if (err < 0) + return err; + + /* Read the device tree child node and set the new parameters */ + devbus_set_timing_params(devbus, node, cs); + + /* + * If we manage to set 'simple-bus' compatible string + * to device-bus node, then we don't really need this. + */ + child->pdev = of_platform_device_create(node, NULL, &pdev->dev); + if (!child->pdev) { + dev_warn(dev, "cannot create child device %s\n", node->name); + /* Remove the allocated window */ + mvebu_mbus_del_window(child->mem_res.start, + resource_size(&child->mem_res)); + } + + return 0; +} + +static int mvebu_devbus_probe(struct platform_device *pdev) +{ + struct device_node *child; + struct devbus *devbus; + struct resource *res; + struct clk *clk; + unsigned long rate; + int err; + + devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL); + if (!devbus) + return -ENOMEM; + + devbus->dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + devbus->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(devbus->base)) + return PTR_ERR(devbus->base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clk_prepare_enable(clk); + + /* + * Obtain clock period in picoseconds, + * we need this in order to convert timing + * parameters from cycles to picoseconds. + */ + rate = clk_get_rate(clk) / 1000; + devbus->tick_ps = 1000000000 / rate; + + /* + * We probe NOR/NAND with different functions, because + * we expect them to have some different parameters. + * If this turns out not to be the case, we'll be able + * to use any name for the child, and rename to devbus_probe_child(). + */ + for_each_node_by_name(child, "nor") { + err = devbus_probe_nor_child(devbus, pdev, child); + if (err < 0) { + of_node_put(child); + return err; + } + } + + platform_set_drvdata(pdev, devbus); + + return 0; +} + +static int mvebu_devbus_remove(struct platform_device *pdev) +{ + int i; + struct devbus *devbus = platform_get_drvdata(pdev); + + for (i = 0; i < DEVBUS_MAXCHILDS_NR; i++) { + struct devbus_child *child; + + if (!devbus->childs[i].pdev) + continue; + + child = &devbus->childs[i]; + + /* Release device */ + of_device_unregister(child->pdev); + + /* Release window */ + mvebu_mbus_del_window(child->mem_res.start, + resource_size(&child->mem_res)); + + child->pdev = NULL; + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/* Perhaps it makes sense to unify both compatible strins? */ +static const struct of_device_id mvebu_devbus_of_match[] = { + { .compatible = "marvell,armada370-devbus" }, + { .compatible = "marvell,armadaxp-devbus" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match); + +static struct platform_driver mvebu_devbus_driver = { + .probe = mvebu_devbus_probe, + .remove = mvebu_devbus_remove, + .driver = { + .name = "mvebu-devbus", + .owner = THIS_MODULE, + .of_match_table = mvebu_devbus_of_match, + }, +}; + +module_platform_driver(mvebu_devbus_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ezequiel Garcia "); +MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller");