From mboxrd@z Thu Jan 1 00:00:00 1970 From: jason@lakedaemon.net (Jason Cooper) Date: Wed, 10 Apr 2013 15:16:41 -0400 Subject: [PATCH v7 1/5] drivers: memory: Introduce Marvell EBU Device Bus driver In-Reply-To: <1365620644-15790-2-git-send-email-ezequiel.garcia@free-electrons.com> References: <1365620644-15790-1-git-send-email-ezequiel.garcia@free-electrons.com> <1365620644-15790-2-git-send-email-ezequiel.garcia@free-electrons.com> Message-ID: <20130410191641.GG28693@titan.lakedaemon.net> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org gregkh, On Wed, Apr 10, 2013 at 04:04:00PM -0300, Ezequiel Garcia wrote: > 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 and Discovery 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 > Acked-by: Arnd Bergmann > --- > .../bindings/memory-controllers/mvebu-devbus.txt | 156 ++++++++++ > drivers/memory/Kconfig | 10 + > drivers/memory/Makefile | 1 + > drivers/memory/mvebu-devbus.c | 340 +++++++++++++++++++++ > 4 files changed, 507 insertions(+) > create mode 100644 Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt > create mode 100644 drivers/memory/mvebu-devbus.c As every commit to drivers/memory has been signed by you, would you like to take this patch through your tree? Or, I'll take it through arm-soc with your Ack to keep the series together. thx, Jason. > > 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..653c90c > --- /dev/null > +++ b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt > @@ -0,0 +1,156 @@ > +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: Currently only Armada 370/XP SoC are supported, > + with this compatible string: > + > + marvell,mvebu-devbus > + > + - reg: A resource specifier for the register space. > + This is the base address of a chip select within > + the controller's register space. > + (see the example below) > + > + - #address-cells: Must be set to 1 > + - #size-cells: Must be set to 1 > + - 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,bus-width: Defines the bus width (e.g. <16>) > + > + - 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 implicitly specifies the chip select as this: > + > + 0x10400: DEV_BOOTCS > + 0x10408: DEV_CS0 > + 0x10410: DEV_CS1 > + 0x10418: DEV_CS2 > + 0x10420: DEV_CS3 > + > +Example: > + > + devbus-bootcs at d0010400 { > + status = "okay"; > + ranges = <0 0xf0000000 0x1000000>; /* @addr 0xf0000000, size 0x1000000 */ > + #address-cells = <1>; > + #size-cells = <1>; > + > + /* Device Bus parameters are required */ > + > + /* Read parameters */ > + devbus,bus-width = <8>; > + 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>; > + > + /* Write parameters */ > + devbus,sync-enable = <0>; > + devbus,wr-high-ps = <60000>; > + devbus,wr-low-ps = <60000>; > + devbus,ale-wr-ps = <60000>; > + > + flash at 0 { > + compatible = "cfi-flash"; > + > + /* 16 MiB */ > + reg = <0 0x1000000>; > + bank-width = <2>; > + #address-cells = <1>; > + #size-cells = <1>; > + > + /* > + * We split the 16 MiB in two partitions, > + * just as an example. > + */ > + partition at 0 { > + label = "First"; > + reg = <0 0x800000>; > + }; > + > + partition at 800000 { > + label = "Second"; > + reg = <0x800000 0x800000>; > + }; > + }; > + }; > diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig > index 067f311..29a11db 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 > + bool "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..978e8e3 > --- /dev/null > +++ b/drivers/memory/mvebu-devbus.c > @@ -0,0 +1,340 @@ > +/* > + * 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 0x0 > +#define WRITE_PARAM_OFFSET 0x4 > + > +static const char * const devbus_wins[] = { > + "devbus-boot", > + "devbus-cs0", > + "devbus-cs1", > + "devbus-cs2", > + "devbus-cs3", > +}; > + > +struct devbus_read_params { > + u32 bus_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 { > + struct device *dev; > + void __iomem *base; > + unsigned long tick_ps; > +}; > + > +static int get_timing_param_ps(struct devbus *devbus, > + struct device_node *node, > + const char *name, > + u32 *ticks) > +{ > + u32 time_ps; > + int err; > + > + err = of_property_read_u32(node, name, &time_ps); > + if (err < 0) { > + dev_err(devbus->dev, "%s has no '%s' property\n", > + name, node->full_name); > + return err; > + } > + > + *ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps; > + > + dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n", > + name, time_ps, *ticks); > + return 0; > +} > + > +static int devbus_set_timing_params(struct devbus *devbus, > + struct device_node *node) > +{ > + struct devbus_read_params r; > + struct devbus_write_params w; > + u32 value; > + int err; > + > + dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n", > + devbus->tick_ps); > + > + /* Get read timings */ > + err = of_property_read_u32(node, "devbus,bus-width", &r.bus_width); > + if (err < 0) { > + dev_err(devbus->dev, > + "%s has no 'devbus,bus-width' property\n", > + node->full_name); > + return err; > + } > + /* Convert bit width to byte width */ > + r.bus_width /= 8; > + > + err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", > + &r.badr_skew); > + if (err < 0) > + return err; > + > + err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps", > + &r.turn_off); > + if (err < 0) > + return err; > + > + err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps", > + &r.acc_first); > + if (err < 0) > + return err; > + > + err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps", > + &r.acc_next); > + if (err < 0) > + return err; > + > + err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps", > + &r.rd_setup); > + if (err < 0) > + return err; > + > + err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps", > + &r.rd_hold); > + if (err < 0) > + return err; > + > + /* Get write timings */ > + err = of_property_read_u32(node, "devbus,sync-enable", > + &w.sync_enable); > + if (err < 0) { > + dev_err(devbus->dev, > + "%s has no 'devbus,sync-enable' property\n", > + node->full_name); > + return err; > + } > + > + err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps", > + &w.ale_wr); > + if (err < 0) > + return err; > + > + err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps", > + &w.wr_low); > + if (err < 0) > + return err; > + > + err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps", > + &w.wr_high); > + if (err < 0) > + return err; > + > + /* Set read timings */ > + value = r.bus_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, "read parameters register 0x%p = 0x%x\n", > + devbus->base + READ_PARAM_OFFSET, > + value); > + > + writel(value, devbus->base + READ_PARAM_OFFSET); > + > + /* 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, "write parameters register: 0x%p = 0x%x\n", > + devbus->base + WRITE_PARAM_OFFSET, > + value); > + > + writel(value, devbus->base + WRITE_PARAM_OFFSET); > + > + return 0; > +} > + > +static int mvebu_devbus_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *node = pdev->dev.of_node; > + struct device_node *parent; > + struct devbus *devbus; > + struct resource *res; > + struct clk *clk; > + unsigned long rate; > + const __be32 *ranges; > + int err, cs; > + int addr_cells, p_addr_cells, size_cells; > + int ranges_len, tuple_len; > + u32 base, size; > + > + devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL); > + if (!devbus) > + return -ENOMEM; > + > + devbus->dev = 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; > + > + /* Read the device tree node and set the new timing parameters */ > + err = devbus_set_timing_params(devbus, node); > + if (err < 0) > + 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. > + */ > + > + /* > + * Get the CS to choose the window string. > + * This is a bit hacky, but it will be removed once the > + * address windows are declared in the device tree. > + */ > + cs = (((unsigned long)devbus->base) % 0x400) / 8; > + > + /* > + * Parse 'ranges' property to obtain a (base,size) window tuple. > + * This will be removed once the address windows > + * are declared in the device tree. > + */ > + parent = of_get_parent(node); > + if (!parent) > + return -EINVAL; > + > + p_addr_cells = of_n_addr_cells(parent); > + of_node_put(parent); > + > + addr_cells = of_n_addr_cells(node); > + size_cells = of_n_size_cells(node); > + tuple_len = (p_addr_cells + addr_cells + size_cells) * sizeof(__be32); > + > + ranges = of_get_property(node, "ranges", &ranges_len); > + if (ranges == NULL || ranges_len != tuple_len) > + return -EINVAL; > + > + base = of_translate_address(node, ranges + addr_cells); > + if (base == OF_BAD_ADDR) > + return -EINVAL; > + size = of_read_number(ranges + addr_cells + p_addr_cells, size_cells); > + > + /* > + * Create an mbus address windows. > + * FIXME: Remove this, together with the above code, once the > + * address windows are declared in the device tree. > + */ > + err = mvebu_mbus_add_window(devbus_wins[cs], base, size); > + if (err < 0) > + return err; > + > + /* > + * We need to create a child device explicitly from here to > + * guarantee that the child will be probed after the timing > + * parameters for the bus are written. > + */ > + err = of_platform_populate(node, NULL, NULL, dev); > + if (err < 0) { > + mvebu_mbus_del_window(base, size); > + return err; > + } > + > + return 0; > +} > + > +static const struct of_device_id mvebu_devbus_of_match[] = { > + { .compatible = "marvell,mvebu-devbus" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match); > + > +static struct platform_driver mvebu_devbus_driver = { > + .probe = mvebu_devbus_probe, > + .driver = { > + .name = "mvebu-devbus", > + .owner = THIS_MODULE, > + .of_match_table = mvebu_devbus_of_match, > + }, > +}; > + > +static int __init mvebu_devbus_init(void) > +{ > + return platform_driver_register(&mvebu_devbus_driver); > +} > +module_init(mvebu_devbus_init); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_AUTHOR("Ezequiel Garcia "); > +MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller"); > -- > 1.8.1.5 >