Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers: bus: add a new driver for WEIM

The WEIM(Wireless External Interface Module) works like a bus.
You can attach many different devices on it, such as NOR, onenand.

In the case of i.MX6q-sabreauto, the NOR is connected to WEIM.

This patch also adds the devicetree binding document.
The driver only works when the devicetree is enabled.

Signed-off-by: Huang Shijie <b32955@freescale.com>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>

authored by

Huang Shijie and committed by
Shawn Guo
85bf6d4e 81b8a3cd

+197
+49
Documentation/devicetree/bindings/bus/imx-weim.txt
··· 1 + Device tree bindings for i.MX Wireless External Interface Module (WEIM) 2 + 3 + The term "wireless" does not imply that the WEIM is literally an interface 4 + without wires. It simply means that this module was originally designed for 5 + wireless and mobile applications that use low-power technology. 6 + 7 + The actual devices are instantiated from the child nodes of a WEIM node. 8 + 9 + Required properties: 10 + 11 + - compatible: Should be set to "fsl,imx6q-weim" 12 + - reg: A resource specifier for the register space 13 + (see the example below) 14 + - clocks: the clock, see the example below. 15 + - #address-cells: Must be set to 2 to allow memory address translation 16 + - #size-cells: Must be set to 1 to allow CS address passing 17 + - ranges: Must be set up to reflect the memory layout with four 18 + integer values for each chip-select line in use: 19 + 20 + <cs-number> 0 <physical address of mapping> <size> 21 + 22 + Timing property for child nodes. It is mandatory, not optional. 23 + 24 + - fsl,weim-cs-timing: The timing array, contains 6 timing values for the 25 + child node. We can get the CS index from the child 26 + node's "reg" property. This property contains the values 27 + for the registers EIM_CSnGCR1, EIM_CSnGCR2, EIM_CSnRCR1, 28 + EIM_CSnRCR2, EIM_CSnWCR1, EIM_CSnWCR2 in this order. 29 + 30 + Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM: 31 + 32 + weim: weim@021b8000 { 33 + compatible = "fsl,imx6q-weim"; 34 + reg = <0x021b8000 0x4000>; 35 + clocks = <&clks 196>; 36 + #address-cells = <2>; 37 + #size-cells = <1>; 38 + ranges = <0 0 0x08000000 0x08000000>; 39 + 40 + nor@0,0 { 41 + compatible = "cfi-flash"; 42 + reg = <0 0 0x02000000>; 43 + #address-cells = <1>; 44 + #size-cells = <1>; 45 + bank-width = <2>; 46 + fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000 47 + 0x0000c000 0x1404a38e 0x00000000>; 48 + }; 49 + };
+9
drivers/bus/Kconfig
··· 4 4 5 5 menu "Bus devices" 6 6 7 + config IMX_WEIM 8 + bool "Freescale EIM DRIVER" 9 + depends on ARCH_MXC 10 + help 11 + Driver for i.MX6 WEIM controller. 12 + The WEIM(Wireless External Interface Module) works like a bus. 13 + You can attach many different devices on it, such as NOR, onenand. 14 + But now, we only support the Parallel NOR. 15 + 7 16 config MVEBU_MBUS 8 17 bool 9 18 depends on PLAT_ORION
+1
drivers/bus/Makefile
··· 2 2 # Makefile for the bus drivers. 3 3 # 4 4 5 + obj-$(CONFIG_IMX_WEIM) += imx-weim.o 5 6 obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o 6 7 obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o 7 8
+138
drivers/bus/imx-weim.c
··· 1 + /* 2 + * EIM driver for Freescale's i.MX chips 3 + * 4 + * Copyright (C) 2013 Freescale Semiconductor, Inc. 5 + * 6 + * This file is licensed under the terms of the GNU General Public 7 + * License version 2. This program is licensed "as is" without any 8 + * warranty of any kind, whether express or implied. 9 + */ 10 + #include <linux/module.h> 11 + #include <linux/clk.h> 12 + #include <linux/io.h> 13 + #include <linux/of_device.h> 14 + 15 + struct imx_weim { 16 + void __iomem *base; 17 + struct clk *clk; 18 + }; 19 + 20 + static const struct of_device_id weim_id_table[] = { 21 + { .compatible = "fsl,imx6q-weim", }, 22 + {} 23 + }; 24 + MODULE_DEVICE_TABLE(of, weim_id_table); 25 + 26 + #define CS_TIMING_LEN 6 27 + #define CS_REG_RANGE 0x18 28 + 29 + /* Parse and set the timing for this device. */ 30 + static int 31 + weim_timing_setup(struct platform_device *pdev, struct device_node *np) 32 + { 33 + struct imx_weim *weim = platform_get_drvdata(pdev); 34 + u32 value[CS_TIMING_LEN]; 35 + u32 cs_idx; 36 + int ret; 37 + int i; 38 + 39 + /* get the CS index from this child node's "reg" property. */ 40 + ret = of_property_read_u32(np, "reg", &cs_idx); 41 + if (ret) 42 + return ret; 43 + 44 + /* The weim has four chip selects. */ 45 + if (cs_idx > 3) 46 + return -EINVAL; 47 + 48 + ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", 49 + value, CS_TIMING_LEN); 50 + if (ret) 51 + return ret; 52 + 53 + /* set the timing for WEIM */ 54 + for (i = 0; i < CS_TIMING_LEN; i++) 55 + writel(value[i], weim->base + cs_idx * CS_REG_RANGE + i * 4); 56 + return 0; 57 + } 58 + 59 + static int weim_parse_dt(struct platform_device *pdev) 60 + { 61 + struct device_node *child; 62 + int ret; 63 + 64 + for_each_child_of_node(pdev->dev.of_node, child) { 65 + if (!child->name) 66 + continue; 67 + 68 + ret = weim_timing_setup(pdev, child); 69 + if (ret) { 70 + dev_err(&pdev->dev, "%s set timing failed.\n", 71 + child->full_name); 72 + return ret; 73 + } 74 + } 75 + 76 + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 77 + if (ret) 78 + dev_err(&pdev->dev, "%s fail to create devices.\n", 79 + pdev->dev.of_node->full_name); 80 + return ret; 81 + } 82 + 83 + static int weim_probe(struct platform_device *pdev) 84 + { 85 + struct imx_weim *weim; 86 + struct resource *res; 87 + int ret = -EINVAL; 88 + 89 + weim = devm_kzalloc(&pdev->dev, sizeof(*weim), GFP_KERNEL); 90 + if (!weim) { 91 + ret = -ENOMEM; 92 + goto weim_err; 93 + } 94 + platform_set_drvdata(pdev, weim); 95 + 96 + /* get the resource */ 97 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 98 + weim->base = devm_ioremap_resource(&pdev->dev, res); 99 + if (IS_ERR(weim->base)) { 100 + ret = PTR_ERR(weim->base); 101 + goto weim_err; 102 + } 103 + 104 + /* get the clock */ 105 + weim->clk = devm_clk_get(&pdev->dev, NULL); 106 + if (IS_ERR(weim->clk)) 107 + goto weim_err; 108 + 109 + ret = clk_prepare_enable(weim->clk); 110 + if (ret) 111 + goto weim_err; 112 + 113 + /* parse the device node */ 114 + ret = weim_parse_dt(pdev); 115 + if (ret) { 116 + clk_disable_unprepare(weim->clk); 117 + goto weim_err; 118 + } 119 + 120 + dev_info(&pdev->dev, "WEIM driver registered.\n"); 121 + return 0; 122 + 123 + weim_err: 124 + return ret; 125 + } 126 + 127 + static struct platform_driver weim_driver = { 128 + .driver = { 129 + .name = "imx-weim", 130 + .of_match_table = weim_id_table, 131 + }, 132 + .probe = weim_probe, 133 + }; 134 + 135 + module_platform_driver(weim_driver); 136 + MODULE_AUTHOR("Freescale Semiconductor Inc."); 137 + MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 138 + MODULE_LICENSE("GPL");