From 919c1e541dd766085546a620bd5569506d9033ce Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 31 Jan 2012 09:29:45 +0200 Subject: ASoC: ALC5632: Add device tree binding documentation Document the device tree binding for the ALC5632 codec and update vendor specific prefix for the Realtek. Signed-off-by: Leon Romanovsky Acked-by: Stephen Warren Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/alc5632.txt | 24 ++++++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + 2 files changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/alc5632.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/alc5632.txt b/Documentation/devicetree/bindings/sound/alc5632.txt new file mode 100644 index 000000000000..8608f747dcfe --- /dev/null +++ b/Documentation/devicetree/bindings/sound/alc5632.txt @@ -0,0 +1,24 @@ +ALC5632 audio CODEC + +This device supports I2C only. + +Required properties: + + - compatible : "realtek,alc5632" + + - reg : the I2C address of the device. + + - gpio-controller : Indicates this device is a GPIO controller. + + - #gpio-cells : Should be two. The first cell is the pin number and the + second cell is used to specify optional parameters (currently unused). + +Example: + +alc5632: alc5632@1e { + compatible = "realtek,alc5632"; + reg = <0x1a>; + + gpio-controller; + #gpio-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ecc6a6cd26c1..ca8ee14bf004 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -33,6 +33,7 @@ nxp NXP Semiconductors powervr Imagination Technologies qcom Qualcomm, Inc. ramtron Ramtron International +realtek Realtek Semiconductor Corp. samsung Samsung Semiconductor sbs Smart Battery System schindler Schindler -- cgit v1.2.3 From 761bfdd91184c6662a9233976e855b4ccb883c96 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 31 Jan 2012 09:30:40 +0200 Subject: ASoC: Tegra+ALC5632 machine: Add device tree binding documentation Document device tree binding for the tegra board with ALC5632 codec according to datasheet functional block description. Signed-off-by: Leon Romanovsky Acked-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/tegra-audio-alc5632.txt | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt new file mode 100644 index 000000000000..65b001e16517 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt @@ -0,0 +1,57 @@ +NVIDIA Tegra audio complex + +Required properties: +- compatible : "nvidia,tegra-audio-alc5632" +- nvidia,model : The user-visible name of this sound complex. +- nvidia,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the ALC5632's pins: + + ALC5632 pins: + + * SPK_OUTP + * SPK_OUTN + * HP_OUT_L + * HP_OUT_R + * AUX_OUT_P + * AUX_OUT_N + * LINE_IN_L + * LINE_IN_R + * PHONE_P + * PHONE_N + * MIC1_P + * MIC1_N + * MIC2_P + * MIC2_N + * MICBIAS1 + + Board connectors: + + * Headset Stereophone + * Int Spk + * Headset Mic + +- nvidia,i2s-controller : The phandle of the Tegra I2S controller +- nvidia,audio-codec : The phandle of the ALC5632 audio codec + +Example: + +sound { + compatible = "nvidia,tegra-audio-alc5632-paz00", + "nvidia,tegra-audio-alc5632"; + + nvidia,model = "Compal PAZ00"; + + nvidia,audio-routing = + "Int Spk", "SPK_OUTP", + "Int Spk", "SPK_OUTN", + "Headset Mic","MICBIAS1", + "MIC1_N", "Headset Mic", + "MIC1_P", "Headset Mic", + "Headset Stereophone", "HP_OUT_R", + "Headset Stereophone", "HP_OUT_L"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&alc5632>; +}; -- cgit v1.2.3 From b43ab901d671e3e3cad425ea5e9a3c74e266dcdd Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 27 Jun 2011 09:26:23 +0200 Subject: gpio: Add a driver for Sodaville GPIO controller Sodaville has GPIO controller behind the PCI bus. To my suprissed it is not the same as on PXA. The interrupt & gpio chip can be referenced from the device tree like from any other driver. Unfortunately the driver which uses the gpio interrupt has to use irq_of_parse_and_map() instead of platform_get_irq(). The problem is that the platform device (which is created from the device tree) is most likely created before the interrupt chip is registered and therefore irq_of_parse_and_map() fails. In theory the driver works as module. In reality most of the irq functions are not exported to modules and it is possible that _this_ module is unloaded while the provided irqs are still in use. Signed-off-by: Hans J. Koch [torbenh@linutronix.de: make it work after the irq namespace cleanup, add some device tree entries.] Signed-off-by: Torben Hohn [bigeasy@linutronix.de: convert to generic irq & gpio chip] Signed-off-by: Sebastian Andrzej Siewior [grant.likely@secretlab.ca: depend on x86 to avoid irq_domain breakage] Signed-off-by: Grant Likely --- .../devicetree/bindings/gpio/sodaville.txt | 48 ++++ arch/x86/platform/ce4100/falconfalls.dts | 7 +- drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-sodaville.c | 302 +++++++++++++++++++++ 5 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/sodaville.txt create mode 100644 drivers/gpio/gpio-sodaville.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/sodaville.txt b/Documentation/devicetree/bindings/gpio/sodaville.txt new file mode 100644 index 000000000000..563eff22b975 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/sodaville.txt @@ -0,0 +1,48 @@ +GPIO controller on CE4100 / Sodaville SoCs +========================================== + +The bindings for CE4100's GPIO controller match the generic description +which is covered by the gpio.txt file in this folder. + +The only additional property is the intel,muxctl property which holds the +value which is written into the MUXCNTL register. + +There is no compatible property for now because the driver is probed via +PCI id (vendor 0x8086 device 0x2e67). + +The interrupt specifier consists of two cells encoded as follows: + - <1st cell>: The interrupt-number that identifies the interrupt source. + - <2nd cell>: The level-sense information, encoded as follows: + 4 - active high level-sensitive + 8 - active low level-sensitive + +Example of the GPIO device and one user: + + pcigpio: gpio@b,1 { + /* two cells for GPIO and interrupt */ + #gpio-cells = <2>; + #interrupt-cells = <2>; + compatible = "pci8086,2e67.2", + "pci8086,2e67", + "pciclassff0000", + "pciclassff00"; + + reg = <0x15900 0x0 0x0 0x0 0x0>; + /* Interrupt line of the gpio device */ + interrupts = <15 1>; + /* It is an interrupt and GPIO controller itself */ + interrupt-controller; + gpio-controller; + intel,muxctl = <0>; + }; + + testuser@20 { + compatible = "example,testuser"; + /* User the 11th GPIO line as an active high triggered + * level interrupt + */ + interrupts = <11 8>; + interrupt-parent = <&pcigpio>; + /* Use this GPIO also with the gpio functions */ + gpios = <&pcigpio 11 0>; + }; diff --git a/arch/x86/platform/ce4100/falconfalls.dts b/arch/x86/platform/ce4100/falconfalls.dts index e70be38ce039..ce874f872cc6 100644 --- a/arch/x86/platform/ce4100/falconfalls.dts +++ b/arch/x86/platform/ce4100/falconfalls.dts @@ -208,16 +208,19 @@ interrupts = <14 1>; }; - gpio@b,1 { + pcigpio: gpio@b,1 { + #gpio-cells = <2>; + #interrupt-cells = <2>; compatible = "pci8086,2e67.2", "pci8086,2e67", "pciclassff0000", "pciclassff00"; - #gpio-cells = <2>; reg = <0x15900 0x0 0x0 0x0 0x0>; interrupts = <15 1>; + interrupt-controller; gpio-controller; + intel,muxctl = <0>; }; i2c-controller@b,2 { diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index eaa7d3828e70..dbb1909ca0a2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -417,6 +417,14 @@ config GPIO_ML_IOH Hub) which is for IVI(In-Vehicle Infotainment) use. This driver can access the IOH's GPIO device. +config GPIO_SODAVILLE + bool "Intel Sodaville GPIO support" + depends on X86 && PCI && OF + select GPIO_GENERIC + select GENERIC_IRQ_CHIP + help + Say Y here to support Intel Sodaville GPIO. + config GPIO_TIMBERDALE bool "Support for timberdale GPIO IP" depends on MFD_TIMBERDALE && HAS_IOMEM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8863a7f2dece..593bdcd1976e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o +obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c new file mode 100644 index 000000000000..9ba15d31d242 --- /dev/null +++ b/drivers/gpio/gpio-sodaville.c @@ -0,0 +1,302 @@ +/* + * GPIO interface for Intel Sodaville SoCs. + * + * Copyright (c) 2010, 2011 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "sdv_gpio" +#define SDV_NUM_PUB_GPIOS 12 +#define PCI_DEVICE_ID_SDV_GPIO 0x2e67 +#define GPIO_BAR 0 + +#define GPOUTR 0x00 +#define GPOER 0x04 +#define GPINR 0x08 + +#define GPSTR 0x0c +#define GPIT1R0 0x10 +#define GPIO_INT 0x14 +#define GPIT1R1 0x18 + +#define GPMUXCTL 0x1c + +struct sdv_gpio_chip_data { + int irq_base; + void __iomem *gpio_pub_base; + struct irq_domain id; + struct irq_chip_generic *gc; + struct bgpio_chip bgpio; +}; + +static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct sdv_gpio_chip_data *sd = gc->private; + void __iomem *type_reg; + u32 irq_offs = d->irq - sd->irq_base; + u32 reg; + + if (irq_offs < 8) + type_reg = sd->gpio_pub_base + GPIT1R0; + else + type_reg = sd->gpio_pub_base + GPIT1R1; + + reg = readl(type_reg); + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + reg &= ~BIT(4 * (irq_offs % 8)); + break; + + case IRQ_TYPE_LEVEL_LOW: + reg |= BIT(4 * (irq_offs % 8)); + break; + + default: + return -EINVAL; + } + + writel(reg, type_reg); + return 0; +} + +static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data) +{ + struct sdv_gpio_chip_data *sd = data; + u32 irq_stat = readl(sd->gpio_pub_base + GPSTR); + + irq_stat &= readl(sd->gpio_pub_base + GPIO_INT); + if (!irq_stat) + return IRQ_NONE; + + while (irq_stat) { + u32 irq_bit = __fls(irq_stat); + + irq_stat &= ~BIT(irq_bit); + generic_handle_irq(sd->irq_base + irq_bit); + } + + return IRQ_HANDLED; +} + +static int sdv_xlate(struct irq_domain *h, struct device_node *node, + const u32 *intspec, u32 intsize, irq_hw_number_t *out_hwirq, + u32 *out_type) +{ + u32 line, type; + + if (node != h->of_node) + return -EINVAL; + + if (intsize < 2) + return -EINVAL; + + line = *intspec; + *out_hwirq = line; + + intspec++; + type = *intspec; + + switch (type) { + case IRQ_TYPE_LEVEL_LOW: + case IRQ_TYPE_LEVEL_HIGH: + *out_type = type; + break; + default: + return -EINVAL; + } + return 0; +} + +static struct irq_domain_ops irq_domain_sdv_ops = { + .dt_translate = sdv_xlate, +}; + +static __devinit int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, + struct pci_dev *pdev) +{ + struct irq_chip_type *ct; + int ret; + + sd->irq_base = irq_alloc_descs(-1, 0, SDV_NUM_PUB_GPIOS, -1); + if (sd->irq_base < 0) + return sd->irq_base; + + /* mask + ACK all interrupt sources */ + writel(0, sd->gpio_pub_base + GPIO_INT); + writel((1 << 11) - 1, sd->gpio_pub_base + GPSTR); + + ret = request_irq(pdev->irq, sdv_gpio_pub_irq_handler, IRQF_SHARED, + "sdv_gpio", sd); + if (ret) + goto out_free_desc; + + sd->id.irq_base = sd->irq_base; + sd->id.of_node = of_node_get(pdev->dev.of_node); + sd->id.ops = &irq_domain_sdv_ops; + + /* + * This gpio irq controller latches level irqs. Testing shows that if + * we unmask & ACK the IRQ before the source of the interrupt is gone + * then the interrupt is active again. + */ + sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base, + sd->gpio_pub_base, handle_fasteoi_irq); + if (!sd->gc) { + ret = -ENOMEM; + goto out_free_irq; + } + + sd->gc->private = sd; + ct = sd->gc->chip_types; + ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW; + ct->regs.eoi = GPSTR; + ct->regs.mask = GPIO_INT; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->chip.irq_eoi = irq_gc_eoi; + ct->chip.irq_set_type = sdv_gpio_pub_set_type; + + irq_setup_generic_chip(sd->gc, IRQ_MSK(SDV_NUM_PUB_GPIOS), + IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, + IRQ_LEVEL | IRQ_NOPROBE); + + irq_domain_add(&sd->id); + return 0; +out_free_irq: + free_irq(pdev->irq, sd); +out_free_desc: + irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS); + return ret; +} + +static int __devinit sdv_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + struct sdv_gpio_chip_data *sd; + unsigned long addr; + const void *prop; + int len; + int ret; + u32 mux_val; + + sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL); + if (!sd) + return -ENOMEM; + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "can't enable device.\n"); + goto done; + } + + ret = pci_request_region(pdev, GPIO_BAR, DRV_NAME); + if (ret) { + dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR); + goto disable_pci; + } + + addr = pci_resource_start(pdev, GPIO_BAR); + if (!addr) + goto release_reg; + sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR)); + + prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len); + if (prop && len == 4) { + mux_val = of_read_number(prop, 1); + writel(mux_val, sd->gpio_pub_base + GPMUXCTL); + } + + ret = bgpio_init(&sd->bgpio, &pdev->dev, 4, + sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR, + NULL, sd->gpio_pub_base + GPOER, NULL, false); + if (ret) + goto unmap; + sd->bgpio.gc.ngpio = SDV_NUM_PUB_GPIOS; + + ret = gpiochip_add(&sd->bgpio.gc); + if (ret < 0) { + dev_err(&pdev->dev, "gpiochip_add() failed.\n"); + goto unmap; + } + + ret = sdv_register_irqsupport(sd, pdev); + if (ret) + goto unmap; + + pci_set_drvdata(pdev, sd); + dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n"); + return 0; + +unmap: + iounmap(sd->gpio_pub_base); +release_reg: + pci_release_region(pdev, GPIO_BAR); +disable_pci: + pci_disable_device(pdev); +done: + kfree(sd); + return ret; +} + +static void sdv_gpio_remove(struct pci_dev *pdev) +{ + struct sdv_gpio_chip_data *sd = pci_get_drvdata(pdev); + + irq_domain_del(&sd->id); + free_irq(pdev->irq, sd); + irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS); + + if (gpiochip_remove(&sd->bgpio.gc)) + dev_err(&pdev->dev, "gpiochip_remove() failed.\n"); + + pci_release_region(pdev, GPIO_BAR); + iounmap(sd->gpio_pub_base); + pci_disable_device(pdev); + kfree(sd); +} + +static struct pci_device_id sdv_gpio_pci_ids[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) }, + { 0, }, +}; + +static struct pci_driver sdv_gpio_driver = { + .name = DRV_NAME, + .id_table = sdv_gpio_pci_ids, + .probe = sdv_gpio_probe, + .remove = sdv_gpio_remove, +}; + +static int __init sdv_gpio_init(void) +{ + return pci_register_driver(&sdv_gpio_driver); +} +module_init(sdv_gpio_init); + +static void __exit sdv_gpio_exit(void) +{ + pci_unregister_driver(&sdv_gpio_driver); +} +module_exit(sdv_gpio_exit); + +MODULE_AUTHOR("Hans J. Koch "); +MODULE_DESCRIPTION("GPIO interface for Intel Sodaville SoCs"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 0c6700abfa3cc90e08d625a934ba0e06e147227e Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Thu, 13 Oct 2011 02:14:55 -0700 Subject: ARM: tegra: emc: device tree bindings Device tree bindings for the EMC tables on tegra. Signed-off-by: Olof Johansson Acked-by: Grant Likely --- .../devicetree/bindings/arm/tegra/emc.txt | 100 +++++++++++++++++++++ arch/arm/boot/dts/tegra20.dtsi | 7 ++ 2 files changed, 107 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/tegra/emc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/tegra/emc.txt b/Documentation/devicetree/bindings/arm/tegra/emc.txt new file mode 100644 index 000000000000..09335f8eee00 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/tegra/emc.txt @@ -0,0 +1,100 @@ +Embedded Memory Controller + +Properties: +- name : Should be emc +- #address-cells : Should be 1 +- #size-cells : Should be 0 +- compatible : Should contain "nvidia,tegra20-emc". +- reg : Offset and length of the register set for the device +- nvidia,use-ram-code : If present, the sub-nodes will be addressed + and chosen using the ramcode board selector. If omitted, only one + set of tables can be present and said tables will be used + irrespective of ram-code configuration. + +Child device nodes describe the memory settings for different configurations and clock rates. + +Example: + + emc@7000f400 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "nvidia,tegra20-emc"; + reg = <0x7000f4000 0x200>; + } + + +Embedded Memory Controller ram-code table + +If the emc node has the nvidia,use-ram-code property present, then the +next level of nodes below the emc table are used to specify which settings +apply for which ram-code settings. + +If the emc node lacks the nvidia,use-ram-code property, this level is omitted +and the tables are stored directly under the emc node (see below). + +Properties: + +- name : Should be emc-tables +- nvidia,ram-code : the binary representation of the ram-code board strappings + for which this node (and children) are valid. + + + +Embedded Memory Controller configuration table + +This is a table containing the EMC register settings for the various +operating speeds of the memory controller. They are always located as +subnodes of the emc controller node. + +There are two ways of specifying which tables to use: + +* The simplest is if there is just one set of tables in the device tree, + and they will always be used (based on which frequency is used). + This is the preferred method, especially when firmware can fill in + this information based on the specific system information and just + pass it on to the kernel. + +* The slightly more complex one is when more than one memory configuration + might exist on the system. The Tegra20 platform handles this during + early boot by selecting one out of possible 4 memory settings based + on a 2-pin "ram code" bootstrap setting on the board. The values of + these strappings can be read through a register in the SoC, and thus + used to select which tables to use. + +Properties: +- name : Should be emc-table +- compatible : Should contain "nvidia,tegra20-emc-table". +- reg : either an opaque enumerator to tell different tables apart, or + the valid frequency for which the table should be used (in kHz). +- clock-frequency : the clock frequency for the EMC at which this + table should be used (in kHz). +- nvidia,emc-registers : a 46 word array of EMC registers to be programmed + for operation at the 'clock-frequency' setting. + The order and contents of the registers are: + RC, RFC, RAS, RP, R2W, W2R, R2P, W2P, RD_RCD, WR_RCD, RRD, REXT, + WDV, QUSE, QRST, QSAFE, RDV, REFRESH, BURST_REFRESH_NUM, PDEX2WR, + PDEX2RD, PCHG2PDEN, ACT2PDEN, AR2PDEN, RW2PDEN, TXSR, TCKE, TFAW, + TRPAB, TCLKSTABLE, TCLKSTOP, TREFBW, QUSE_EXTRA, FBIO_CFG6, ODT_WRITE, + ODT_READ, FBIO_CFG5, CFG_DIG_DLL, DLL_XFORM_DQS, DLL_XFORM_QUSE, + ZCAL_REF_CNT, ZCAL_WAIT_CNT, AUTO_CAL_INTERVAL, CFG_CLKTRIM_0, + CFG_CLKTRIM_1, CFG_CLKTRIM_2 + + emc-table@166000 { + reg = <166000>; + compatible = "nvidia,tegra20-emc-table"; + clock-frequency = < 166000 >; + nvidia,emc-registers = < 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 >; + }; + + emc-table@333000 { + reg = <333000>; + compatible = "nvidia,tegra20-emc-table"; + clock-frequency = < 333000 >; + nvidia,emc-registers = < 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 >; + }; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 3da7afd45322..c1622413490a 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -120,6 +120,13 @@ interrupts = < 0 91 0x04 >; }; + emc@7000f400 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "nvidia,tegra20-emc"; + reg = <0x7000f400 0x200>; + }; + sdhci@c8000000 { compatible = "nvidia,tegra20-sdhci"; reg = <0xc8000000 0x200>; -- cgit v1.2.3 From a38b84fce9d1f3b1f054409b6363d98bf019e263 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Jan 2012 08:39:34 +0000 Subject: dt: tegra gpio: Flesh out binding documentation Document the required reg and interrupts properties. Add a complete example. Signed-off-by: Stephen Warren Acked-by: Grant Likely Signed-off-by: Olof Johansson --- Documentation/devicetree/bindings/gpio/gpio_nvidia.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt index eb4b530d64e1..50b363c5b884 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt @@ -2,7 +2,25 @@ NVIDIA Tegra 2 GPIO controller Required properties: - compatible : "nvidia,tegra20-gpio" +- reg : Physical base address and length of the controller's registers. +- interrupts : The interrupt outputs from the controller. - #gpio-cells : Should be two. The first cell is the pin number and the second cell is used to specify optional parameters: - bit 0 specifies polarity (0 for normal, 1 for inverted) - gpio-controller : Marks the device node as a GPIO controller. + +Example: + +gpio: gpio@6000d000 { + compatible = "nvidia,tegra20-gpio"; + reg = < 0x6000d000 0x1000 >; + interrupts = < 0 32 0x04 + 0 33 0x04 + 0 34 0x04 + 0 35 0x04 + 0 55 0x04 + 0 87 0x04 + 0 89 0x04 >; + #gpio-cells = <2>; + gpio-controller; +}; -- cgit v1.2.3 From 8051b75ab32f72ebd7bf232e554d631f56f1ee42 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 11 Jan 2012 16:09:54 -0700 Subject: ARM: dt: tegra: Add Tegra APB DMA device tree binding Document binding, and add the node to tegra*.dtsi. The driver isn't actually instantiated from this node yet, but the I2S binding will rely on being able to refer to the APB DMA node using a phandle. Signed-off-by: Stephen Warren Signed-off-by: Olof Johansson --- .../devicetree/bindings/dma/tegra20-apbdma.txt | 30 ++++++++++++++++++ arch/arm/boot/dts/tegra20.dtsi | 21 ++++++++++++ arch/arm/boot/dts/tegra30.dtsi | 37 ++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/tegra20-apbdma.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/dma/tegra20-apbdma.txt b/Documentation/devicetree/bindings/dma/tegra20-apbdma.txt new file mode 100644 index 000000000000..90fa7da525b8 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/tegra20-apbdma.txt @@ -0,0 +1,30 @@ +* NVIDIA Tegra APB DMA controller + +Required properties: +- compatible: Should be "nvidia,-apbdma" +- reg: Should contain DMA registers location and length. This shuld include + all of the per-channel registers. +- interrupts: Should contain all of the per-channel DMA interrupts. + +Examples: + +apbdma: dma@6000a000 { + compatible = "nvidia,tegra20-apbdma"; + reg = <0x6000a000 0x1200>; + interrupts = < 0 136 0x04 + 0 137 0x04 + 0 138 0x04 + 0 139 0x04 + 0 140 0x04 + 0 141 0x04 + 0 142 0x04 + 0 143 0x04 + 0 144 0x04 + 0 145 0x04 + 0 146 0x04 + 0 147 0x04 + 0 148 0x04 + 0 149 0x04 + 0 150 0x04 + 0 151 0x04 >; +}; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index c1622413490a..603dc2114ac6 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -12,6 +12,27 @@ < 0x50040100 0x0100 >; }; + apbdma: dma@6000a000 { + compatible = "nvidia,tegra20-apbdma"; + reg = <0x6000a000 0x1200>; + interrupts = < 0 104 0x04 + 0 105 0x04 + 0 106 0x04 + 0 107 0x04 + 0 108 0x04 + 0 109 0x04 + 0 110 0x04 + 0 111 0x04 + 0 112 0x04 + 0 113 0x04 + 0 114 0x04 + 0 115 0x04 + 0 116 0x04 + 0 117 0x04 + 0 118 0x04 + 0 119 0x04 >; + }; + i2c@7000c000 { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index 2b3f6cd3c798..8a7e230832d7 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -12,6 +12,43 @@ < 0x50040100 0x0100 >; }; + apbdma: dma@6000a000 { + compatible = "nvidia,tegra30-apbdma", "nvidia,tegra20-apbdma"; + reg = <0x6000a000 0x1400>; + interrupts = < 0 104 0x04 + 0 105 0x04 + 0 106 0x04 + 0 107 0x04 + 0 108 0x04 + 0 109 0x04 + 0 110 0x04 + 0 111 0x04 + 0 112 0x04 + 0 113 0x04 + 0 114 0x04 + 0 115 0x04 + 0 116 0x04 + 0 117 0x04 + 0 118 0x04 + 0 119 0x04 + 0 128 0x04 + 0 129 0x04 + 0 130 0x04 + 0 131 0x04 + 0 132 0x04 + 0 133 0x04 + 0 134 0x04 + 0 135 0x04 + 0 136 0x04 + 0 137 0x04 + 0 138 0x04 + 0 139 0x04 + 0 140 0x04 + 0 141 0x04 + 0 142 0x04 + 0 143 0x04 >; + }; + i2c@7000c000 { #address-cells = <1>; #size-cells = <0>; -- cgit v1.2.3 From d17adfdb17fb9f182d1fc5189d4772cd03f187c3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 25 Jan 2012 14:43:27 -0700 Subject: ARM: dt: Add binding for Tegra PMC The Tegra PMC (Power Management Controller) interfaces with an external PMU (Power Management Unit), and controls wake-up from sleep modes. This initial binding is the bare minimum required to control the PMC's inversion of the PMU's interrupt signal. Signed-off-by: Stephen Warren Signed-off-by: Olof Johansson --- .../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 19 +++++++++++++++++++ arch/arm/boot/dts/tegra-harmony.dts | 4 ++++ arch/arm/boot/dts/tegra20.dtsi | 5 +++++ arch/arm/boot/dts/tegra30.dtsi | 5 +++++ 4 files changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt new file mode 100644 index 000000000000..b5846e21cc2e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt @@ -0,0 +1,19 @@ +NVIDIA Tegra Power Management Controller (PMC) + +Properties: +- name : Should be pmc +- compatible : Should contain "nvidia,tegra-pmc". +- reg : Offset and length of the register set for the device +- nvidia,invert-interrupt : If present, inverts the PMU interrupt signal. + The PMU is an external Power Management Unit, whose interrupt output + signal is fed into the PMC. This signal is optionally inverted, and then + fed into the ARM GIC. The PMC is not involved in the detection or + handling of this interrupt signal, merely its inversion. + +Example: + +pmc@7000f400 { + compatible = "nvidia,tegra20-pmc"; + reg = <0x7000e400 0x400>; + nvidia,invert-interrupt; +}; diff --git a/arch/arm/boot/dts/tegra-harmony.dts b/arch/arm/boot/dts/tegra-harmony.dts index 9a52615c13b8..6e8447dc0202 100644 --- a/arch/arm/boot/dts/tegra-harmony.dts +++ b/arch/arm/boot/dts/tegra-harmony.dts @@ -10,6 +10,10 @@ reg = < 0x00000000 0x40000000 >; }; + pmc@7000f400 { + nvidia,invert-interrupt; + }; + i2c@7000c000 { clock-frequency = <400000>; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 8712449be9e1..3195ad5562d3 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -4,6 +4,11 @@ compatible = "nvidia,tegra20"; interrupt-parent = <&intc>; + pmc@7000f400 { + compatible = "nvidia,tegra20-pmc"; + reg = <0x7000e400 0x400>; + }; + intc: interrupt-controller@50041000 { compatible = "arm,cortex-a9-gic"; interrupt-controller; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index 8a7e230832d7..fd25e8e9ffd9 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -4,6 +4,11 @@ compatible = "nvidia,tegra30"; interrupt-parent = <&intc>; + pmc@7000f400 { + compatible = "nvidia,tegra20-pmc", "nvidia,tegra30-pmc"; + reg = <0x7000e400 0x400>; + }; + intc: interrupt-controller@50041000 { compatible = "arm,cortex-a9-gic"; interrupt-controller; -- cgit v1.2.3 From 6f74dc9bc8de41f3de474a7269a70921e773c40f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Jan 2012 08:39:37 +0000 Subject: gpio: tegra: Dynamically allocate IRQ base, and support DT Enhance the driver to dynamically allocate the base IRQ number, and create an IRQ domain for itself. The use of an IRQ domain ensures that any device tree node interrupts properties are correctly parsed. Describe interrupt-related properties in the device tree binding docs, and the contents of "child" node interrupts property. Update tegra*.dtsi to specify the required interrupt-related properties. Finally, remove the definition of TEGRA_GPIO_TO_IRQ; this macro no longer gives correct results since the IRQ numbers for GPIOs are dynamically allocated. Signed-off-by: Stephen Warren Acked-by: Grant Likely Signed-off-by: Olof Johansson --- .../devicetree/bindings/gpio/gpio_nvidia.txt | 12 +++++++++++ arch/arm/boot/dts/tegra20.dtsi | 2 ++ arch/arm/boot/dts/tegra30.dtsi | 2 ++ arch/arm/mach-tegra/include/mach/gpio-tegra.h | 2 -- drivers/gpio/gpio-tegra.c | 25 ++++++++++++++++------ 5 files changed, 34 insertions(+), 9 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt index 50b363c5b884..d114e1997d39 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt @@ -8,6 +8,16 @@ Required properties: second cell is used to specify optional parameters: - bit 0 specifies polarity (0 for normal, 1 for inverted) - gpio-controller : Marks the device node as a GPIO controller. +- #interrupt-cells : Should be 2. + The first cell is the GPIO number. + The second cell is used to specify flags: + bits[3:0] trigger type and level flags: + 1 = low-to-high edge triggered. + 2 = high-to-low edge triggered. + 4 = active high level-sensitive. + 8 = active low level-sensitive. + Valid combinations are 1, 2, 3, 4, 8. +- interrupt-controller : Marks the device node as an interrupt controller. Example: @@ -23,4 +33,6 @@ gpio: gpio@6000d000 { 0 89 0x04 >; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; }; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 3195ad5562d3..ec1f0101c79c 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -101,6 +101,8 @@ 0 89 0x04 >; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; }; pinmux: pinmux@70000000 { diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index fd25e8e9ffd9..ac4b75cb26c0 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -107,6 +107,8 @@ 0 125 0x04 >; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; }; serial@70006000 { diff --git a/arch/arm/mach-tegra/include/mach/gpio-tegra.h b/arch/arm/mach-tegra/include/mach/gpio-tegra.h index 87d37fdf5084..6140820555e1 100644 --- a/arch/arm/mach-tegra/include/mach/gpio-tegra.h +++ b/arch/arm/mach-tegra/include/mach/gpio-tegra.h @@ -25,8 +25,6 @@ #define TEGRA_NR_GPIOS INT_GPIO_NR -#define TEGRA_GPIO_TO_IRQ(gpio) (INT_GPIO_BASE + (gpio)) - struct tegra_gpio_table { int gpio; /* GPIO number */ bool enable; /* Enable for GPIO at init? */ diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index bdc293791590..bc923c7acce9 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -74,7 +75,7 @@ struct tegra_gpio_bank { #endif }; - +static struct irq_domain irq_domain; static void __iomem *regs; static struct tegra_gpio_bank tegra_gpio_banks[7]; @@ -139,7 +140,7 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - return TEGRA_GPIO_TO_IRQ(offset); + return irq_domain_to_irq(&irq_domain, offset); } static struct gpio_chip tegra_gpio_chip = { @@ -155,28 +156,28 @@ static struct gpio_chip tegra_gpio_chip = { static void tegra_gpio_irq_ack(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio)); } static void tegra_gpio_irq_mask(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0); } static void tegra_gpio_irq_unmask(struct irq_data *d) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1); } static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - int gpio = d->irq - INT_GPIO_BASE; + int gpio = d->hwirq; struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); int port = GPIO_PORT(gpio); int lvl_type; @@ -343,6 +344,16 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) int i; int j; + irq_domain.irq_base = irq_alloc_descs(-1, 0, TEGRA_NR_GPIOS, 0); + if (irq_domain.irq_base < 0) { + dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n"); + return -ENODEV; + } + irq_domain.nr_irq = TEGRA_NR_GPIOS; + irq_domain.ops = &irq_domain_simple_ops; + irq_domain.of_node = pdev->dev.of_node; + irq_domain_add(&irq_domain); + for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); if (!res) { @@ -381,7 +392,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) gpiochip_add(&tegra_gpio_chip); for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) { - int irq = TEGRA_GPIO_TO_IRQ(gpio); + int irq = irq_domain_to_irq(&irq_domain, gpio); /* No validity check; all Tegra GPIOs are valid IRQs */ bank = &tegra_gpio_banks[GPIO_BANK(gpio)]; -- cgit v1.2.3 From 3391811c4294da42e412ec5f83a251caf05869a4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 19 Jan 2012 08:16:35 +0000 Subject: gpio: tegra: Parameterize the number of banks Tegra20's GPIO controller has 7 banks, and Tegra30's controller has 8 banks. Allow the number of banks to be configured at run-time by the device tree. Signed-off-by: Stephen Warren Acked-by: Grant Likely Signed-off-by: Olof Johansson --- .../devicetree/bindings/gpio/gpio_nvidia.txt | 8 +++-- drivers/gpio/gpio-tegra.c | 42 +++++++++++++++++----- 2 files changed, 38 insertions(+), 12 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt index d114e1997d39..023c9526e5f8 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_nvidia.txt @@ -1,9 +1,11 @@ -NVIDIA Tegra 2 GPIO controller +NVIDIA Tegra GPIO controller Required properties: -- compatible : "nvidia,tegra20-gpio" +- compatible : "nvidia,tegra-gpio" - reg : Physical base address and length of the controller's registers. -- interrupts : The interrupt outputs from the controller. +- interrupts : The interrupt outputs from the controller. For Tegra20, + there should be 7 interrupts specified, and for Tegra30, there should + be 8 interrupts specified. - #gpio-cells : Should be two. The first cell is the pin number and the second cell is used to specify optional parameters: - bit 0 specifies polarity (0 for normal, 1 for inverted) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index bc923c7acce9..98f3980dab7c 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -77,7 +77,8 @@ struct tegra_gpio_bank { static struct irq_domain irq_domain; static void __iomem *regs; -static struct tegra_gpio_bank tegra_gpio_banks[7]; +static u32 tegra_gpio_bank_count; +static struct tegra_gpio_bank *tegra_gpio_banks; static inline void tegra_gpio_writel(u32 val, u32 reg) { @@ -274,7 +275,7 @@ void tegra_gpio_resume(void) local_irq_save(flags); - for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + for (b = 0; b < tegra_gpio_bank_count; b++) { struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { @@ -297,7 +298,7 @@ void tegra_gpio_suspend(void) int p; local_irq_save(flags); - for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + for (b = 0; b < tegra_gpio_bank_count; b++) { struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { @@ -338,23 +339,46 @@ static struct lock_class_key gpio_lock_class; static int __devinit tegra_gpio_probe(struct platform_device *pdev) { + int irq_base; struct resource *res; struct tegra_gpio_bank *bank; int gpio; int i; int j; - irq_domain.irq_base = irq_alloc_descs(-1, 0, TEGRA_NR_GPIOS, 0); - if (irq_domain.irq_base < 0) { + for (;;) { + res = platform_get_resource(pdev, IORESOURCE_IRQ, tegra_gpio_bank_count); + if (!res) + break; + tegra_gpio_bank_count++; + } + if (!tegra_gpio_bank_count) { + dev_err(&pdev->dev, "Missing IRQ resource\n"); + return -ENODEV; + } + + tegra_gpio_chip.ngpio = tegra_gpio_bank_count * 32; + + tegra_gpio_banks = devm_kzalloc(&pdev->dev, + tegra_gpio_bank_count * sizeof(*tegra_gpio_banks), + GFP_KERNEL); + if (!tegra_gpio_banks) { + dev_err(&pdev->dev, "Couldn't allocate bank structure\n"); + return -ENODEV; + } + + irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0); + if (irq_base < 0) { dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n"); return -ENODEV; } - irq_domain.nr_irq = TEGRA_NR_GPIOS; + irq_domain.irq_base = irq_base; + irq_domain.nr_irq = tegra_gpio_chip.ngpio; irq_domain.ops = &irq_domain_simple_ops; irq_domain.of_node = pdev->dev.of_node; irq_domain_add(&irq_domain); - for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { + for (i = 0; i < tegra_gpio_bank_count; i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); if (!res) { dev_err(&pdev->dev, "Missing IRQ resource\n"); @@ -391,7 +415,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) gpiochip_add(&tegra_gpio_chip); - for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) { + for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) { int irq = irq_domain_to_irq(&irq_domain, gpio); /* No validity check; all Tegra GPIOs are valid IRQs */ @@ -404,7 +428,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) set_irq_flags(irq, IRQF_VALID); } - for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) { + for (i = 0; i < tegra_gpio_bank_count; i++) { bank = &tegra_gpio_banks[i]; irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler); -- cgit v1.2.3 From 8deed1786a6479d0ccda51226582920ab1d6976c Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 23 Feb 2012 13:04:51 +0000 Subject: ARM: vexpress: Add Device Tree support This patch adds generic Versatile Express DT machine description, Device Tree description for the motherboard and documentation for the bindings. Signed-off-by: Pawel Moll --- Documentation/devicetree/bindings/arm/vexpress.txt | 146 +++++++++++++++ arch/arm/boot/dts/vexpress-v2m.dtsi | 200 +++++++++++++++++++++ arch/arm/mach-vexpress/Kconfig | 47 ++++- arch/arm/mach-vexpress/Makefile.boot | 2 + arch/arm/mach-vexpress/include/mach/motherboard.h | 6 + arch/arm/mach-vexpress/v2m.c | 153 +++++++++++++++- 6 files changed, 549 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/vexpress.txt create mode 100644 arch/arm/boot/dts/vexpress-v2m.dtsi (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/vexpress.txt b/Documentation/devicetree/bindings/arm/vexpress.txt new file mode 100644 index 000000000000..ec8b50cbb2e8 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/vexpress.txt @@ -0,0 +1,146 @@ +ARM Versatile Express boards family +----------------------------------- + +ARM's Versatile Express platform consists of a motherboard and one +or more daughterboards (tiles). The motherboard provides a set of +peripherals. Processor and RAM "live" on the tiles. + +The motherboard and each core tile should be described by a separate +Device Tree source file, with the tile's description including +the motherboard file using a /include/ directive. As the motherboard +can be initialized in one of two different configurations ("memory +maps"), care must be taken to include the correct one. + +Required properties in the root node: +- compatible value: + compatible = "arm,vexpress,", "arm,vexpress"; + where is the full tile model name (as used in the tile's + Technical Reference Manual), eg.: + - for Coretile Express A5x2 (V2P-CA5s): + compatible = "arm,vexpress,v2p-ca5s", "arm,vexpress"; + - for Coretile Express A9x4 (V2P-CA9): + compatible = "arm,vexpress,v2p-ca9", "arm,vexpress"; + If a tile comes in several variants or can be used in more then one + configuration, the compatible value should be: + compatible = "arm,vexpress,,", \ + "arm,vexpress,", "arm,vexpress"; + eg: + - Coretile Express A15x2 (V2P-CA15) with Tech Chip 1: + compatible = "arm,vexpress,v2p-ca15,tc1", \ + "arm,vexpress,v2p-ca15", "arm,vexpress"; + - LogicTile Express 13MG (V2F-2XV6) running Cortex-A7 (3 cores) SMM: + compatible = "arm,vexpress,v2f-2xv6,ca7x3", \ + "arm,vexpress,v2f-2xv6", "arm,vexpress"; + +Optional properties in the root node: +- tile model name (use name from the tile's Technical Reference + Manual, eg. "V2P-CA5s") + model = ""; +- tile's HBI number (unique ARM's board model ID, visible on the + PCB's silkscreen) in hexadecimal transcription: + arm,hbi = <0xhbi> + eg: + - for Coretile Express A5x2 (V2P-CA5s) HBI-0191: + arm,hbi = <0x191>; + - Coretile Express A9x4 (V2P-CA9) HBI-0225: + arm,hbi = <0x225>; + +Top-level standard "cpus" node is required. It must contain a node +with device_type = "cpu" property for every available core, eg.: + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a5"; + reg = <0>; + }; + }; + +The motherboard description file provides a single "motherboard" node +using 2 address cells corresponding to the Static Memory Bus used +between the motherboard and the tile. The first cell defines the Chip +Select (CS) line number, the second cell address offset within the CS. +All interrupt lines between the motherboard and the tile are active +high and are described using single cell. + +Optional properties of the "motherboard" node: +- motherboard's memory map variant: + arm,v2m-memory-map = ""; + where name is one of: + - "rs1" - for RS1 map (i.a. peripherals on CS3); this map is also + referred to as "ARM Cortex-A Series memory map": + arm,v2m-memory-map = "rs1"; + When this property is missing, the motherboard is using the original + memory map (also known as the "Legacy memory map", primarily used + with the original CoreTile Express A9x4) with peripherals on CS7. + +Motherboard .dtsi files provide a set of labelled peripherals that +can be used to obtain required phandle in the tile's "aliases" node: +- UARTs, note that the numbers correspond to the physical connectors + on the motherboard's back panel: + v2m_serial0, v2m_serial1, v2m_serial2 and v2m_serial3 +- I2C controllers: + v2m_i2c_dvi and v2m_i2c_pcie +- SP804 timers: + v2m_timer01 and v2m_timer23 + +Current Linux implementation requires a "arm,v2m_timer" alias +pointing at one of the motherboard's SP804 timers, if it is to be +used as the system timer. This alias should be defined in the +motherboard files. + +The tile description must define "ranges", "interrupt-map-mask" and +"interrupt-map" properties to translate the motherboard's address +and interrupt space into one used by the tile's processor. + +Abbreviated example: + +/dts-v1/; + +/ { + model = "V2P-CA5s"; + arm,hbi = <0x225>; + compatible = "arm,vexpress-v2p-ca5s", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a5"; + reg = <0>; + }; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x2c001000 0x1000>, + <0x2c000100 0x100>; + }; + + motherboard { + /* CS0 is visible at 0x08000000 */ + ranges = <0 0 0x08000000 0x04000000>; + interrupt-map-mask = <0 0 63>; + /* Active high IRQ 0 is connected to GIC's SPI0 */ + interrupt-map = <0 0 0 &gic 0 0 4>; + }; +}; + +/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi new file mode 100644 index 000000000000..a6c9c7c82d53 --- /dev/null +++ b/arch/arm/boot/dts/vexpress-v2m.dtsi @@ -0,0 +1,200 @@ +/* + * ARM Ltd. Versatile Express + * + * Motherboard Express uATX + * V2M-P1 + * + * HBI-0190D + * + * Original memory map ("Legacy memory map" in the board's + * Technical Reference Manual) + * + * WARNING! The hardware described in this file is independent from the + * RS1 variant (vexpress-v2m-rs1.dtsi), but there is a strong + * correspondence between the two configurations. + * + * TAKE CARE WHEN MAINTAINING THIS FILE TO PROPAGATE ANY RELEVANT + * CHANGES TO vexpress-v2m-rs1.dtsi! + */ + +/ { + aliases { + arm,v2m_timer = &v2m_timer01; + }; + + motherboard { + compatible = "simple-bus"; + #address-cells = <2>; /* SMB chipselect number and offset */ + #size-cells = <1>; + #interrupt-cells = <1>; + + flash@0,00000000 { + compatible = "arm,vexpress-flash", "cfi-flash"; + reg = <0 0x00000000 0x04000000>, + <1 0x00000000 0x04000000>; + bank-width = <4>; + }; + + psram@2,00000000 { + compatible = "arm,vexpress-psram", "mtd-ram"; + reg = <2 0x00000000 0x02000000>; + bank-width = <4>; + }; + + vram@3,00000000 { + compatible = "arm,vexpress-vram"; + reg = <3 0x00000000 0x00800000>; + }; + + ethernet@3,02000000 { + compatible = "smsc,lan9118", "smsc,lan9115"; + reg = <3 0x02000000 0x10000>; + interrupts = <15>; + phy-mode = "mii"; + reg-io-width = <4>; + smsc,irq-active-high; + smsc,irq-push-pull; + }; + + usb@3,03000000 { + compatible = "nxp,usb-isp1761"; + reg = <3 0x03000000 0x20000>; + interrupts = <16>; + port1-otg; + }; + + iofpga@7,00000000 { + compatible = "arm,amba-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 7 0 0x20000>; + + sysreg@00000 { + compatible = "arm,vexpress-sysreg"; + reg = <0x00000 0x1000>; + }; + + sysctl@01000 { + compatible = "arm,sp810", "arm,primecell"; + reg = <0x01000 0x1000>; + }; + + /* PCI-E I2C bus */ + v2m_i2c_pcie: i2c@02000 { + compatible = "arm,versatile-i2c"; + reg = <0x02000 0x1000>; + + #address-cells = <1>; + #size-cells = <0>; + + pcie-switch@60 { + compatible = "idt,89hpes32h8"; + reg = <0x60>; + }; + }; + + aaci@04000 { + compatible = "arm,pl041", "arm,primecell"; + reg = <0x04000 0x1000>; + interrupts = <11>; + }; + + mmci@05000 { + compatible = "arm,pl180", "arm,primecell"; + reg = <0x05000 0x1000>; + interrupts = <9 10>; + }; + + kmi@06000 { + compatible = "arm,pl050", "arm,primecell"; + reg = <0x06000 0x1000>; + interrupts = <12>; + }; + + kmi@07000 { + compatible = "arm,pl050", "arm,primecell"; + reg = <0x07000 0x1000>; + interrupts = <13>; + }; + + v2m_serial0: uart@09000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x09000 0x1000>; + interrupts = <5>; + }; + + v2m_serial1: uart@0a000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0a000 0x1000>; + interrupts = <6>; + }; + + v2m_serial2: uart@0b000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0b000 0x1000>; + interrupts = <7>; + }; + + v2m_serial3: uart@0c000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0c000 0x1000>; + interrupts = <8>; + }; + + wdt@0f000 { + compatible = "arm,sp805", "arm,primecell"; + reg = <0x0f000 0x1000>; + interrupts = <0>; + }; + + v2m_timer01: timer@11000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0x11000 0x1000>; + interrupts = <2>; + }; + + v2m_timer23: timer@12000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0x12000 0x1000>; + }; + + /* DVI I2C bus */ + v2m_i2c_dvi: i2c@16000 { + compatible = "arm,versatile-i2c"; + reg = <0x16000 0x1000>; + + #address-cells = <1>; + #size-cells = <0>; + + dvi-transmitter@39 { + compatible = "sil,sii9022-tpi", "sil,sii9022"; + reg = <0x39>; + }; + + dvi-transmitter@60 { + compatible = "sil,sii9022-cpi", "sil,sii9022"; + reg = <0x60>; + }; + }; + + rtc@17000 { + compatible = "arm,pl031", "arm,primecell"; + reg = <0x17000 0x1000>; + interrupts = <4>; + }; + + compact-flash@1a000 { + compatible = "arm,vexpress-cf", "ata-generic"; + reg = <0x1a000 0x100 + 0x1a100 0xf00>; + reg-shift = <2>; + }; + + clcd@1f000 { + compatible = "arm,pl111", "arm,primecell"; + reg = <0x1f000 0x1000>; + interrupts = <14>; + }; + }; + }; +}; diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 9b3d0fbaee72..cf8730d35e70 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -1,14 +1,55 @@ menu "Versatile Express platform type" depends on ARCH_VEXPRESS +config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA + bool + select ARM_ERRATA_720789 + select ARM_ERRATA_751472 + select PL310_ERRATA_753970 if CACHE_PL310 + help + Provides common dependencies for Versatile Express platforms + based on Cortex-A5 and Cortex-A9 processors. In order to + build a working kernel, you must also enable relevant core + tile support or Flattened Device Tree based support options. + config ARCH_VEXPRESS_CA9X4 bool "Versatile Express Cortex-A9x4 tile" + select ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA + select ARM_GIC select CPU_V7 + select HAVE_SMP + select MIGHT_HAVE_CACHE_L2X0 + +config ARCH_VEXPRESS_DT + bool "Device Tree support for Versatile Express platforms" + select ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA select ARM_GIC - select ARM_ERRATA_720789 - select ARM_ERRATA_751472 - select ARM_ERRATA_753970 + select ARM_PATCH_PHYS_VIRT + select AUTO_ZRELADDR + select CPU_V7 select HAVE_SMP select MIGHT_HAVE_CACHE_L2X0 + select USE_OF + help + New Versatile Express platforms require Flattened Device Tree to + be passed to the kernel. + + This option enables support for systems using Cortex processor based + ARM core and logic (FPGA) tiles on the Versatile Express motherboard, + for example: + + - CoreTile Express A5x2 (V2P-CA5s) + - CoreTile Express A9x4 (V2P-CA9) + - CoreTile Express A15x2 (V2P-CA15) + - LogicTile Express 13MG (V2F-2XV6) with A5, A7, A9 or A15 SMMs + (Soft Macrocell Models) + - Versatile Express RTSMs (Models) + + You must boot using a Flattened Device Tree in order to use these + platforms. The traditional (ATAGs) boot method is not usable on + these boards with this option. + + If your bootloader supports Flattened Device Tree based booting, + say Y here. endmenu diff --git a/arch/arm/mach-vexpress/Makefile.boot b/arch/arm/mach-vexpress/Makefile.boot index 8630b3d10a4d..c6dd8918b9ed 100644 --- a/arch/arm/mach-vexpress/Makefile.boot +++ b/arch/arm/mach-vexpress/Makefile.boot @@ -1,3 +1,5 @@ +# Those numbers are used only by the non-DT V2P-CA9 platform +# The DT-enabled ones require CONFIG_AUTO_ZRELADDR=y zreladdr-y += 0x60008000 params_phys-y := 0x60000100 initrd_phys-y := 0x60800000 diff --git a/arch/arm/mach-vexpress/include/mach/motherboard.h b/arch/arm/mach-vexpress/include/mach/motherboard.h index b4c498c1dbee..31a92890893d 100644 --- a/arch/arm/mach-vexpress/include/mach/motherboard.h +++ b/arch/arm/mach-vexpress/include/mach/motherboard.h @@ -116,6 +116,12 @@ int v2m_cfg_write(u32 devfn, u32 data); int v2m_cfg_read(u32 devfn, u32 *data); void v2m_flags_set(u32 data); +/* + * Miscellaneous + */ +#define SYS_MISC_MASTERSITE (1 << 14) +#define SYS_PROCIDx_HBI_MASK 0xfff + /* * Core tile IDs */ diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index c76f9144898a..dbf055103000 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -21,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -430,8 +436,9 @@ static void __init v2m_populate_ct_desc(void) ct_desc = ct_descs[i]; if (!ct_desc) - panic("vexpress: failed to populate core tile description " - "for tile ID 0x%8x\n", current_tile_id); + panic("vexpress: this kernel does not support core tile ID 0x%08x when booting via ATAGs.\n" + "You may need a device tree blob or a different kernel to boot on this board.\n", + current_tile_id); } static void __init v2m_map_io(void) @@ -476,3 +483,145 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express") .init_machine = v2m_init, .restart = v2m_restart, MACHINE_END + +#if defined(CONFIG_ARCH_VEXPRESS_DT) + +void __init v2m_dt_map_io(void) +{ + iotable_init(v2m_io_desc, ARRAY_SIZE(v2m_io_desc)); + +#if defined(CONFIG_SMP) + vexpress_dt_smp_map_io(); +#endif +} + +static struct clk_lookup v2m_dt_lookups[] = { + { /* AMBA bus clock */ + .con_id = "apb_pclk", + .clk = &dummy_apb_pclk, + }, { /* SP804 timers */ + .dev_id = "sp804", + .con_id = "v2m-timer0", + .clk = &v2m_sp804_clk, + }, { /* SP804 timers */ + .dev_id = "sp804", + .con_id = "v2m-timer1", + .clk = &v2m_sp804_clk, + }, { /* PL180 MMCI */ + .dev_id = "mb:mmci", /* 10005000.mmci */ + .clk = &osc2_clk, + }, { /* PL050 KMI0 */ + .dev_id = "10006000.kmi", + .clk = &osc2_clk, + }, { /* PL050 KMI1 */ + .dev_id = "10007000.kmi", + .clk = &osc2_clk, + }, { /* PL011 UART0 */ + .dev_id = "10009000.uart", + .clk = &osc2_clk, + }, { /* PL011 UART1 */ + .dev_id = "1000a000.uart", + .clk = &osc2_clk, + }, { /* PL011 UART2 */ + .dev_id = "1000b000.uart", + .clk = &osc2_clk, + }, { /* PL011 UART3 */ + .dev_id = "1000c000.uart", + .clk = &osc2_clk, + }, { /* SP805 WDT */ + .dev_id = "1000f000.wdt", + .clk = &v2m_ref_clk, + }, { /* PL111 CLCD */ + .dev_id = "1001f000.clcd", + .clk = &osc1_clk, + }, +}; + +void __init v2m_dt_init_early(void) +{ + struct device_node *node; + u32 dt_hbi; + + node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); + v2m_sysreg_base = of_iomap(node, 0); + if (WARN_ON(!v2m_sysreg_base)) + return; + + /* Confirm board type against DT property, if available */ + if (of_property_read_u32(allnodes, "arm,hbi", &dt_hbi) == 0) { + u32 misc = readl(v2m_sysreg_base + V2M_SYS_MISC); + u32 id = readl(v2m_sysreg_base + (misc & SYS_MISC_MASTERSITE ? + V2M_SYS_PROCID1 : V2M_SYS_PROCID0)); + u32 hbi = id & SYS_PROCIDx_HBI_MASK; + + if (WARN_ON(dt_hbi != hbi)) + pr_warning("vexpress: DT HBI (%x) is not matching " + "hardware (%x)!\n", dt_hbi, hbi); + } + + clkdev_add_table(v2m_dt_lookups, ARRAY_SIZE(v2m_dt_lookups)); + versatile_sched_clock_init(v2m_sysreg_base + V2M_SYS_24MHZ, 24000000); +} + +static struct of_device_id vexpress_irq_match[] __initdata = { + { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, + {} +}; + +static void __init v2m_dt_init_irq(void) +{ + of_irq_init(vexpress_irq_match); +} + +static void __init v2m_dt_timer_init(void) +{ + struct device_node *node; + const char *path; + int err; + + node = of_find_compatible_node(NULL, NULL, "arm,sp810"); + v2m_sysctl_init(of_iomap(node, 0)); + + err = of_property_read_string(of_aliases, "arm,v2m_timer", &path); + if (WARN_ON(err)) + return; + node = of_find_node_by_path(path); + v2m_sp804_init(of_iomap(node, 0), irq_of_parse_and_map(node, 0)); +} + +static struct sys_timer v2m_dt_timer = { + .init = v2m_dt_timer_init, +}; + +static struct of_dev_auxdata v2m_dt_auxdata_lookup[] __initdata = { + OF_DEV_AUXDATA("arm,vexpress-flash", V2M_NOR0, "physmap-flash", + &v2m_flash_data), + OF_DEV_AUXDATA("arm,primecell", V2M_MMCI, "mb:mmci", &v2m_mmci_data), + {} +}; + +static void __init v2m_dt_init(void) +{ + l2x0_of_init(0x00400000, 0xfe0fffff); + of_platform_populate(NULL, of_default_bus_match_table, + v2m_dt_auxdata_lookup, NULL); + pm_power_off = v2m_power_off; +} + +const static char *v2m_dt_match[] __initconst = { + "arm,vexpress", + NULL, +}; + +DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express") + .dt_compat = v2m_dt_match, + .map_io = v2m_dt_map_io, + .init_early = v2m_dt_init_early, + .init_irq = v2m_dt_init_irq, + .timer = &v2m_dt_timer, + .init_machine = v2m_dt_init, + .handle_irq = gic_handle_irq, + .restart = v2m_restart, +MACHINE_END + +#endif -- cgit v1.2.3 From 52fa212088b141dd05957dc4b7f06a893c8166ad Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Wed, 30 Nov 2011 19:21:07 +0100 Subject: ARM: OMAP2/3: intc: Add DT support for TI interrupt controller Add a function to initialize the OMAP2/3 interrupt controller (INTC) using a device tree node. This version take advantage of the new irq_domain_add_legacy API. Replace some printk() with the proper pr_ macro. Signed-off-by: Benoit Cousson Cc: Tony Lindgren Acked-by: Rob Herring Acked-by: Grant Likely --- .../devicetree/bindings/arm/omap/intc.txt | 27 ++++++++++ arch/arm/mach-omap2/common.h | 12 +++++ arch/arm/mach-omap2/irq.c | 60 ++++++++++++++++++---- 3 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/omap/intc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/omap/intc.txt b/Documentation/devicetree/bindings/arm/omap/intc.txt new file mode 100644 index 000000000000..f2583e6ec060 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/omap/intc.txt @@ -0,0 +1,27 @@ +* OMAP Interrupt Controller + +OMAP2/3 are using a TI interrupt controller that can support several +configurable number of interrupts. + +Main node required properties: + +- compatible : should be: + "ti,omap2-intc" +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The type shall be a and the value shall be 1. + + The cell contains the interrupt number in the range [0-128]. +- ti,intc-size: Number of interrupts handled by the interrupt controller. +- reg: physical base address and size of the intc registers map. + +Example: + + intc: interrupt-controller@1 { + compatible = "ti,omap2-intc"; + interrupt-controller; + #interrupt-cells = <1>; + ti,intc-size = <96>; + reg = <0x48200000 0x1000>; + }; + diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index febffde2ff10..dd8c9906b1ab 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -174,6 +174,18 @@ void omap3_intc_handle_irq(struct pt_regs *regs); extern void __iomem *omap4_get_l2cache_base(void); #endif +struct device_node; +#ifdef CONFIG_OF +int __init omap_intc_of_init(struct device_node *node, + struct device_node *parent); +#else +int __init omap_intc_of_init(struct device_node *node, + struct device_node *parent) +{ + return 0; +} +#endif + #ifdef CONFIG_SMP extern void __iomem *omap4_get_scu_base(void); #else diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 1fef061f7927..26eaf37ce4d9 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -11,12 +11,16 @@ * for more details. */ #include +#include #include #include #include #include #include #include +#include +#include +#include /* selected INTC register offsets */ @@ -57,6 +61,8 @@ static struct omap_irq_bank { }, }; +static struct irq_domain *domain; + /* Structure to save interrupt controller context */ struct omap3_intc_regs { u32 sysconfig; @@ -147,17 +153,27 @@ omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) IRQ_NOREQUEST | IRQ_NOPROBE, 0); } -static void __init omap_init_irq(u32 base, int nr_irqs) +static void __init omap_init_irq(u32 base, int nr_irqs, + struct device_node *node) { void __iomem *omap_irq_base; unsigned long nr_of_irqs = 0; unsigned int nr_banks = 0; - int i, j; + int i, j, irq_base; omap_irq_base = ioremap(base, SZ_4K); if (WARN_ON(!omap_irq_base)) return; + irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + if (irq_base < 0) { + pr_warn("Couldn't allocate IRQ numbers\n"); + irq_base = 0; + } + + domain = irq_domain_add_legacy(node, nr_irqs, irq_base, 0, + &irq_domain_simple_ops, NULL); + for (i = 0; i < ARRAY_SIZE(irq_banks); i++) { struct omap_irq_bank *bank = irq_banks + i; @@ -166,36 +182,36 @@ static void __init omap_init_irq(u32 base, int nr_irqs) /* Static mapping, never released */ bank->base_reg = ioremap(base, SZ_4K); if (!bank->base_reg) { - printk(KERN_ERR "Could not ioremap irq bank%i\n", i); + pr_err("Could not ioremap irq bank%i\n", i); continue; } omap_irq_bank_init_one(bank); for (j = 0; j < bank->nr_irqs; j += 32) - omap_alloc_gc(bank->base_reg + j, j, 32); + omap_alloc_gc(bank->base_reg + j, j + irq_base, 32); nr_of_irqs += bank->nr_irqs; nr_banks++; } - printk(KERN_INFO "Total of %ld interrupts on %d active controller%s\n", - nr_of_irqs, nr_banks, nr_banks > 1 ? "s" : ""); + pr_info("Total of %ld interrupts on %d active controller%s\n", + nr_of_irqs, nr_banks, nr_banks > 1 ? "s" : ""); } void __init omap2_init_irq(void) { - omap_init_irq(OMAP24XX_IC_BASE, 96); + omap_init_irq(OMAP24XX_IC_BASE, 96, NULL); } void __init omap3_init_irq(void) { - omap_init_irq(OMAP34XX_IC_BASE, 96); + omap_init_irq(OMAP34XX_IC_BASE, 96, NULL); } void __init ti81xx_init_irq(void) { - omap_init_irq(OMAP34XX_IC_BASE, 128); + omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); } static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs *regs) @@ -225,8 +241,10 @@ out: irqnr = readl_relaxed(base_addr + INTCPS_SIR_IRQ_OFFSET); irqnr &= ACTIVEIRQ_MASK; - if (irqnr) + if (irqnr) { + irqnr = irq_find_mapping(domain, irqnr); handle_IRQ(irqnr, regs); + } } while (irqnr); } @@ -236,6 +254,28 @@ asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs omap_intc_handle_irq(base_addr, regs); } +int __init omap_intc_of_init(struct device_node *node, + struct device_node *parent) +{ + struct resource res; + u32 nr_irqs = 96; + + if (WARN_ON(!node)) + return -ENODEV; + + if (of_address_to_resource(node, 0, &res)) { + WARN(1, "unable to get intc registers\n"); + return -EINVAL; + } + + if (of_property_read_u32(node, "ti,intc-size", &nr_irqs)) + pr_warn("unable to get intc-size, default to %d\n", nr_irqs); + + omap_init_irq(res.start, nr_irqs, of_node_get(node)); + + return 0; +} + #ifdef CONFIG_ARCH_OMAP3 static struct omap3_intc_regs intc_context[ARRAY_SIZE(irq_banks)]; -- cgit v1.2.3 From 6334018f70ca61bcf86194bf5c04d1ac6bd6ec1b Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Mon, 27 Feb 2012 21:18:19 +0200 Subject: ASoC: DT: Update digital microphone binding documentation to PAZ00 board. This patch updates device tree binding documentation to add digital microphone to PAZ00 board. Signed-off-by: Leon Romanovsky Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt index 65b001e16517..b77a97c9101e 100644 --- a/Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt +++ b/Documentation/devicetree/bindings/sound/tegra-audio-alc5632.txt @@ -25,12 +25,14 @@ Required properties: * MIC2_P * MIC2_N * MICBIAS1 + * DMICDAT Board connectors: * Headset Stereophone * Int Spk * Headset Mic + * Digital Mic - nvidia,i2s-controller : The phandle of the Tegra I2S controller - nvidia,audio-codec : The phandle of the ALC5632 audio codec -- cgit v1.2.3 From 6ad109fa3f46713a5cd28f41d872c9a4fc2bcedc Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 29 Feb 2012 09:14:03 +0100 Subject: devicetree-bindings: Add documentation for i.MX generic boards Signed-off-by: Sascha Hauer --- Documentation/devicetree/bindings/arm/fsl.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt index 54bdddadf1cf..bfbc771a65f8 100644 --- a/Documentation/devicetree/bindings/arm/fsl.txt +++ b/Documentation/devicetree/bindings/arm/fsl.txt @@ -28,3 +28,25 @@ Required root node properties: i.MX6 Quad SABRE Lite Board Required root node properties: - compatible = "fsl,imx6q-sabrelite", "fsl,imx6q"; + +Generic i.MX boards +------------------- + +No iomux setup is done for these boards, so this must have been configured +by the bootloader for boards to work with the generic bindings. + +i.MX27 generic board +Required root node properties: + - compatible = "fsl,imx27"; + +i.MX51 generic board +Required root node properties: + - compatible = "fsl,imx51"; + +i.MX53 generic board +Required root node properties: + - compatible = "fsl,imx53"; + +i.MX6q generic board +Required root node properties: + - compatible = "fsl,imx6q"; -- cgit v1.2.3 From e261501d05bd2df244d31e0866b1e81776766ecf Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 22 Nov 2011 22:26:09 +0100 Subject: ARM: at91/aic: add irq domain and device tree support Add an irqdomain for the AIC interrupt controller. The device tree support is mapping the registers and is using the irq_domain_add_legacy() to manage hwirq translation. The documentation is describing the meaning of the two cells required for using this "interrupt-controller" in a device tree node. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD --- .../devicetree/bindings/arm/atmel-aic.txt | 38 +++++++++++ arch/arm/Kconfig | 1 + arch/arm/boot/dts/at91sam9g20.dtsi | 18 +++--- arch/arm/boot/dts/at91sam9g45.dtsi | 16 ++--- arch/arm/mach-at91/irq.c | 74 ++++++++++++++++++---- 5 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/atmel-aic.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-aic.txt b/Documentation/devicetree/bindings/arm/atmel-aic.txt new file mode 100644 index 000000000000..aabca4f83402 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/atmel-aic.txt @@ -0,0 +1,38 @@ +* Advanced Interrupt Controller (AIC) + +Required properties: +- compatible: Should be "atmel,-aic" +- interrupt-controller: Identifies the node as an interrupt controller. +- interrupt-parent: For single AIC system, it is an empty property. +- #interrupt-cells: The number of cells to define the interrupts. It sould be 2. + The first cell is the IRQ number (aka "Peripheral IDentifier" on datasheet). + The second cell is used to specify flags: + bits[3:0] trigger type and level flags: + 1 = low-to-high edge triggered. + 2 = high-to-low edge triggered. + 4 = active high level-sensitive. + 8 = active low level-sensitive. + Valid combinations are 1, 2, 3, 4, 8. + Default flag for internal sources should be set to 4 (active high). +- reg: Should contain AIC registers location and length + +Examples: + /* + * AIC + */ + aic: interrupt-controller@fffff000 { + compatible = "atmel,at91rm9200-aic"; + interrupt-controller; + interrupt-parent; + #interrupt-cells = <2>; + reg = <0xfffff000 0x200>; + }; + + /* + * An interrupt generating device that is wired to an AIC. + */ + dma: dma-controller@ffffec00 { + compatible = "atmel,at91sam9g45-dma"; + reg = <0xffffec00 0x200>; + interrupts = <21 4>; + }; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 92c9c79c140c..cabd8f556a1f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -322,6 +322,7 @@ config ARCH_AT91 select ARCH_REQUIRE_GPIOLIB select HAVE_CLK select CLKDEV_LOOKUP + select IRQ_DOMAIN help This enables support for systems based on the Atmel AT91RM9200, AT91SAM9 processors. diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index 07603b8c9503..9a0aee791a40 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -47,7 +47,7 @@ ranges; aic: interrupt-controller@fffff000 { - #interrupt-cells = <1>; + #interrupt-cells = <2>; compatible = "atmel,at91rm9200-aic"; interrupt-controller; interrupt-parent; @@ -57,14 +57,14 @@ dbgu: serial@fffff200 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffff200 0x200>; - interrupts = <1>; + interrupts = <1 4>; status = "disabled"; }; usart0: serial@fffb0000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffb0000 0x200>; - interrupts = <6>; + interrupts = <6 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -73,7 +73,7 @@ usart1: serial@fffb4000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffb4000 0x200>; - interrupts = <7>; + interrupts = <7 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -82,7 +82,7 @@ usart2: serial@fffb8000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffb8000 0x200>; - interrupts = <8>; + interrupts = <8 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -91,7 +91,7 @@ usart3: serial@fffd0000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffd0000 0x200>; - interrupts = <23>; + interrupts = <23 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -100,7 +100,7 @@ usart4: serial@fffd4000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffd4000 0x200>; - interrupts = <24>; + interrupts = <24 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -109,7 +109,7 @@ usart5: serial@fffd8000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffd8000 0x200>; - interrupts = <25>; + interrupts = <25 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -118,7 +118,7 @@ macb0: ethernet@fffc4000 { compatible = "cdns,at32ap7000-macb", "cdns,macb"; reg = <0xfffc4000 0x100>; - interrupts = <21>; + interrupts = <21 4>; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index fffa005300a4..67f94d3698a2 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -46,7 +46,7 @@ ranges; aic: interrupt-controller@fffff000 { - #interrupt-cells = <1>; + #interrupt-cells = <2>; compatible = "atmel,at91rm9200-aic"; interrupt-controller; interrupt-parent; @@ -56,20 +56,20 @@ dma: dma-controller@ffffec00 { compatible = "atmel,at91sam9g45-dma"; reg = <0xffffec00 0x200>; - interrupts = <21>; + interrupts = <21 4>; }; dbgu: serial@ffffee00 { compatible = "atmel,at91sam9260-usart"; reg = <0xffffee00 0x200>; - interrupts = <1>; + interrupts = <1 4>; status = "disabled"; }; usart0: serial@fff8c000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfff8c000 0x200>; - interrupts = <7>; + interrupts = <7 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -78,7 +78,7 @@ usart1: serial@fff90000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfff90000 0x200>; - interrupts = <8>; + interrupts = <8 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -87,7 +87,7 @@ usart2: serial@fff94000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfff94000 0x200>; - interrupts = <9>; + interrupts = <9 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -96,7 +96,7 @@ usart3: serial@fff98000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfff98000 0x200>; - interrupts = <10>; + interrupts = <10 4>; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; @@ -105,7 +105,7 @@ macb0: ethernet@fffbc000 { compatible = "cdns,at32ap7000-macb", "cdns,macb"; reg = <0xfffbc000 0x100>; - interrupts = <25>; + interrupts = <25 4>; status = "disabled"; }; }; diff --git a/arch/arm/mach-at91/irq.c b/arch/arm/mach-at91/irq.c index be6b639ecd7b..46682fafa96f 100644 --- a/arch/arm/mach-at91/irq.c +++ b/arch/arm/mach-at91/irq.c @@ -24,6 +24,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -34,22 +40,24 @@ #include void __iomem *at91_aic_base; +static struct irq_domain *at91_aic_domain; +static struct device_node *at91_aic_np; static void at91_aic_mask_irq(struct irq_data *d) { /* Disable interrupt on AIC */ - at91_aic_write(AT91_AIC_IDCR, 1 << d->irq); + at91_aic_write(AT91_AIC_IDCR, 1 << d->hwirq); } static void at91_aic_unmask_irq(struct irq_data *d) { /* Enable interrupt on AIC */ - at91_aic_write(AT91_AIC_IECR, 1 << d->irq); + at91_aic_write(AT91_AIC_IECR, 1 << d->hwirq); } unsigned int at91_extern_irq; -#define is_extern_irq(irq) ((1 << (irq)) & at91_extern_irq) +#define is_extern_irq(hwirq) ((1 << (hwirq)) & at91_extern_irq) static int at91_aic_set_type(struct irq_data *d, unsigned type) { @@ -63,13 +71,13 @@ static int at91_aic_set_type(struct irq_data *d, unsigned type) srctype = AT91_AIC_SRCTYPE_RISING; break; case IRQ_TYPE_LEVEL_LOW: - if ((d->irq == AT91_ID_FIQ) || is_extern_irq(d->irq)) /* only supported on external interrupts */ + if ((d->hwirq == AT91_ID_FIQ) || is_extern_irq(d->hwirq)) /* only supported on external interrupts */ srctype = AT91_AIC_SRCTYPE_LOW; else return -EINVAL; break; case IRQ_TYPE_EDGE_FALLING: - if ((d->irq == AT91_ID_FIQ) || is_extern_irq(d->irq)) /* only supported on external interrupts */ + if ((d->hwirq == AT91_ID_FIQ) || is_extern_irq(d->hwirq)) /* only supported on external interrupts */ srctype = AT91_AIC_SRCTYPE_FALLING; else return -EINVAL; @@ -78,8 +86,8 @@ static int at91_aic_set_type(struct irq_data *d, unsigned type) return -EINVAL; } - smr = at91_aic_read(AT91_AIC_SMR(d->irq)) & ~AT91_AIC_SRCTYPE; - at91_aic_write(AT91_AIC_SMR(d->irq), smr | srctype); + smr = at91_aic_read(AT91_AIC_SMR(d->hwirq)) & ~AT91_AIC_SRCTYPE; + at91_aic_write(AT91_AIC_SMR(d->hwirq), smr | srctype); return 0; } @@ -90,13 +98,13 @@ static u32 backups; static int at91_aic_set_wake(struct irq_data *d, unsigned value) { - if (unlikely(d->irq >= 32)) + if (unlikely(d->hwirq >= NR_AIC_IRQS)) return -EINVAL; if (value) - wakeups |= (1 << d->irq); + wakeups |= (1 << d->hwirq); else - wakeups &= ~(1 << d->irq); + wakeups &= ~(1 << d->hwirq); return 0; } @@ -127,24 +135,64 @@ static struct irq_chip at91_aic_chip = { .irq_set_wake = at91_aic_set_wake, }; +#if defined(CONFIG_OF) +static int __init __at91_aic_of_init(struct device_node *node, + struct device_node *parent) +{ + at91_aic_base = of_iomap(node, 0); + at91_aic_np = node; + + return 0; +} + +static const struct of_device_id aic_ids[] __initconst = { + { .compatible = "atmel,at91rm9200-aic", .data = __at91_aic_of_init }, + { /*sentinel*/ } +}; + +static void __init at91_aic_of_init(void) +{ + of_irq_init(aic_ids); +} +#else +static void __init at91_aic_of_init(void) {} +#endif + /* * Initialize the AIC interrupt controller. */ void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS]) { unsigned int i; + int irq_base; - at91_aic_base = ioremap(AT91_AIC, 512); + if (of_have_populated_dt()) + at91_aic_of_init(); + else + at91_aic_base = ioremap(AT91_AIC, 512); if (!at91_aic_base) - panic("Impossible to ioremap AT91_AIC\n"); + panic("Unable to ioremap AIC registers\n"); + + /* Add irq domain for AIC */ + irq_base = irq_alloc_descs(-1, 0, NR_AIC_IRQS, 0); + if (irq_base < 0) { + WARN(1, "Cannot allocate irq_descs, assuming pre-allocated\n"); + irq_base = 0; + } + at91_aic_domain = irq_domain_add_legacy(at91_aic_np, NR_AIC_IRQS, + irq_base, 0, + &irq_domain_simple_ops, NULL); + + if (!at91_aic_domain) + panic("Unable to add AIC irq domain\n"); /* * The IVR is used by macro get_irqnr_and_base to read and verify. * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred. */ for (i = 0; i < NR_AIC_IRQS; i++) { - /* Put irq number in Source Vector Register: */ + /* Put hardware irq number in Source Vector Register: */ at91_aic_write(AT91_AIC_SVR(i), i); /* Active Low interrupt, with the specified priority */ at91_aic_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]); -- cgit v1.2.3 From 21f81872788b8089ec4214afad8fc6a0a23f70c8 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Sat, 11 Feb 2012 15:41:40 +0100 Subject: ARM: at91/gpio: add irqdomain and DT support Add "legacy" type of irqdomain to preserve old-style numbering and allow smooth transition for both DT and non-DT cases. Original idea and code by Jean-Christophe Plagniol-Villard. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD --- .../devicetree/bindings/gpio/gpio_atmel.txt | 20 ++ arch/arm/boot/dts/at91sam9g20.dtsi | 30 +++ arch/arm/boot/dts/at91sam9g45.dtsi | 50 +++++ arch/arm/boot/dts/at91sam9x5.dtsi | 4 + arch/arm/mach-at91/gpio.c | 233 +++++++++++++++++---- 5 files changed, 291 insertions(+), 46 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio_atmel.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio_atmel.txt b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt new file mode 100644 index 000000000000..a7bcaec913bf --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt @@ -0,0 +1,20 @@ +* Atmel GPIO controller (PIO) + +Required properties: +- compatible: "atmel,at91rm9200-gpio" +- reg: Should contain GPIO controller registers location and length +- interrupts: Should be the port interrupt shared by all the pins. +- #gpio-cells: Should be two. The first cell is the pin number and + the second cell is used to specify optional parameters (currently + unused). +- gpio-controller: Marks the device node as a GPIO controller. + +Example: + pioA: gpio@fffff200 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff200 0x100>; + interrupts = <2 4>; + #gpio-cells = <2>; + gpio-controller; + }; + diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index 9a0aee791a40..325989a27a7a 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -23,6 +23,9 @@ serial4 = &usart3; serial5 = &usart4; serial6 = &usart5; + gpio0 = &pioA; + gpio1 = &pioB; + gpio2 = &pioC; }; cpus { cpu@0 { @@ -54,6 +57,33 @@ reg = <0xfffff000 0x200>; }; + pioA: gpio@fffff400 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff400 0x100>; + interrupts = <2 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioB: gpio@fffff600 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff600 0x100>; + interrupts = <3 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioC: gpio@fffff800 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff800 0x100>; + interrupts = <4 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + dbgu: serial@fffff200 { compatible = "atmel,at91sam9260-usart"; reg = <0xfffff200 0x200>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 67f94d3698a2..a9dbbb5b86f5 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -22,6 +22,11 @@ serial2 = &usart1; serial3 = &usart2; serial4 = &usart3; + gpio0 = &pioA; + gpio1 = &pioB; + gpio2 = &pioC; + gpio3 = &pioD; + gpio4 = &pioE; }; cpus { cpu@0 { @@ -59,6 +64,51 @@ interrupts = <21 4>; }; + pioA: gpio@fffff200 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff200 0x100>; + interrupts = <2 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioB: gpio@fffff400 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff400 0x100>; + interrupts = <3 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioC: gpio@fffff600 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff600 0x100>; + interrupts = <4 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioD: gpio@fffff800 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffff800 0x100>; + interrupts = <5 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + + pioE: gpio@fffffa00 { + compatible = "atmel,at91rm9200-gpio"; + reg = <0xfffffa00 0x100>; + interrupts = <5 4>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + }; + dbgu: serial@ffffee00 { compatible = "atmel,at91sam9260-usart"; reg = <0xffffee00 0x200>; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index e91391f50730..bb0c676b3393 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -94,6 +94,7 @@ interrupts = <2 4>; #gpio-cells = <2>; gpio-controller; + interrupt-controller; }; pioB: gpio@fffff600 { @@ -102,6 +103,7 @@ interrupts = <2 4>; #gpio-cells = <2>; gpio-controller; + interrupt-controller; }; pioC: gpio@fffff800 { @@ -110,6 +112,7 @@ interrupts = <3 4>; #gpio-cells = <2>; gpio-controller; + interrupt-controller; }; pioD: gpio@fffffa00 { @@ -118,6 +121,7 @@ interrupts = <3 4>; #gpio-cells = <2>; gpio-controller; + interrupt-controller; }; dbgu: serial@fffff200 { diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c index b762afc4ec17..89e683aaae6c 100644 --- a/arch/arm/mach-at91/gpio.c +++ b/arch/arm/mach-at91/gpio.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include #include @@ -30,8 +33,10 @@ struct at91_gpio_chip { struct gpio_chip chip; struct at91_gpio_chip *next; /* Bank sharing same clock */ int pioc_hwirq; /* PIO bank interrupt identifier on AIC */ + int pioc_idx; /* PIO bank index */ void __iomem *regbase; /* PIO bank virtual address */ struct clk *clock; /* associated clock */ + struct irq_domain *domain; /* associated irq domain */ }; #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip) @@ -273,9 +278,9 @@ static u32 backups[MAX_GPIO_BANKS]; static int gpio_irq_set_wake(struct irq_data *d, unsigned state) { - unsigned pin = irq_to_gpio(d->irq); - unsigned mask = pin_to_mask(pin); - unsigned bank = pin / 32; + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + unsigned mask = 1 << d->hwirq; + unsigned bank = at91_gpio->pioc_idx; if (unlikely(bank >= MAX_GPIO_BANKS)) return -EINVAL; @@ -301,9 +306,10 @@ void at91_gpio_suspend(void) __raw_writel(backups[i], pio + PIO_IDR); __raw_writel(wakeups[i], pio + PIO_IER); - if (!wakeups[i]) + if (!wakeups[i]) { + clk_unprepare(gpio_chip[i].clock); clk_disable(gpio_chip[i].clock); - else { + } else { #ifdef CONFIG_PM_DEBUG printk(KERN_DEBUG "GPIO-%c may wake for %08x\n", 'A'+i, wakeups[i]); #endif @@ -318,8 +324,10 @@ void at91_gpio_resume(void) for (i = 0; i < gpio_banks; i++) { void __iomem *pio = gpio_chip[i].regbase; - if (!wakeups[i]) - clk_enable(gpio_chip[i].clock); + if (!wakeups[i]) { + if (clk_prepare(gpio_chip[i].clock) == 0) + clk_enable(gpio_chip[i].clock); + } __raw_writel(wakeups[i], pio + PIO_IDR); __raw_writel(backups[i], pio + PIO_IER); @@ -344,9 +352,9 @@ void at91_gpio_resume(void) static void gpio_irq_mask(struct irq_data *d) { - unsigned pin = irq_to_gpio(d->irq); - void __iomem *pio = pin_to_controller(pin); - unsigned mask = pin_to_mask(pin); + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; if (pio) __raw_writel(mask, pio + PIO_IDR); @@ -354,9 +362,9 @@ static void gpio_irq_mask(struct irq_data *d) static void gpio_irq_unmask(struct irq_data *d) { - unsigned pin = irq_to_gpio(d->irq); - void __iomem *pio = pin_to_controller(pin); - unsigned mask = pin_to_mask(pin); + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; if (pio) __raw_writel(mask, pio + PIO_IER); @@ -384,7 +392,7 @@ static struct irq_chip gpio_irqchip = { static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) { - unsigned irq_pin; + unsigned virq; struct irq_data *idata = irq_desc_get_irq_data(desc); struct irq_chip *chip = irq_data_get_irq_chip(idata); struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(idata); @@ -407,12 +415,12 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) continue; } - irq_pin = gpio_to_irq(at91_gpio->chip.base); + virq = gpio_to_irq(at91_gpio->chip.base); while (isr) { if (isr & 1) - generic_handle_irq(irq_pin); - irq_pin++; + generic_handle_irq(virq); + virq++; isr >>= 1; } } @@ -482,6 +490,26 @@ postcore_initcall(at91_gpio_debugfs_init); /*--------------------------------------------------------------------------*/ +/* + * irqdomain initialization: pile up irqdomains on top of AIC range + */ +static void __init at91_gpio_irqdomain(struct at91_gpio_chip *at91_gpio) +{ + int irq_base; + + irq_base = irq_alloc_descs(-1, 0, at91_gpio->chip.ngpio, 0); + if (irq_base < 0) + panic("at91_gpio.%d: error %d: couldn't allocate IRQ numbers.\n", + at91_gpio->pioc_idx, irq_base); + at91_gpio->domain = irq_domain_add_legacy(at91_gpio->chip.of_node, + at91_gpio->chip.ngpio, + irq_base, 0, + &irq_domain_simple_ops, NULL); + if (!at91_gpio->domain) + panic("at91_gpio.%d: couldn't allocate irq domain.\n", + at91_gpio->pioc_idx); +} + /* * This lock class tells lockdep that GPIO irqs are in a different * category than their parents, so it won't report false recursion. @@ -493,28 +521,35 @@ static struct lock_class_key gpio_lock_class; */ void __init at91_gpio_irq_setup(void) { - unsigned pioc, irq = gpio_to_irq(0); + unsigned pioc; + int gpio_irqnbr = 0; struct at91_gpio_chip *this, *prev; for (pioc = 0, this = gpio_chip, prev = NULL; pioc++ < gpio_banks; prev = this, this++) { unsigned pioc_hwirq = this->pioc_hwirq; - unsigned i; + int offset; __raw_writel(~0, this->regbase + PIO_IDR); - for (i = 0, irq = gpio_to_irq(this->chip.base); i < 32; - i++, irq++) { - irq_set_lockdep_class(irq, &gpio_lock_class); + /* setup irq domain for this GPIO controller */ + at91_gpio_irqdomain(this); + + for (offset = 0; offset < this->chip.ngpio; offset++) { + unsigned int virq = irq_find_mapping(this->domain, offset); + irq_set_lockdep_class(virq, &gpio_lock_class); /* * Can use the "simple" and not "edge" handler since it's * shorter, and the AIC handles interrupts sanely. */ - irq_set_chip_and_handler(irq, &gpio_irqchip, + irq_set_chip_and_handler(virq, &gpio_irqchip, handle_simple_irq); - set_irq_flags(irq, IRQF_VALID); + set_irq_flags(virq, IRQF_VALID); + irq_set_chip_data(virq, this); + + gpio_irqnbr++; } /* The toplevel handler handles one bank of GPIOs, except @@ -527,7 +562,7 @@ void __init at91_gpio_irq_setup(void) irq_set_chip_data(pioc_hwirq, this); irq_set_chained_handler(pioc_hwirq, gpio_irq_handler); } - pr_info("AT91: %d gpio irqs in %d banks\n", irq - gpio_to_irq(0), gpio_banks); + pr_info("AT91: %d gpio irqs in %d banks\n", gpio_irqnbr, gpio_banks); } /* gpiolib support */ @@ -600,39 +635,145 @@ static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) } } +static int __init at91_gpio_setup_clk(int idx) +{ + struct at91_gpio_chip *at91_gpio = &gpio_chip[idx]; + + /* retreive PIO controller's clock */ + at91_gpio->clock = clk_get_sys(NULL, at91_gpio->chip.label); + if (IS_ERR(at91_gpio->clock)) { + pr_err("at91_gpio.%d, failed to get clock, ignoring.\n", idx); + goto err; + } + + if (clk_prepare(at91_gpio->clock)) + goto clk_prep_err; + + /* enable PIO controller's clock */ + if (clk_enable(at91_gpio->clock)) { + pr_err("at91_gpio.%d, failed to enable clock, ignoring.\n", idx); + goto clk_err; + } + + return 0; + +clk_err: + clk_unprepare(at91_gpio->clock); +clk_prep_err: + clk_put(at91_gpio->clock); +err: + return -EINVAL; +} + +#ifdef CONFIG_OF_GPIO +static void __init of_at91_gpio_init_one(struct device_node *np) +{ + int alias_idx; + struct at91_gpio_chip *at91_gpio; + + if (!np) + return; + + alias_idx = of_alias_get_id(np, "gpio"); + if (alias_idx >= MAX_GPIO_BANKS) { + pr_err("at91_gpio, failed alias idx(%d) > MAX_GPIO_BANKS(%d), ignoring.\n", + alias_idx, MAX_GPIO_BANKS); + return; + } + + at91_gpio = &gpio_chip[alias_idx]; + at91_gpio->chip.base = alias_idx * at91_gpio->chip.ngpio; + + at91_gpio->regbase = of_iomap(np, 0); + if (!at91_gpio->regbase) { + pr_err("at91_gpio.%d, failed to map registers, ignoring.\n", + alias_idx); + return; + } + + /* Get the interrupts property */ + if (of_property_read_u32(np, "interrupts", &at91_gpio->pioc_hwirq)) { + pr_err("at91_gpio.%d, failed to get interrupts property, ignoring.\n", + alias_idx); + goto ioremap_err; + } + + /* Setup clock */ + if (at91_gpio_setup_clk(alias_idx)) + goto ioremap_err; + + at91_gpio->chip.of_node = np; + gpio_banks = max(gpio_banks, alias_idx + 1); + at91_gpio->pioc_idx = alias_idx; + return; + +ioremap_err: + iounmap(at91_gpio->regbase); +} + +static int __init of_at91_gpio_init(void) +{ + struct device_node *np = NULL; + + /* + * This isn't ideal, but it gets things hooked up until this + * driver is converted into a platform_device + */ + for_each_compatible_node(np, NULL, "atmel,at91rm9200-gpio") + of_at91_gpio_init_one(np); + + return gpio_banks > 0 ? 0 : -EINVAL; +} +#else +static int __init of_at91_gpio_init(void) +{ + return -EINVAL; +} +#endif + +static void __init at91_gpio_init_one(int idx, u32 regbase, int pioc_hwirq) +{ + struct at91_gpio_chip *at91_gpio = &gpio_chip[idx]; + + at91_gpio->chip.base = idx * at91_gpio->chip.ngpio; + at91_gpio->pioc_hwirq = pioc_hwirq; + at91_gpio->pioc_idx = idx; + + at91_gpio->regbase = ioremap(regbase, 512); + if (!at91_gpio->regbase) { + pr_err("at91_gpio.%d, failed to map registers, ignoring.\n", idx); + return; + } + + if (at91_gpio_setup_clk(idx)) + goto ioremap_err; + + gpio_banks = max(gpio_banks, idx + 1); + return; + +ioremap_err: + iounmap(at91_gpio->regbase); +} + /* * Called from the processor-specific init to enable GPIO pin support. */ void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks) { - unsigned i; + unsigned i; struct at91_gpio_chip *at91_gpio, *last = NULL; BUG_ON(nr_banks > MAX_GPIO_BANKS); - gpio_banks = nr_banks; + if (of_at91_gpio_init() < 0) { + /* No GPIO controller found in device tree */ + for (i = 0; i < nr_banks; i++) + at91_gpio_init_one(i, data[i].regbase, data[i].id); + } - for (i = 0; i < nr_banks; i++) { + for (i = 0; i < gpio_banks; i++) { at91_gpio = &gpio_chip[i]; - at91_gpio->pioc_hwirq = data[i].pioc_hwirq; - at91_gpio->chip.base = i * 32; - - at91_gpio->regbase = ioremap(data[i].regbase, 512); - if (!at91_gpio->regbase) { - pr_err("at91_gpio.%d, failed to map registers, ignoring.\n", i); - continue; - } - - at91_gpio->clock = clk_get_sys(NULL, at91_gpio->chip.label); - if (!at91_gpio->clock) { - pr_err("at91_gpio.%d, failed to get clock, ignoring.\n", i); - continue; - } - - /* enable PIO controller's clock */ - clk_enable(at91_gpio->clock); - /* * GPIO controller are grouped on some SoC: * PIOC, PIOD and PIOE can share the same IRQ line -- cgit v1.2.3 From 23fa648fd32658ca295de3ef2b7c883c7b8a6120 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Mon, 27 Feb 2012 11:19:34 +0100 Subject: ARM: at91: pit add DT support Retreive registers address and IRQ from device tree entry. Called from at91_dt_init_irq() so that timers are up-n-running when timers initialization will occur. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD [nicolas.ferre@atmel.com: change error path and interrupts property handling] Signed-off-by: Nicolas Ferre --- .../devicetree/bindings/arm/atmel-at91.txt | 8 +++ arch/arm/boot/dts/at91sam9g20.dtsi | 6 +++ arch/arm/boot/dts/at91sam9g45.dtsi | 6 +++ arch/arm/mach-at91/at91sam926x_time.c | 63 +++++++++++++++++++++- arch/arm/mach-at91/at91sam9x5.c | 2 - 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/atmel-at91.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt new file mode 100644 index 000000000000..380f711a2021 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -0,0 +1,8 @@ +Atmel AT91 device tree bindings. +================================ + +PIT Timer required properties: +- compatible: Should be "atmel,at91sam9260-pit" +- reg: Should contain registers location and length +- interrupts: Should contain interrupt for the PIT which is the IRQ line + shared across all System Controller members. diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index 325989a27a7a..04c56c41001f 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -57,6 +57,12 @@ reg = <0xfffff000 0x200>; }; + pit: timer@fffffd30 { + compatible = "atmel,at91sam9260-pit"; + reg = <0xfffffd30 0xf>; + interrupts = <1 4>; + }; + pioA: gpio@fffff400 { compatible = "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x100>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index a9dbbb5b86f5..3881cab965fa 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -58,6 +58,12 @@ reg = <0xfffff000 0x200>; }; + pit: timer@fffffd30 { + compatible = "atmel,at91sam9260-pit"; + reg = <0xfffffd30 0xf>; + interrupts = <1 4>; + }; + dma: dma-controller@ffffec00 { compatible = "atmel,at91sam9g45-dma"; reg = <0xffffec00 0x200>; diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index d89ead740a99..5d71476a2832 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include @@ -133,7 +136,8 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) static struct irqaction at91sam926x_pit_irq = { .name = "at91_tick", .flags = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = at91sam926x_pit_interrupt + .handler = at91sam926x_pit_interrupt, + .irq = AT91_ID_SYS, }; static void at91sam926x_pit_reset(void) @@ -149,6 +153,49 @@ static void at91sam926x_pit_reset(void) pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN); } +#ifdef CONFIG_OF +static struct of_device_id pit_timer_ids[] = { + { .compatible = "atmel,at91sam9260-pit" }, + { /* sentinel */ } +}; + +static int __init of_at91sam926x_pit_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_matching_node(NULL, pit_timer_ids); + if (!np) + goto err; + + pit_base_addr = of_iomap(np, 0); + if (!pit_base_addr) + goto node_err; + + /* Get the interrupts property */ + ret = irq_of_parse_and_map(np, 0); + if (!ret) + goto ioremap_err; + at91sam926x_pit_irq.irq = ret; + + of_node_put(np); + + return 0; + +ioremap_err: + iounmap(pit_base_addr); +node_err: + of_node_put(np); +err: + return -EINVAL; +} +#else +static int __init of_at91sam926x_pit_init(void) +{ + return -EINVAL; +} +#endif + /* * Set up both clocksource and clockevent support. */ @@ -157,6 +204,9 @@ static void __init at91sam926x_pit_init(void) unsigned long pit_rate; unsigned bits; + /* For device tree enabled device: initialize here */ + of_at91sam926x_pit_init(); + /* * Use our actual MCK to figure out how many MCK/16 ticks per * 1/HZ period (instead of a compile-time constant LATCH). @@ -177,7 +227,7 @@ static void __init at91sam926x_pit_init(void) clocksource_register_hz(&pit_clk, pit_rate); /* Set up irq handler */ - setup_irq(AT91_ID_SYS, &at91sam926x_pit_irq); + setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); /* Set up and register clockevents */ pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift); @@ -193,6 +243,15 @@ static void at91sam926x_pit_suspend(void) void __init at91sam926x_ioremap_pit(u32 addr) { +#if defined(CONFIG_OF) + struct device_node *np = + of_find_matching_node(NULL, pit_timer_ids); + + if (np) { + of_node_put(np); + return; + } +#endif pit_base_addr = ioremap(addr, 16); if (!pit_base_addr) diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index d17d4262665b..a34d96afa746 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -301,8 +301,6 @@ static void __init at91sam9x5_map_io(void) static void __init at91sam9x5_ioremap_registers(void) { - if (of_at91sam926x_pit_init() < 0) - panic("Impossible to find PIT\n"); at91_ioremap_ramc(0, AT91SAM9X5_BASE_DDRSDRC0, 512); } -- cgit v1.2.3 From 3a61a5dae49bf3d1afb7f75c8acb3607f26565af Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 19 Jan 2012 10:13:40 +0100 Subject: ARM: at91/tc: add device tree support to atmel_tclib Device tree support added to atmel_tclib: the generic Timer Counter library. This is used by the clocksource/clockevent driver tcb_clksrc. The current DT enabled platforms are also modified to use it: - .dtsi files are modified to add Timer Counter Block entries - alias are created to allow identification of each block - clkdev lookup tables are added for clocks identification. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Grant Likely --- .../devicetree/bindings/arm/atmel-at91.txt | 24 +++++++++++++++++++ arch/arm/boot/dts/at91sam9g20.dtsi | 14 +++++++++++ arch/arm/boot/dts/at91sam9g45.dtsi | 15 ++++++++++++ arch/arm/mach-at91/at91sam9260.c | 7 ++++++ arch/arm/mach-at91/at91sam9260_devices.c | 17 ++++++++++++++ arch/arm/mach-at91/at91sam9g45.c | 3 +++ arch/arm/mach-at91/at91sam9g45_devices.c | 17 ++++++++++++++ drivers/misc/atmel_tclib.c | 27 ++++++++++++++++++++-- 8 files changed, 122 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 380f711a2021..1aeaf6f2a1ba 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -6,3 +6,27 @@ PIT Timer required properties: - reg: Should contain registers location and length - interrupts: Should contain interrupt for the PIT which is the IRQ line shared across all System Controller members. + +TC/TCLIB Timer required properties: +- compatible: Should be "atmel,-pit". + can be "at91rm9200" or "at91sam9x5" +- reg: Should contain registers location and length +- interrupts: Should contain all interrupts for the TC block + Note that you can specify several interrupt cells if the TC + block has one interrupt per channel. + +Examples: + +One interrupt per TC block: + tcb0: timer@fff7c000 { + compatible = "atmel,at91rm9200-tcb"; + reg = <0xfff7c000 0x100>; + interrupts = <18 4>; + }; + +One interrupt per TC channel in a TC block: + tcb1: timer@fffdc000 { + compatible = "atmel,at91rm9200-tcb"; + reg = <0xfffdc000 0x100>; + interrupts = <26 4 27 4 28 4>; + }; diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index 04c56c41001f..a100db03ec90 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -26,6 +26,8 @@ gpio0 = &pioA; gpio1 = &pioB; gpio2 = &pioC; + tcb0 = &tcb0; + tcb1 = &tcb1; }; cpus { cpu@0 { @@ -63,6 +65,18 @@ interrupts = <1 4>; }; + tcb0: timer@fffa0000 { + compatible = "atmel,at91rm9200-tcb"; + reg = <0xfffa0000 0x100>; + interrupts = <17 4 18 4 19 4>; + }; + + tcb1: timer@fffdc000 { + compatible = "atmel,at91rm9200-tcb"; + reg = <0xfffdc000 0x100>; + interrupts = <26 4 27 4 28 4>; + }; + pioA: gpio@fffff400 { compatible = "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x100>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 3881cab965fa..f779667159b1 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -27,6 +27,8 @@ gpio2 = &pioC; gpio3 = &pioD; gpio4 = &pioE; + tcb0 = &tcb0; + tcb1 = &tcb1; }; cpus { cpu@0 { @@ -64,6 +66,19 @@ interrupts = <1 4>; }; + + tcb0: timer@fff7c000 { + compatible = "atmel,at91rm9200-tcb"; + reg = <0xfff7c000 0x100>; + interrupts = <18 4>; + }; + + tcb1: timer@fffd4000 { + compatible = "atmel,at91rm9200-tcb"; + reg = <0xfffd4000 0x100>; + interrupts = <18 4>; + }; + dma: dma-controller@ffffec00 { compatible = "atmel,at91sam9g45-dma"; reg = <0xffffec00 0x200>; diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index 4ade265be805..14b5a9c9a514 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -209,6 +209,13 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("usart", "fffd0000.serial", &usart3_clk), CLKDEV_CON_DEV_ID("usart", "fffd4000.serial", &usart4_clk), CLKDEV_CON_DEV_ID("usart", "fffd8000.serial", &usart5_clk), + /* more tc lookup table for DT entries */ + CLKDEV_CON_DEV_ID("t0_clk", "fffa0000.timer", &tc0_clk), + CLKDEV_CON_DEV_ID("t1_clk", "fffa0000.timer", &tc1_clk), + CLKDEV_CON_DEV_ID("t2_clk", "fffa0000.timer", &tc2_clk), + CLKDEV_CON_DEV_ID("t0_clk", "fffdc000.timer", &tc3_clk), + CLKDEV_CON_DEV_ID("t1_clk", "fffdc000.timer", &tc4_clk), + CLKDEV_CON_DEV_ID("t2_clk", "fffdc000.timer", &tc5_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_ID("pioA", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index c450cb3970a0..e82a5ae6ea1e 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -699,8 +699,25 @@ static struct platform_device at91sam9260_tcb1_device = { .num_resources = ARRAY_SIZE(tcb1_resources), }; +#if defined(CONFIG_OF) +static struct of_device_id tcb_ids[] = { + { .compatible = "atmel,at91rm9200-tcb" }, + { /*sentinel*/ } +}; +#endif + static void __init at91_add_device_tc(void) { +#if defined(CONFIG_OF) + struct device_node *np; + + np = of_find_matching_node(NULL, tcb_ids); + if (np) { + of_node_put(np); + return; + } +#endif + platform_device_register(&at91sam9260_tcb0_device); platform_device_register(&at91sam9260_tcb1_device); } diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index a41622ea61b8..0014573dfe17 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -229,6 +229,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("usart", "fff90000.serial", &usart1_clk), CLKDEV_CON_DEV_ID("usart", "fff94000.serial", &usart2_clk), CLKDEV_CON_DEV_ID("usart", "fff98000.serial", &usart3_clk), + /* more tc lookup table for DT entries */ + CLKDEV_CON_DEV_ID("t0_clk", "fff7c000.timer", &tcb0_clk), + CLKDEV_CON_DEV_ID("t0_clk", "fffd4000.timer", &tcb0_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &uhphs_clk), CLKDEV_CON_ID("pioA", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index aee595013d33..4320b2096789 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -1090,8 +1090,25 @@ static struct platform_device at91sam9g45_tcb1_device = { .num_resources = ARRAY_SIZE(tcb1_resources), }; +#if defined(CONFIG_OF) +static struct of_device_id tcb_ids[] = { + { .compatible = "atmel,at91rm9200-tcb" }, + { /*sentinel*/ } +}; +#endif + static void __init at91_add_device_tc(void) { +#if defined(CONFIG_OF) + struct device_node *np; + + np = of_find_matching_node(NULL, tcb_ids); + if (np) { + of_node_put(np); + return; + } +#endif + platform_device_register(&at91sam9g45_tcb0_device); platform_device_register(&at91sam9g45_tcb1_device); } diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index 7a6512a148d4..de6dea7c5d52 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include /* * This is a thin library to solve the problem of how to portably allocate @@ -48,7 +50,13 @@ struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) spin_lock(&tc_list_lock); list_for_each_entry(tc, &tc_list, node) { - if (tc->pdev->id == block) { + if (tc->pdev->dev.of_node) { + if (of_alias_get_id(tc->pdev->dev.of_node, "tcb") + == block) { + pdev = tc->pdev; + break; + } + } else if (tc->pdev->id == block) { pdev = tc->pdev; break; } @@ -105,6 +113,18 @@ void atmel_tc_free(struct atmel_tc *tc) } EXPORT_SYMBOL_GPL(atmel_tc_free); +#if defined(CONFIG_OF) +static const struct of_device_id atmel_tcb_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-tcb", + }, { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids); +#endif + static int __init tc_probe(struct platform_device *pdev) { struct atmel_tc *tc; @@ -154,7 +174,10 @@ static int __init tc_probe(struct platform_device *pdev) } static struct platform_driver tc_driver = { - .driver.name = "atmel_tcb", + .driver = { + .name = "atmel_tcb", + .of_match_table = of_match_ptr(atmel_tcb_dt_ids), + }, }; static int __init tc_init(void) -- cgit v1.2.3 From 582d5fbd4e81e7debe5f3a0e6ce1a0bcdf636c6e Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 20 Jul 2010 19:18:51 +0200 Subject: ARM: at91/pio: add new PIO3 features This patch adds the support for new PIO controller found on some at91sam SOCs. - more peripheral multiplexing - more features to configure on a PIO (pull-down, Schmitt trigger, debouncer) - support for several IRQ triggering features (type and polarity) Support for those new features are retrieved from the device tree compatibility string. Debugfs at91_gpio file is updated to monitor configuration. Signed-off-by: Nicolas Ferre --- .../devicetree/bindings/gpio/gpio_atmel.txt | 2 +- arch/arm/boot/dts/at91sam9x5.dtsi | 8 +- arch/arm/mach-at91/board-dt.c | 1 + arch/arm/mach-at91/gpio.c | 262 +++++++++++++++++++-- arch/arm/mach-at91/include/mach/at91_pio.h | 25 ++ arch/arm/mach-at91/include/mach/gpio.h | 5 + 6 files changed, 282 insertions(+), 21 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio_atmel.txt b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt index a7bcaec913bf..66efc804806a 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_atmel.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt @@ -1,7 +1,7 @@ * Atmel GPIO controller (PIO) Required properties: -- compatible: "atmel,at91rm9200-gpio" +- compatible: "atmel,-gpio", where is at91rm9200 or at91sam9x5. - reg: Should contain GPIO controller registers location and length - interrupts: Should be the port interrupt shared by all the pins. - #gpio-cells: Should be two. The first cell is the pin number and diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index bb0c676b3393..a02e636d8a57 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -89,7 +89,7 @@ }; pioA: gpio@fffff400 { - compatible = "atmel,at91rm9200-gpio"; + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x100>; interrupts = <2 4>; #gpio-cells = <2>; @@ -98,7 +98,7 @@ }; pioB: gpio@fffff600 { - compatible = "atmel,at91rm9200-gpio"; + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; reg = <0xfffff600 0x100>; interrupts = <2 4>; #gpio-cells = <2>; @@ -107,7 +107,7 @@ }; pioC: gpio@fffff800 { - compatible = "atmel,at91rm9200-gpio"; + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; reg = <0xfffff800 0x100>; interrupts = <3 4>; #gpio-cells = <2>; @@ -116,7 +116,7 @@ }; pioD: gpio@fffffa00 { - compatible = "atmel,at91rm9200-gpio"; + compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; reg = <0xfffffa00 0x100>; interrupts = <3 4>; #gpio-cells = <2>; diff --git a/arch/arm/mach-at91/board-dt.c b/arch/arm/mach-at91/board-dt.c index acbe23c5b260..583b72472ad9 100644 --- a/arch/arm/mach-at91/board-dt.c +++ b/arch/arm/mach-at91/board-dt.c @@ -86,6 +86,7 @@ static const struct of_device_id irq_of_match[] __initconst = { { .compatible = "atmel,at91rm9200-aic", .data = at91_aic_of_init }, { .compatible = "atmel,at91rm9200-gpio", .data = at91_gpio_of_irq_setup }, + { .compatible = "atmel,at91sam9x5-gpio", .data = at91_gpio_of_irq_setup }, { /*sentinel*/ } }; diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c index 567df654a2e1..325837a264c9 100644 --- a/arch/arm/mach-at91/gpio.c +++ b/arch/arm/mach-at91/gpio.c @@ -76,6 +76,14 @@ static struct at91_gpio_chip gpio_chip[] = { }; static int gpio_banks; +static unsigned long at91_gpio_caps; + +/* All PIO controllers support PIO3 features */ +#define AT91_GPIO_CAP_PIO3 (1 << 0) + +#define has_pio3() (at91_gpio_caps & AT91_GPIO_CAP_PIO3) + +/*--------------------------------------------------------------------------*/ static inline void __iomem *pin_to_controller(unsigned pin) { @@ -92,6 +100,25 @@ static inline unsigned pin_to_mask(unsigned pin) } +static char peripheral_function(void __iomem *pio, unsigned mask) +{ + char ret = 'X'; + u8 select; + + if (pio) { + if (has_pio3()) { + select = !!(__raw_readl(pio + PIO_ABCDSR1) & mask); + select |= (!!(__raw_readl(pio + PIO_ABCDSR2) & mask) << 1); + ret = 'A' + select; + } else { + ret = __raw_readl(pio + PIO_ABSR) & mask ? + 'B' : 'A'; + } + } + + return ret; +} + /*--------------------------------------------------------------------------*/ /* Not all hardware capabilities are exposed through these calls; they @@ -139,7 +166,14 @@ int __init_or_module at91_set_A_periph(unsigned pin, int use_pullup) __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); - __raw_writel(mask, pio + PIO_ASR); + if (has_pio3()) { + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) & ~mask, + pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); + } else { + __raw_writel(mask, pio + PIO_ASR); + } __raw_writel(mask, pio + PIO_PDR); return 0; } @@ -159,7 +193,14 @@ int __init_or_module at91_set_B_periph(unsigned pin, int use_pullup) __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); - __raw_writel(mask, pio + PIO_BSR); + if (has_pio3()) { + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) | mask, + pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); + } else { + __raw_writel(mask, pio + PIO_BSR); + } __raw_writel(mask, pio + PIO_PDR); return 0; } @@ -167,8 +208,50 @@ EXPORT_SYMBOL(at91_set_B_periph); /* - * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and - * configure it for an input. + * mux the pin to the "C" internal peripheral role. + */ +int __init_or_module at91_set_C_periph(unsigned pin, int use_pullup) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + __raw_writel(mask, pio + PIO_IDR); + __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) & ~mask, pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); + __raw_writel(mask, pio + PIO_PDR); + return 0; +} +EXPORT_SYMBOL(at91_set_C_periph); + + +/* + * mux the pin to the "D" internal peripheral role. + */ +int __init_or_module at91_set_D_periph(unsigned pin, int use_pullup) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + __raw_writel(mask, pio + PIO_IDR); + __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); + __raw_writel(__raw_readl(pio + PIO_ABCDSR1) | mask, pio + PIO_ABCDSR1); + __raw_writel(__raw_readl(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); + __raw_writel(mask, pio + PIO_PDR); + return 0; +} +EXPORT_SYMBOL(at91_set_D_periph); + + +/* + * mux the pin to the gpio controller (instead of "A", "B", "C" + * or "D" peripheral), and configure it for an input. */ int __init_or_module at91_set_gpio_input(unsigned pin, int use_pullup) { @@ -188,8 +271,8 @@ EXPORT_SYMBOL(at91_set_gpio_input); /* - * mux the pin to the gpio controller (instead of "A" or "B" peripheral), - * and configure it for an output. + * mux the pin to the gpio controller (instead of "A", "B", "C" + * or "D" peripheral), and configure it for an output. */ int __init_or_module at91_set_gpio_output(unsigned pin, int value) { @@ -219,11 +302,36 @@ int __init_or_module at91_set_deglitch(unsigned pin, int is_on) if (!pio) return -EINVAL; + + if (has_pio3() && is_on) + __raw_writel(mask, pio + PIO_IFSCDR); __raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR)); return 0; } EXPORT_SYMBOL(at91_set_deglitch); +/* + * enable/disable the debounce filter; + */ +int __init_or_module at91_set_debounce(unsigned pin, int is_on, int div) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + if (is_on) { + __raw_writel(mask, pio + PIO_IFSCER); + __raw_writel(div & PIO_SCDR_DIV, pio + PIO_SCDR); + __raw_writel(mask, pio + PIO_IFER); + } else { + __raw_writel(mask, pio + PIO_IFDR); + } + return 0; +} +EXPORT_SYMBOL(at91_set_debounce); + /* * enable/disable the multi-driver; This is only valid for output and * allows the output pin to run as an open collector output. @@ -241,6 +349,41 @@ int __init_or_module at91_set_multi_drive(unsigned pin, int is_on) } EXPORT_SYMBOL(at91_set_multi_drive); +/* + * enable/disable the pull-down. + * If pull-up already enabled while calling the function, we disable it. + */ +int __init_or_module at91_set_pulldown(unsigned pin, int is_on) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + /* Disable pull-up anyway */ + __raw_writel(mask, pio + PIO_PUDR); + __raw_writel(mask, pio + (is_on ? PIO_PPDER : PIO_PPDDR)); + return 0; +} +EXPORT_SYMBOL(at91_set_pulldown); + +/* + * disable Schmitt trigger + */ +int __init_or_module at91_disable_schmitt_trig(unsigned pin) +{ + void __iomem *pio = pin_to_controller(pin); + unsigned mask = pin_to_mask(pin); + + if (!pio || !has_pio3()) + return -EINVAL; + + __raw_writel(__raw_readl(pio + PIO_SCHMITT) | mask, pio + PIO_SCHMITT); + return 0; +} +EXPORT_SYMBOL(at91_disable_schmitt_trig); + /* * assuming the pin is muxed as a gpio output, set its value. */ @@ -347,7 +490,10 @@ void at91_gpio_resume(void) * To use any AT91_PIN_* as an externally triggered IRQ, first call * at91_set_gpio_input() then maybe enable its glitch filter. * Then just request_irq() with the pin ID; it works like any ARM IRQ - * handler, though it always triggers on rising and falling edges. + * handler. + * First implementation always triggers on rising and falling edges + * whereas the newer PIO3 can be additionally configured to trigger on + * level, edge with any polarity. * * Alternatively, certain pins may be used directly as IRQ0..IRQ6 after * configuring them with at91_set_a_periph() or at91_set_b_periph(). @@ -385,12 +531,55 @@ static int gpio_irq_type(struct irq_data *d, unsigned type) } } +/* Alternate irq type for PIO3 support */ +static int alt_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + __raw_writel(mask, pio + PIO_ESR); + __raw_writel(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_FALLING: + __raw_writel(mask, pio + PIO_ESR); + __raw_writel(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_LOW: + __raw_writel(mask, pio + PIO_LSR); + __raw_writel(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_HIGH: + __raw_writel(mask, pio + PIO_LSR); + __raw_writel(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_BOTH: + /* + * disable additional interrupt modes: + * fall back to default behavior + */ + __raw_writel(mask, pio + PIO_AIMDR); + return 0; + case IRQ_TYPE_NONE: + default: + pr_warn("AT91: No type for irq %d\n", gpio_to_irq(d->irq)); + return -EINVAL; + } + + /* enable additional interrupt modes */ + __raw_writel(mask, pio + PIO_AIMER); + + return 0; +} + static struct irq_chip gpio_irqchip = { .name = "GPIO", .irq_disable = gpio_irq_mask, .irq_mask = gpio_irq_mask, .irq_unmask = gpio_irq_unmask, - .irq_set_type = gpio_irq_type, + /* .irq_set_type is set dynamically */ .irq_set_wake = gpio_irq_set_wake, }; @@ -433,6 +622,33 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) #ifdef CONFIG_DEBUG_FS +static void gpio_printf(struct seq_file *s, void __iomem *pio, unsigned mask) +{ + char *trigger = NULL; + char *polarity = NULL; + + if (__raw_readl(pio + PIO_IMR) & mask) { + if (!has_pio3() || !(__raw_readl(pio + PIO_AIMMR) & mask )) { + trigger = "edge"; + polarity = "both"; + } else { + if (__raw_readl(pio + PIO_ELSR) & mask) { + trigger = "level"; + polarity = __raw_readl(pio + PIO_FRLHSR) & mask ? + "high" : "low"; + } else { + trigger = "edge"; + polarity = __raw_readl(pio + PIO_FRLHSR) & mask ? + "rising" : "falling"; + } + } + seq_printf(s, "IRQ:%s-%s\t", trigger, polarity); + } else { + seq_printf(s, "GPIO:%s\t\t", + __raw_readl(pio + PIO_PDSR) & mask ? "1" : "0"); + } +} + static int at91_gpio_show(struct seq_file *s, void *unused) { int bank, j; @@ -440,7 +656,7 @@ static int at91_gpio_show(struct seq_file *s, void *unused) /* print heading */ seq_printf(s, "Pin\t"); for (bank = 0; bank < gpio_banks; bank++) { - seq_printf(s, "PIO%c\t", 'A' + bank); + seq_printf(s, "PIO%c\t\t", 'A' + bank); }; seq_printf(s, "\n\n"); @@ -454,11 +670,10 @@ static int at91_gpio_show(struct seq_file *s, void *unused) unsigned mask = pin_to_mask(pin); if (__raw_readl(pio + PIO_PSR) & mask) - seq_printf(s, "GPIO:%s", __raw_readl(pio + PIO_PDSR) & mask ? "1" : "0"); + gpio_printf(s, pio, mask); else - seq_printf(s, "%s", __raw_readl(pio + PIO_ABSR) & mask ? "B" : "A"); - - seq_printf(s, "\t"); + seq_printf(s, "%c\t\t", + peripheral_function(pio, mask)); } seq_printf(s, "\n"); @@ -529,6 +744,12 @@ int __init at91_gpio_of_irq_setup(struct device_node *node, int alias_idx = of_alias_get_id(node, "gpio"); struct at91_gpio_chip *at91_gpio = &gpio_chip[alias_idx]; + /* Setup proper .irq_set_type function */ + if (has_pio3()) + gpio_irqchip.irq_set_type = alt_gpio_irq_type; + else + gpio_irqchip.irq_set_type = gpio_irq_type; + /* Disable irqs of this PIO controller */ __raw_writel(~0, at91_gpio->regbase + PIO_IDR); @@ -593,6 +814,12 @@ void __init at91_gpio_irq_setup(void) int gpio_irqnbr = 0; struct at91_gpio_chip *this, *prev; + /* Setup proper .irq_set_type function */ + if (has_pio3()) + gpio_irqchip.irq_set_type = alt_gpio_irq_type; + else + gpio_irqchip.irq_set_type = gpio_irq_type; + for (pioc = 0, this = gpio_chip, prev = NULL; pioc++ < gpio_banks; prev = this, this++) { @@ -696,9 +923,8 @@ static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) at91_get_gpio_value(pin) ? "set" : "clear"); else - seq_printf(s, "[periph %s]\n", - __raw_readl(pio + PIO_ABSR) & - mask ? "B" : "A"); + seq_printf(s, "[periph %c]\n", + peripheral_function(pio, mask)); } } } @@ -781,6 +1007,10 @@ static void __init of_at91_gpio_init_one(struct device_node *np) goto ioremap_err; } + /* Get capabilities from compatibility property */ + if (of_device_is_compatible(np, "atmel,at91sam9x5-gpio")) + at91_gpio_caps |= AT91_GPIO_CAP_PIO3; + /* Setup clock */ if (at91_gpio_setup_clk(alias_idx)) goto ioremap_err; diff --git a/arch/arm/mach-at91/include/mach/at91_pio.h b/arch/arm/mach-at91/include/mach/at91_pio.h index c6a31bf8a5c6..732b11c37f1a 100644 --- a/arch/arm/mach-at91/include/mach/at91_pio.h +++ b/arch/arm/mach-at91/include/mach/at91_pio.h @@ -40,10 +40,35 @@ #define PIO_PUER 0x64 /* Pull-up Enable Register */ #define PIO_PUSR 0x68 /* Pull-up Status Register */ #define PIO_ASR 0x70 /* Peripheral A Select Register */ +#define PIO_ABCDSR1 0x70 /* Peripheral ABCD Select Register 1 [some sam9 only] */ #define PIO_BSR 0x74 /* Peripheral B Select Register */ +#define PIO_ABCDSR2 0x74 /* Peripheral ABCD Select Register 2 [some sam9 only] */ #define PIO_ABSR 0x78 /* AB Status Register */ +#define PIO_IFSCDR 0x80 /* Input Filter Slow Clock Disable Register */ +#define PIO_IFSCER 0x84 /* Input Filter Slow Clock Enable Register */ +#define PIO_IFSCSR 0x88 /* Input Filter Slow Clock Status Register */ +#define PIO_SCDR 0x8c /* Slow Clock Divider Debouncing Register */ +#define PIO_SCDR_DIV (0x3fff << 0) /* Slow Clock Divider Mask */ +#define PIO_PPDDR 0x90 /* Pad Pull-down Disable Register */ +#define PIO_PPDER 0x94 /* Pad Pull-down Enable Register */ +#define PIO_PPDSR 0x98 /* Pad Pull-down Status Register */ #define PIO_OWER 0xa0 /* Output Write Enable Register */ #define PIO_OWDR 0xa4 /* Output Write Disable Register */ #define PIO_OWSR 0xa8 /* Output Write Status Register */ +#define PIO_AIMER 0xb0 /* Additional Interrupt Modes Enable Register */ +#define PIO_AIMDR 0xb4 /* Additional Interrupt Modes Disable Register */ +#define PIO_AIMMR 0xb8 /* Additional Interrupt Modes Mask Register */ +#define PIO_ESR 0xc0 /* Edge Select Register */ +#define PIO_LSR 0xc4 /* Level Select Register */ +#define PIO_ELSR 0xc8 /* Edge/Level Status Register */ +#define PIO_FELLSR 0xd0 /* Falling Edge/Low Level Select Register */ +#define PIO_REHLSR 0xd4 /* Rising Edge/ High Level Select Register */ +#define PIO_FRLHSR 0xd8 /* Fall/Rise - Low/High Status Register */ +#define PIO_SCHMITT 0x100 /* Schmitt Trigger Register */ + +#define ABCDSR_PERIPH_A 0x0 +#define ABCDSR_PERIPH_B 0x1 +#define ABCDSR_PERIPH_C 0x2 +#define ABCDSR_PERIPH_D 0x3 #endif diff --git a/arch/arm/mach-at91/include/mach/gpio.h b/arch/arm/mach-at91/include/mach/gpio.h index 7cf009be8d0d..eed465ab0dd7 100644 --- a/arch/arm/mach-at91/include/mach/gpio.h +++ b/arch/arm/mach-at91/include/mach/gpio.h @@ -191,10 +191,15 @@ extern int __init_or_module at91_set_GPIO_periph(unsigned pin, int use_pullup); extern int __init_or_module at91_set_A_periph(unsigned pin, int use_pullup); extern int __init_or_module at91_set_B_periph(unsigned pin, int use_pullup); +extern int __init_or_module at91_set_C_periph(unsigned pin, int use_pullup); +extern int __init_or_module at91_set_D_periph(unsigned pin, int use_pullup); extern int __init_or_module at91_set_gpio_input(unsigned pin, int use_pullup); extern int __init_or_module at91_set_gpio_output(unsigned pin, int value); extern int __init_or_module at91_set_deglitch(unsigned pin, int is_on); +extern int __init_or_module at91_set_debounce(unsigned pin, int is_on, int div); extern int __init_or_module at91_set_multi_drive(unsigned pin, int is_on); +extern int __init_or_module at91_set_pulldown(unsigned pin, int is_on); +extern int __init_or_module at91_disable_schmitt_trig(unsigned pin); /* callable at any time */ extern int at91_set_gpio_value(unsigned pin, int value); -- cgit v1.2.3 From 4bdd47997f110ee06ed8a1c6668d12106936ba12 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 3 Mar 2012 23:20:00 +0800 Subject: ASoC: sgtl5000: rename device tree binding document It moves and renames sgtl5000 device tree binding document to make it aligned with other codecs. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/sgtl5000.txt | 11 +++++++++++ .../devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/sgtl5000.txt delete mode 100644 Documentation/devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt new file mode 100644 index 000000000000..2c3cd413f042 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -0,0 +1,11 @@ +* Freescale SGTL5000 Stereo Codec + +Required properties: +- compatible : "fsl,sgtl5000". + +Example: + +codec: sgtl5000@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; +}; diff --git a/Documentation/devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt b/Documentation/devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt deleted file mode 100644 index 2c3cd413f042..000000000000 --- a/Documentation/devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt +++ /dev/null @@ -1,11 +0,0 @@ -* Freescale SGTL5000 Stereo Codec - -Required properties: -- compatible : "fsl,sgtl5000". - -Example: - -codec: sgtl5000@0a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; -}; -- cgit v1.2.3 From 384ebe1c2849160d040df3e68634ec506f13d9ff Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Tue, 16 Aug 2011 11:53:02 +0200 Subject: gpio/omap: Add DT support to GPIO driver Adapt the GPIO driver to retrieve information from a DT file. Allocate the irq_base dynamically and rename bank->virtual_irq_start to bank->irq_base. Change irq_base type to int instead of u16 to match irq_alloc_descs output. Add documentation for GPIO properties specific to OMAP. Signed-off-by: Benoit Cousson Cc: Tarun Kanti DebBarma Acked-by: Rob Herring --- .../devicetree/bindings/gpio/gpio-omap.txt | 36 ++++++ drivers/gpio/gpio-omap.c | 121 +++++++++++++++++++-- 2 files changed, 148 insertions(+), 9 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-omap.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio-omap.txt b/Documentation/devicetree/bindings/gpio/gpio-omap.txt new file mode 100644 index 000000000000..bff51a2fee1e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-omap.txt @@ -0,0 +1,36 @@ +OMAP GPIO controller bindings + +Required properties: +- compatible: + - "ti,omap2-gpio" for OMAP2 controllers + - "ti,omap3-gpio" for OMAP3 controllers + - "ti,omap4-gpio" for OMAP4 controllers +- #gpio-cells : Should be two. + - first cell is the pin number + - second cell is used to specify optional parameters (unused) +- gpio-controller : Marks the device node as a GPIO controller. +- #interrupt-cells : Should be 2. +- interrupt-controller: Mark the device node as an interrupt controller + The first cell is the GPIO number. + The second cell is used to specify flags: + bits[3:0] trigger type and level flags: + 1 = low-to-high edge triggered. + 2 = high-to-low edge triggered. + 4 = active high level-sensitive. + 8 = active low level-sensitive. + +OMAP specific properties: +- ti,hwmods: Name of the hwmod associated to the GPIO: + "gpio", being the 1-based instance number from the HW spec + + +Example: + +gpio4: gpio4 { + compatible = "ti,omap4-gpio"; + ti,hwmods = "gpio4"; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; +}; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index c3a9dc8fe732..bc2bd698ff2a 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include #include @@ -52,7 +55,8 @@ struct gpio_bank { struct list_head node; void __iomem *base; u16 irq; - u16 virtual_irq_start; + int irq_base; + struct irq_domain *domain; u32 suspend_wakeup; u32 saved_wakeup; u32 non_wakeup_gpios; @@ -669,7 +673,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) if (!isr) break; - gpio_irq = bank->virtual_irq_start; + gpio_irq = bank->irq_base; for (; isr != 0; isr >>= 1, gpio_irq++) { gpio_index = GPIO_INDEX(bank, irq_to_gpio(gpio_irq)); @@ -915,7 +919,7 @@ static int gpio_2irq(struct gpio_chip *chip, unsigned offset) struct gpio_bank *bank; bank = container_of(chip, struct gpio_bank, chip); - return bank->virtual_irq_start + offset; + return bank->irq_base + offset; } /*---------------------------------------------------------------------*/ @@ -1028,8 +1032,7 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) gpiochip_add(&bank->chip); - for (j = bank->virtual_irq_start; - j < bank->virtual_irq_start + bank->width; j++) { + for (j = bank->irq_base; j < bank->irq_base + bank->width; j++) { irq_set_lockdep_class(j, &gpio_lock_class); irq_set_chip_data(j, bank); if (bank->is_mpuio) { @@ -1044,15 +1047,22 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank) irq_set_handler_data(bank->irq, bank); } +static const struct of_device_id omap_gpio_match[]; + static int __devinit omap_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + const struct of_device_id *match; struct omap_gpio_platform_data *pdata; struct resource *res; struct gpio_bank *bank; int ret = 0; - if (!dev->platform_data) + match = of_match_device(of_match_ptr(omap_gpio_match), dev); + + pdata = match ? match->data : dev->platform_data; + if (!pdata) return -EINVAL; bank = devm_kzalloc(&pdev->dev, sizeof(struct gpio_bank), GFP_KERNEL); @@ -1068,9 +1078,6 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) } bank->irq = res->start; - - pdata = pdev->dev.platform_data; - bank->virtual_irq_start = pdata->virtual_irq_start; bank->dev = dev; bank->dbck_flag = pdata->dbck_flag; bank->stride = pdata->bank_stride; @@ -1080,6 +1087,18 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) bank->loses_context = pdata->loses_context; bank->get_context_loss_count = pdata->get_context_loss_count; bank->regs = pdata->regs; +#ifdef CONFIG_OF_GPIO + bank->chip.of_node = of_node_get(node); +#endif + + bank->irq_base = irq_alloc_descs(-1, 0, bank->width, 0); + if (bank->irq_base < 0) { + dev_err(dev, "Couldn't allocate IRQ numbers\n"); + return -ENODEV; + } + + bank->domain = irq_domain_add_legacy(node, bank->width, bank->irq_base, + 0, &irq_domain_simple_ops, NULL); if (bank->regs->set_dataout && bank->regs->clr_dataout) bank->set_dataout = _set_gpio_dataout_reg; @@ -1387,11 +1406,95 @@ static const struct dev_pm_ops gpio_pm_ops = { NULL) }; +#if defined(CONFIG_OF) +static struct omap_gpio_reg_offs omap2_gpio_regs = { + .revision = OMAP24XX_GPIO_REVISION, + .direction = OMAP24XX_GPIO_OE, + .datain = OMAP24XX_GPIO_DATAIN, + .dataout = OMAP24XX_GPIO_DATAOUT, + .set_dataout = OMAP24XX_GPIO_SETDATAOUT, + .clr_dataout = OMAP24XX_GPIO_CLEARDATAOUT, + .irqstatus = OMAP24XX_GPIO_IRQSTATUS1, + .irqstatus2 = OMAP24XX_GPIO_IRQSTATUS2, + .irqenable = OMAP24XX_GPIO_IRQENABLE1, + .irqenable2 = OMAP24XX_GPIO_IRQENABLE2, + .set_irqenable = OMAP24XX_GPIO_SETIRQENABLE1, + .clr_irqenable = OMAP24XX_GPIO_CLEARIRQENABLE1, + .debounce = OMAP24XX_GPIO_DEBOUNCE_VAL, + .debounce_en = OMAP24XX_GPIO_DEBOUNCE_EN, + .ctrl = OMAP24XX_GPIO_CTRL, + .wkup_en = OMAP24XX_GPIO_WAKE_EN, + .leveldetect0 = OMAP24XX_GPIO_LEVELDETECT0, + .leveldetect1 = OMAP24XX_GPIO_LEVELDETECT1, + .risingdetect = OMAP24XX_GPIO_RISINGDETECT, + .fallingdetect = OMAP24XX_GPIO_FALLINGDETECT, +}; + +static struct omap_gpio_reg_offs omap4_gpio_regs = { + .revision = OMAP4_GPIO_REVISION, + .direction = OMAP4_GPIO_OE, + .datain = OMAP4_GPIO_DATAIN, + .dataout = OMAP4_GPIO_DATAOUT, + .set_dataout = OMAP4_GPIO_SETDATAOUT, + .clr_dataout = OMAP4_GPIO_CLEARDATAOUT, + .irqstatus = OMAP4_GPIO_IRQSTATUS0, + .irqstatus2 = OMAP4_GPIO_IRQSTATUS1, + .irqenable = OMAP4_GPIO_IRQSTATUSSET0, + .irqenable2 = OMAP4_GPIO_IRQSTATUSSET1, + .set_irqenable = OMAP4_GPIO_IRQSTATUSSET0, + .clr_irqenable = OMAP4_GPIO_IRQSTATUSCLR0, + .debounce = OMAP4_GPIO_DEBOUNCINGTIME, + .debounce_en = OMAP4_GPIO_DEBOUNCENABLE, + .ctrl = OMAP4_GPIO_CTRL, + .wkup_en = OMAP4_GPIO_IRQWAKEN0, + .leveldetect0 = OMAP4_GPIO_LEVELDETECT0, + .leveldetect1 = OMAP4_GPIO_LEVELDETECT1, + .risingdetect = OMAP4_GPIO_RISINGDETECT, + .fallingdetect = OMAP4_GPIO_FALLINGDETECT, +}; + +static struct omap_gpio_platform_data omap2_pdata = { + .regs = &omap2_gpio_regs, + .bank_width = 32, + .dbck_flag = false, +}; + +static struct omap_gpio_platform_data omap3_pdata = { + .regs = &omap2_gpio_regs, + .bank_width = 32, + .dbck_flag = true, +}; + +static struct omap_gpio_platform_data omap4_pdata = { + .regs = &omap4_gpio_regs, + .bank_width = 32, + .dbck_flag = true, +}; + +static const struct of_device_id omap_gpio_match[] = { + { + .compatible = "ti,omap4-gpio", + .data = &omap4_pdata, + }, + { + .compatible = "ti,omap3-gpio", + .data = &omap3_pdata, + }, + { + .compatible = "ti,omap2-gpio", + .data = &omap2_pdata, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap_gpio_match); +#endif + static struct platform_driver omap_gpio_driver = { .probe = omap_gpio_probe, .driver = { .name = "omap_gpio", .pm = &gpio_pm_ops, + .of_match_table = of_match_ptr(omap_gpio_match), }, }; -- cgit v1.2.3 From 9d5ef2663fe220a88412a7190942b7d933da0333 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Mon, 5 Mar 2012 22:31:04 +0800 Subject: ASoC: fsl: add dt support for imx-audmux It adds device tree probe support for imx-audmux driver. Signed-off-by: Richard Zhao Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/imx-audmux.txt | 13 +++++++++++++ sound/soc/imx/imx-audmux.c | 14 ++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/imx-audmux.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.txt b/Documentation/devicetree/bindings/sound/imx-audmux.txt new file mode 100644 index 000000000000..215aa9817213 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audmux.txt @@ -0,0 +1,13 @@ +Freescale Digital Audio Mux (AUDMUX) device + +Required properties: +- compatible : "fsl,imx21-audmux" for AUDMUX version firstly used on i.MX21, + or "fsl,imx31-audmux" for the version firstly used on i.MX31. +- reg : Should contain AUDMUX registers location and length + +Example: + +audmux@021d8000 { + compatible = "fsl,imx6q-audmux", "fsl,imx31-audmux"; + reg = <0x021d8000 0x4000>; +}; diff --git a/sound/soc/imx/imx-audmux.c b/sound/soc/imx/imx-audmux.c index 87f8768e1cdc..b83699d905bb 100644 --- a/sound/soc/imx/imx-audmux.c +++ b/sound/soc/imx/imx-audmux.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -197,6 +199,13 @@ static struct platform_device_id imx_audmux_ids[] = { }; MODULE_DEVICE_TABLE(platform, imx_audmux_ids); +static const struct of_device_id imx_audmux_dt_ids[] = { + { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, + { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); + static const uint8_t port_mapping[] = { 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c, }; @@ -243,6 +252,8 @@ EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); static int __init imx_audmux_probe(struct platform_device *pdev) { struct resource *res; + const struct of_device_id *of_id = + of_match_device(imx_audmux_dt_ids, &pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); audmux_base = devm_request_and_ioremap(&pdev->dev, res); @@ -256,6 +267,8 @@ static int __init imx_audmux_probe(struct platform_device *pdev) audmux_clk = NULL; } + if (of_id) + pdev->id_entry = of_id->data; audmux_type = pdev->id_entry->driver_data; if (audmux_type == IMX31_AUDMUX) audmux_debugfs_init(); @@ -279,6 +292,7 @@ static struct platform_driver imx_audmux_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = imx_audmux_dt_ids, } }; -- cgit v1.2.3 From 46e446db4fb2cdb2f1bc69d3981fa23738a42835 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Thu, 1 Mar 2012 13:49:57 +0800 Subject: Document: devicetree: add OF documents for arch-mmp Add OF support in Document/devicetree directory. Signed-off-by: Haojian Zhuang Acked-by: Arnd Bergmann --- Documentation/devicetree/bindings/arm/mrvl.txt | 6 ++++ .../devicetree/bindings/gpio/mrvl-gpio.txt | 23 ++++++++++++++ Documentation/devicetree/bindings/i2c/mrvl-i2c.txt | 37 ++++++++++++++++++++++ .../devicetree/bindings/rtc/sa1100-rtc.txt | 17 ++++++++++ .../devicetree/bindings/serial/mrvl-serial.txt | 4 +++ 5 files changed, 87 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/mrvl.txt create mode 100644 Documentation/devicetree/bindings/gpio/mrvl-gpio.txt create mode 100644 Documentation/devicetree/bindings/i2c/mrvl-i2c.txt create mode 100644 Documentation/devicetree/bindings/rtc/sa1100-rtc.txt create mode 100644 Documentation/devicetree/bindings/serial/mrvl-serial.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/mrvl.txt b/Documentation/devicetree/bindings/arm/mrvl.txt new file mode 100644 index 000000000000..d8de933e9d81 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mrvl.txt @@ -0,0 +1,6 @@ +Marvell Platforms Device Tree Bindings +---------------------------------------------------- + +PXA168 Aspenite Board +Required root node properties: + - compatible = "mrvl,pxa168-aspenite", "mrvl,pxa168"; diff --git a/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt b/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt new file mode 100644 index 000000000000..1e34cfe5ebea --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt @@ -0,0 +1,23 @@ +* Marvell PXA GPIO controller + +Required properties: +- compatible : Should be "mrvl,pxa-gpio" or "mrvl,mmp-gpio" +- reg : Address and length of the register set for the device +- interrupts : Should be the port interrupt shared by all gpio pins, if +- interrupt-name : Should be the name of irq resource. + one number. +- gpio-controller : Marks the device node as a gpio controller. +- #gpio-cells : Should be one. It is the pin number. + +Example: + + gpio: gpio@d4019000 { + compatible = "mrvl,mmp-gpio", "mrvl,pxa-gpio"; + reg = <0xd4019000 0x1000>; + interrupts = <49>, <17>, <18>; + interrupt-name = "gpio_mux", "gpio0", "gpio1"; + gpio-controller; + #gpio-cells = <1>; + interrupt-controller; + #interrupt-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt new file mode 100644 index 000000000000..071eb3caae91 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt @@ -0,0 +1,37 @@ +* I2C + +Required properties : + + - reg : Offset and length of the register set for the device + - compatible : should be "mrvl,mmp-twsi" where CHIP is the name of a + compatible processor, e.g. pxa168, pxa910, mmp2, mmp3. + For the pxa2xx/pxa3xx, an additional node "mrvl,pxa-i2c" is required + as shown in the example below. + +Recommended properties : + + - interrupts : where a is the interrupt number and b is a + field that represents an encoding of the sense and level + information for the interrupt. This should be encoded based on + the information in section 2) depending on the type of interrupt + controller you have. + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + - mrvl,i2c-polling : Disable interrupt of i2c controller. Polling + status register of i2c controller instead. + - mrvl,i2c-fast-mode : Enable fast mode of i2c controller. + +Examples: + twsi1: i2c@d4011000 { + compatible = "mrvl,mmp-twsi", "mrvl,pxa-i2c"; + reg = <0xd4011000 0x1000>; + interrupts = <7>; + mrvl,i2c-fast-mode; + }; + + twsi2: i2c@d4025000 { + compatible = "mrvl,mmp-twsi", "mrvl,pxa-i2c"; + reg = <0xd4025000 0x1000>; + interrupts = <58>; + }; + diff --git a/Documentation/devicetree/bindings/rtc/sa1100-rtc.txt b/Documentation/devicetree/bindings/rtc/sa1100-rtc.txt new file mode 100644 index 000000000000..0cda19ad4859 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/sa1100-rtc.txt @@ -0,0 +1,17 @@ +* Marvell Real Time Clock controller + +Required properties: +- compatible: should be "mrvl,sa1100-rtc" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: Should be two. The first interrupt number is the rtc alarm + interrupt and the second interrupt number is the rtc hz interrupt. +- interrupt-names: Assign name of irq resource. + +Example: + rtc: rtc@d4010000 { + compatible = "mrvl,mmp-rtc"; + reg = <0xd4010000 0x1000>; + interrupts = <5>, <6>; + interrupt-name = "rtc 1Hz", "rtc alarm"; + }; diff --git a/Documentation/devicetree/bindings/serial/mrvl-serial.txt b/Documentation/devicetree/bindings/serial/mrvl-serial.txt new file mode 100644 index 000000000000..d744340de887 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/mrvl-serial.txt @@ -0,0 +1,4 @@ +PXA UART controller + +Required properties: +- compatible : should be "mrvl,mmp-uart" or "mrvl,pxa-uart". -- cgit v1.2.3 From 979b907fa55be8cdbbf455b9204b7e4602f303e6 Mon Sep 17 00:00:00 2001 From: Zhiwu Song Date: Wed, 8 Feb 2012 23:28:35 +0800 Subject: i2c: add CSR SiRFprimaII on-chip I2C controllers driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SiRFprimaII is the latest generation application processor from CSR’s multi-function SoC product family. The SoC support codes are in arch/arm/mach-prima2 from Linux mainline 3.0. There are two I2C controllers on primaII, features include: * Two I2C controller modules are on chip * RISC I/O bus read write register * Up to 16 bytes data buffer for issuing commands and writing data at the same time * Up to 16 commands, and receiving read data 16 bytes at a time * Error INT report (ACK check) * No-ACK bus protocols (SCCB bus protocols) Signed-off-by: Zhiwu Song Signed-off-by: Xiangzhen Ye Signed-off-by: Yuping Luo Signed-off-by: Barry Song Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/sirf-i2c.txt | 19 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-sirf.c | 459 +++++++++++++++++++++ 4 files changed, 489 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/sirf-i2c.txt create mode 100644 drivers/i2c/busses/i2c-sirf.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/sirf-i2c.txt b/Documentation/devicetree/bindings/i2c/sirf-i2c.txt new file mode 100644 index 000000000000..7baf9e133fa8 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/sirf-i2c.txt @@ -0,0 +1,19 @@ +I2C for SiRFprimaII platforms + +Required properties : +- compatible : Must be "sirf,prima2-i2c" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: interrupt number to the cpu. + +Optional properties: +- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. + The absence of the propoerty indicates the default frequency 100 kHz. + +Examples : + +i2c0: i2c@b00e0000 { + compatible = "sirf,prima2-i2c"; + reg = <0xb00e0000 0x10000>; + interrupts = <24>; +}; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e99bfa86ba71..71c1b0a7535c 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -645,6 +645,16 @@ config I2C_SIMTEC This driver can also be built as a module. If so, the module will be called i2c-simtec. +config I2C_SIRF + tristate "CSR SiRFprimaII I2C interface" + depends on ARCH_PRIMA2 + help + If you say yes to this option, support will be included for the + CSR SiRFprimaII I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-sirf. + config I2C_STU300 tristate "ST Microelectronics DDC I2C interface" depends on MACH_U300 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 8f9e7b6677e1..569567b0d027 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o +obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c new file mode 100644 index 000000000000..5574a47792fb --- /dev/null +++ b/drivers/i2c/busses/i2c-sirf.c @@ -0,0 +1,459 @@ +/* + * I2C bus driver for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIRFSOC_I2C_CLK_CTRL 0x00 +#define SIRFSOC_I2C_STATUS 0x0C +#define SIRFSOC_I2C_CTRL 0x10 +#define SIRFSOC_I2C_IO_CTRL 0x14 +#define SIRFSOC_I2C_SDA_DELAY 0x18 +#define SIRFSOC_I2C_CMD_START 0x1C +#define SIRFSOC_I2C_CMD_BUF 0x30 +#define SIRFSOC_I2C_DATA_BUF 0x80 + +#define SIRFSOC_I2C_CMD_BUF_MAX 16 +#define SIRFSOC_I2C_DATA_BUF_MAX 16 + +#define SIRFSOC_I2C_CMD(x) (SIRFSOC_I2C_CMD_BUF + (x)*0x04) +#define SIRFSOC_I2C_DATA_MASK(x) (0xFF<<(((x)&3)*8)) +#define SIRFSOC_I2C_DATA_SHIFT(x) (((x)&3)*8) + +#define SIRFSOC_I2C_DIV_MASK (0xFFFF) + +/* I2C status flags */ +#define SIRFSOC_I2C_STAT_BUSY BIT(0) +#define SIRFSOC_I2C_STAT_TIP BIT(1) +#define SIRFSOC_I2C_STAT_NACK BIT(2) +#define SIRFSOC_I2C_STAT_TR_INT BIT(4) +#define SIRFSOC_I2C_STAT_STOP BIT(6) +#define SIRFSOC_I2C_STAT_CMD_DONE BIT(8) +#define SIRFSOC_I2C_STAT_ERR BIT(9) +#define SIRFSOC_I2C_CMD_INDEX (0x1F<<16) + +/* I2C control flags */ +#define SIRFSOC_I2C_RESET BIT(0) +#define SIRFSOC_I2C_CORE_EN BIT(1) +#define SIRFSOC_I2C_MASTER_MODE BIT(2) +#define SIRFSOC_I2C_CMD_DONE_EN BIT(11) +#define SIRFSOC_I2C_ERR_INT_EN BIT(12) + +#define SIRFSOC_I2C_SDA_DELAY_MASK (0xFF) +#define SIRFSOC_I2C_SCLF_FILTER (3<<8) + +#define SIRFSOC_I2C_START_CMD BIT(0) + +#define SIRFSOC_I2C_CMD_RP(x) ((x)&0x7) +#define SIRFSOC_I2C_NACK BIT(3) +#define SIRFSOC_I2C_WRITE BIT(4) +#define SIRFSOC_I2C_READ BIT(5) +#define SIRFSOC_I2C_STOP BIT(6) +#define SIRFSOC_I2C_START BIT(7) + +#define SIRFSOC_I2C_DEFAULT_SPEED 100000 + +struct sirfsoc_i2c { + void __iomem *base; + struct clk *clk; + u32 cmd_ptr; /* Current position in CMD buffer */ + u8 *buf; /* Buffer passed by user */ + u32 msg_len; /* Message length */ + u32 finished_len; /* number of bytes read/written */ + u32 read_cmd_len; /* number of read cmd sent */ + int msg_read; /* 1 indicates a read message */ + int err_status; /* 1 indicates an error on bus */ + + u32 sda_delay; /* For suspend/resume */ + u32 clk_div; + int last; /* Last message in transfer, STOP cmd can be sent */ + + struct completion done; /* indicates completion of message transfer */ + struct i2c_adapter adapter; +}; + +static void i2c_sirfsoc_read_data(struct sirfsoc_i2c *siic) +{ + u32 data = 0; + int i; + + for (i = 0; i < siic->read_cmd_len; i++) { + if (!(i & 0x3)) + data = readl(siic->base + SIRFSOC_I2C_DATA_BUF + i); + siic->buf[siic->finished_len++] = + (u8)((data & SIRFSOC_I2C_DATA_MASK(i)) >> + SIRFSOC_I2C_DATA_SHIFT(i)); + } +} + +static void i2c_sirfsoc_queue_cmd(struct sirfsoc_i2c *siic) +{ + u32 regval; + int i = 0; + + if (siic->msg_read) { + while (((siic->finished_len + i) < siic->msg_len) + && (siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX)) { + regval = SIRFSOC_I2C_READ | SIRFSOC_I2C_CMD_RP(0); + if (((siic->finished_len + i) == + (siic->msg_len - 1)) && siic->last) + regval |= SIRFSOC_I2C_STOP | SIRFSOC_I2C_NACK; + writel(regval, + siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + i++; + } + + siic->read_cmd_len = i; + } else { + while ((siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX - 1) + && (siic->finished_len < siic->msg_len)) { + regval = SIRFSOC_I2C_WRITE | SIRFSOC_I2C_CMD_RP(0); + if ((siic->finished_len == (siic->msg_len - 1)) + && siic->last) + regval |= SIRFSOC_I2C_STOP; + writel(regval, + siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + writel(siic->buf[siic->finished_len++], + siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + } + } + siic->cmd_ptr = 0; + + /* Trigger the transfer */ + writel(SIRFSOC_I2C_START_CMD, siic->base + SIRFSOC_I2C_CMD_START); +} + +static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id) +{ + struct sirfsoc_i2c *siic = (struct sirfsoc_i2c *)dev_id; + u32 i2c_stat = readl(siic->base + SIRFSOC_I2C_STATUS); + + if (i2c_stat & SIRFSOC_I2C_STAT_ERR) { + /* Error conditions */ + siic->err_status = 1; + writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS); + + if (i2c_stat & SIRFSOC_I2C_STAT_NACK) + dev_err(&siic->adapter.dev, "ACK not received\n"); + else + dev_err(&siic->adapter.dev, "I2C error\n"); + + complete(&siic->done); + } else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) { + /* CMD buffer execution complete */ + if (siic->msg_read) + i2c_sirfsoc_read_data(siic); + if (siic->finished_len == siic->msg_len) + complete(&siic->done); + else /* Fill a new CMD buffer for left data */ + i2c_sirfsoc_queue_cmd(siic); + + writel(SIRFSOC_I2C_STAT_CMD_DONE, siic->base + SIRFSOC_I2C_STATUS); + } + + return IRQ_HANDLED; +} + +static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic, + struct i2c_msg *msg) +{ + unsigned char addr; + u32 regval = SIRFSOC_I2C_START | SIRFSOC_I2C_CMD_RP(0) | SIRFSOC_I2C_WRITE; + + /* no data and last message -> add STOP */ + if (siic->last && (msg->len == 0)) + regval |= SIRFSOC_I2C_STOP; + + writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); + + addr = msg->addr << 1; /* Generate address */ + if (msg->flags & I2C_M_RD) + addr |= 1; + + writel(addr, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); +} + +static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg) +{ + u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL); + /* timeout waiting for the xfer to finish or fail */ + int timeout = msecs_to_jiffies((msg->len + 1) * 50); + int ret = 0; + + i2c_sirfsoc_set_address(siic, msg); + + writel(regval | SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN, + siic->base + SIRFSOC_I2C_CTRL); + i2c_sirfsoc_queue_cmd(siic); + + if (wait_for_completion_timeout(&siic->done, timeout) == 0) { + siic->err_status = 1; + dev_err(&siic->adapter.dev, "Transfer timeout\n"); + } + + writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN), + siic->base + SIRFSOC_I2C_CTRL); + writel(0, siic->base + SIRFSOC_I2C_CMD_START); + + if (siic->err_status) { + writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET, + siic->base + SIRFSOC_I2C_CTRL); + while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET) + cpu_relax(); + + ret = -EIO; + } + + return ret; +} + +static u32 i2c_sirfsoc_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static int i2c_sirfsoc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct sirfsoc_i2c *siic = adap->algo_data; + int i, ret; + + clk_enable(siic->clk); + + for (i = 0; i < num; i++) { + siic->buf = msgs[i].buf; + siic->msg_len = msgs[i].len; + siic->msg_read = !!(msgs[i].flags & I2C_M_RD); + siic->err_status = 0; + siic->cmd_ptr = 0; + siic->finished_len = 0; + siic->last = (i == (num - 1)); + + ret = i2c_sirfsoc_xfer_msg(siic, &msgs[i]); + if (ret) { + clk_disable(siic->clk); + return ret; + } + } + + clk_disable(siic->clk); + return num; +} + +/* I2C algorithms associated with this master controller driver */ +static const struct i2c_algorithm i2c_sirfsoc_algo = { + .master_xfer = i2c_sirfsoc_xfer, + .functionality = i2c_sirfsoc_func, +}; + +static int __devinit i2c_sirfsoc_probe(struct platform_device *pdev) +{ + struct sirfsoc_i2c *siic; + struct i2c_adapter *adap; + struct resource *mem_res; + struct clk *clk; + int bitrate; + int ctrl_speed; + int irq; + + int err; + u32 regval; + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + dev_err(&pdev->dev, "Clock get failed\n"); + goto err_get_clk; + } + + err = clk_prepare(clk); + if (err) { + dev_err(&pdev->dev, "Clock prepare failed\n"); + goto err_clk_prep; + } + + err = clk_enable(clk); + if (err) { + dev_err(&pdev->dev, "Clock enable failed\n"); + goto err_clk_en; + } + + ctrl_speed = clk_get_rate(clk); + + siic = devm_kzalloc(&pdev->dev, sizeof(*siic), GFP_KERNEL); + if (!siic) { + dev_err(&pdev->dev, "Can't allocate driver data\n"); + err = -ENOMEM; + goto out; + } + adap = &siic->adapter; + adap->class = I2C_CLASS_HWMON; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem_res == NULL) { + dev_err(&pdev->dev, "Unable to get MEM resource\n"); + err = -EINVAL; + goto out; + } + + siic->base = devm_request_and_ioremap(&pdev->dev, mem_res); + if (siic->base == NULL) { + dev_err(&pdev->dev, "IO remap failed!\n"); + err = -ENOMEM; + goto out; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + err = irq; + goto out; + } + err = devm_request_irq(&pdev->dev, irq, i2c_sirfsoc_irq, 0, + dev_name(&pdev->dev), siic); + if (err) + goto out; + + adap->algo = &i2c_sirfsoc_algo; + adap->algo_data = siic; + + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + + strlcpy(adap->name, "sirfsoc-i2c", sizeof(adap->name)); + + platform_set_drvdata(pdev, adap); + init_completion(&siic->done); + + /* Controller Initalisation */ + + writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL); + while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET) + cpu_relax(); + writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE, + siic->base + SIRFSOC_I2C_CTRL); + + siic->clk = clk; + + err = of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &bitrate); + if (err < 0) + bitrate = SIRFSOC_I2C_DEFAULT_SPEED; + + if (bitrate < 100000) + regval = + (2 * ctrl_speed) / (2 * bitrate * 11); + else + regval = ctrl_speed / (bitrate * 5); + + writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL); + if (regval > 0xFF) + writel(0xFF, siic->base + SIRFSOC_I2C_SDA_DELAY); + else + writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY); + + err = i2c_add_numbered_adapter(adap); + if (err < 0) { + dev_err(&pdev->dev, "Can't add new i2c adapter\n"); + goto out; + } + + clk_disable(clk); + + dev_info(&pdev->dev, " I2C adapter ready to operate\n"); + + return 0; + +out: + clk_disable(clk); +err_clk_en: + clk_unprepare(clk); +err_clk_prep: + clk_put(clk); +err_get_clk: + return err; +} + +static int __devexit i2c_sirfsoc_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct sirfsoc_i2c *siic = adapter->algo_data; + + writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL); + i2c_del_adapter(adapter); + clk_unprepare(siic->clk); + clk_put(siic->clk); + return 0; +} + +#ifdef CONFIG_PM +static int i2c_sirfsoc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct sirfsoc_i2c *siic = adapter->algo_data; + + clk_enable(siic->clk); + siic->sda_delay = readl(siic->base + SIRFSOC_I2C_SDA_DELAY); + siic->clk_div = readl(siic->base + SIRFSOC_I2C_CLK_CTRL); + clk_disable(siic->clk); + return 0; +} + +static int i2c_sirfsoc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct sirfsoc_i2c *siic = adapter->algo_data; + + clk_enable(siic->clk); + writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL); + writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE, + siic->base + SIRFSOC_I2C_CTRL); + writel(siic->clk_div, siic->base + SIRFSOC_I2C_CLK_CTRL); + writel(siic->sda_delay, siic->base + SIRFSOC_I2C_SDA_DELAY); + clk_disable(siic->clk); + return 0; +} + +static const struct dev_pm_ops i2c_sirfsoc_pm_ops = { + .suspend = i2c_sirfsoc_suspend, + .resume = i2c_sirfsoc_resume, +}; +#endif + +static const struct of_device_id sirfsoc_i2c_of_match[] __devinitconst = { + { .compatible = "sirf,prima2-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match); + +static struct platform_driver i2c_sirfsoc_driver = { + .driver = { + .name = "sirfsoc_i2c", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &i2c_sirfsoc_pm_ops, +#endif + .of_match_table = sirfsoc_i2c_of_match, + }, + .probe = i2c_sirfsoc_probe, + .remove = __devexit_p(i2c_sirfsoc_remove), +}; +module_platform_driver(i2c_sirfsoc_driver); + +MODULE_DESCRIPTION("SiRF SoC I2C master controller driver"); +MODULE_AUTHOR("Zhiwu Song , " + "Xiangzhen Ye "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From d5dfcc91b179f40652318b9f3d76c31d2f908000 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 6 Mar 2012 21:04:32 -0800 Subject: arm: tegra: dts: Support host/device selection and legacy mode Some USB ports can support host and device operation. We add the dr_mode property (as found in Freescale) for this. One USB port has a 'legacy mode', left over from the days of pre-Tegra chips. I don't believe this is actually used, except that we must know to turn this off in the driver. Signed-off-by: Simon Glass Acked-by: Stephen Warren Signed-off-by: Olof Johansson --- Documentation/devicetree/bindings/usb/tegra-usb.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/tegra-usb.txt b/Documentation/devicetree/bindings/usb/tegra-usb.txt index 035d63d5646d..007005ddbe12 100644 --- a/Documentation/devicetree/bindings/usb/tegra-usb.txt +++ b/Documentation/devicetree/bindings/usb/tegra-usb.txt @@ -11,3 +11,16 @@ Required properties : - phy_type : Should be one of "ulpi" or "utmi". - nvidia,vbus-gpio : If present, specifies a gpio that needs to be activated for the bus to be powered. + +Optional properties: + - dr_mode : dual role mode. Indicates the working mode for + nvidia,tegra20-ehci compatible controllers. Can be "host", "peripheral", + or "otg". Default to "host" if not defined for backward compatibility. + host means this is a host controller + peripheral means it is device controller + otg means it can operate as either ("on the go") + - nvidia,has-legacy-mode : boolean indicates whether this controller can + operate in legacy mode (as APX 2500 / 2600). In legacy mode some + registers are accessed through the APB_MISC base address instead of + the USB controller. Since this is a legacy issue it probably does not + warrant a compatible string of its own. -- cgit v1.2.3 From d8e0364364d333feb4564bb7d7d983182b34427e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 10 Jan 2012 22:15:45 +0000 Subject: ARM: smp_twd: add device tree support Add bindings to support DT discovery of the ARM Timer Watchdog (aka TWD). Only the timer side is converted by this patch. Acked-by: Rob Herring Signed-off-by: Marc Zyngier --- Documentation/devicetree/bindings/arm/twd.txt | 48 +++++++++++++++++ arch/arm/include/asm/smp_twd.h | 8 +++ arch/arm/kernel/smp_twd.c | 77 ++++++++++++++++++++++----- 3 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/twd.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/twd.txt b/Documentation/devicetree/bindings/arm/twd.txt new file mode 100644 index 000000000000..75b8610939fa --- /dev/null +++ b/Documentation/devicetree/bindings/arm/twd.txt @@ -0,0 +1,48 @@ +* ARM Timer Watchdog + +ARM 11MP, Cortex-A5 and Cortex-A9 are often associated with a per-core +Timer-Watchdog (aka TWD), which provides both a per-cpu local timer +and watchdog. + +The TWD is usually attached to a GIC to deliver its two per-processor +interrupts. + +** Timer node required properties: + +- compatible : Should be one of: + "arm,cortex-a9-twd-timer" + "arm,cortex-a5-twd-timer" + "arm,arm11mp-twd-timer" + +- interrupts : One interrupt to each core + +- reg : Specify the base address and the size of the TWD timer + register window. + +Example: + + twd-timer@2c000600 { + compatible = "arm,arm11mp-twd-timer""; + reg = <0x2c000600 0x20>; + interrupts = <1 13 0xf01>; + }; + +** Watchdog node properties: + +- compatible : Should be one of: + "arm,cortex-a9-twd-wdt" + "arm,cortex-a5-twd-wdt" + "arm,arm11mp-twd-wdt" + +- interrupts : One interrupt to each core + +- reg : Specify the base address and the size of the TWD watchdog + register window. + +Example: + + twd-watchdog@2c000620 { + compatible = "arm,arm11mp-twd-wdt"; + reg = <0x2c000620 0x20>; + interrupts = <1 14 0xf01>; + }; diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h index 16c89b793f90..8047e6e580b6 100644 --- a/arch/arm/include/asm/smp_twd.h +++ b/arch/arm/include/asm/smp_twd.h @@ -40,4 +40,12 @@ struct twd_local_timer name __initdata = { \ int twd_local_timer_register(struct twd_local_timer *); +#ifdef CONFIG_HAVE_ARM_TWD +void twd_local_timer_of_register(void); +#else +static inline void twd_local_timer_of_register(void) +{ +} +#endif + #endif diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 18c55f1e4e48..761826c628b1 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -284,37 +286,86 @@ static struct local_timer_ops twd_lt_ops __cpuinitdata = { .stop = twd_timer_stop, }; -int __init twd_local_timer_register(struct twd_local_timer *tlt) +static int __init twd_local_timer_common_register(void) { int err; - if (twd_base || twd_evt) - return -EBUSY; - - twd_ppi = tlt->res[1].start; - twd_evt = alloc_percpu(struct clock_event_device *); - twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); - if (!twd_base || !twd_evt) { + if (!twd_evt) { err = -ENOMEM; - goto out; + goto out_free; } err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt); if (err) { pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err); - goto out; + goto out_free; } err = local_timer_register(&twd_lt_ops); if (err) - goto out; + goto out_irq; return 0; -out: +out_irq: + free_percpu_irq(twd_ppi, twd_evt); +out_free: iounmap(twd_base); + twd_base = NULL; free_percpu(twd_evt); - twd_base = twd_evt = NULL; + return err; } + +int __init twd_local_timer_register(struct twd_local_timer *tlt) +{ + if (twd_base || twd_evt) + return -EBUSY; + + twd_ppi = tlt->res[1].start; + + twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); + if (!twd_base) + return -ENOMEM; + + return twd_local_timer_common_register(); +} + +#ifdef CONFIG_OF +const static struct of_device_id twd_of_match[] __initconst = { + { .compatible = "arm,cortex-a9-twd-timer", }, + { .compatible = "arm,cortex-a5-twd-timer", }, + { .compatible = "arm,arm11mp-twd-timer", }, + { }, +}; + +void __init twd_local_timer_of_register(void) +{ + struct device_node *np; + int err; + + np = of_find_matching_node(NULL, twd_of_match); + if (!np) { + err = -ENODEV; + goto out; + } + + twd_ppi = irq_of_parse_and_map(np, 0); + if (!twd_ppi) { + err = -EINVAL; + goto out; + } + + twd_base = of_iomap(np, 0); + if (!twd_base) { + err = -ENOMEM; + goto out; + } + + err = twd_local_timer_common_register(); + +out: + WARN(err, "twd_local_timer_of_register failed (%d)\n", err); +} +#endif -- cgit v1.2.3 From 2cd36877ad1c61429e00c099b6903ebcd936ca00 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 13 Mar 2012 21:35:51 -0700 Subject: Input: of_keymap - add device tree bindings for simple key matrices This adds a simple device tree binding for simple key matrix data and a helper to fill in the platform data. Signed-off-by: Olof Johansson Acked-by: Stephen Warren Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/matrix-keymap.txt | 19 +++++ drivers/input/Kconfig | 4 + drivers/input/Makefile | 1 + drivers/input/keyboard/Kconfig | 1 + drivers/input/of_keymap.c | 87 ++++++++++++++++++++++ include/linux/input/matrix_keypad.h | 19 +++++ 6 files changed, 131 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/matrix-keymap.txt create mode 100644 drivers/input/of_keymap.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/input/matrix-keymap.txt b/Documentation/devicetree/bindings/input/matrix-keymap.txt new file mode 100644 index 000000000000..3cd8b98ccd2d --- /dev/null +++ b/Documentation/devicetree/bindings/input/matrix-keymap.txt @@ -0,0 +1,19 @@ +A simple common binding for matrix-connected key boards. Currently targeted at +defining the keys in the scope of linux key codes since that is a stable and +standardized interface at this time. + +Required properties: +- linux,keymap: an array of packed 1-cell entries containing the equivalent + of row, column and linux key-code. The 32-bit big endian cell is packed + as: + row << 24 | column << 16 | key-code + +Optional properties: +Some users of this binding might choose to specify secondary keymaps for +cases where there is a modifier key such as a Fn key. Proposed names +for said properties are "linux,fn-keymap" or with another descriptive +word for the modifier other from "Fn". + +Example: + linux,keymap = < 0x00030012 + 0x0102003a >; diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 001b147c7f95..332597980817 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,10 @@ config INPUT if INPUT +config INPUT_OF_MATRIX_KEYMAP + depends on USE_OF + bool + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0c789490e0b3..b173a13a73ca 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index cdc385b2cf7d..3371954c979b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -394,6 +394,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA + select INPUT_OF_MATRIX_KEYMAP if USE_OF help Say Y here if you want to use a matrix keyboard connected directly to the internal keyboard controller on Tegra SoCs. diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c new file mode 100644 index 000000000000..061493d57682 --- /dev/null +++ b/drivers/input/of_keymap.c @@ -0,0 +1,87 @@ +/* + * Helpers for open firmware matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include + +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index fe7c4b9ae270..6c07ced0af81 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -3,6 +3,7 @@ #include #include +#include #define MATRIX_MAX_ROWS 32 #define MATRIX_MAX_COLS 32 @@ -106,4 +107,22 @@ matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, __clear_bit(KEY_RESERVED, keybit); } +#ifdef CONFIG_INPUT_OF_MATRIX_KEYMAP +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd); +#else +static inline struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname) +{ + return NULL; +} + +static inline void +matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ +} +#endif + #endif /* _MATRIX_KEYPAD_H */ -- cgit v1.2.3 From 145e97348a3066cf71038d02392aa8063550de6f Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 13 Mar 2012 21:36:29 -0700 Subject: Input: tegra-kbc - revise device tree support This is an incremental patch updating to the revised bindings for matrix keyboards. This includes an optional "linux,fn-keymap" binding that is not yet implemented, that will be used to specify the Fn-key modifier layout if needed. Signed-off-by: Olof Johansson Acked-by: Grant Likely Acked-by: Stephen Warren Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/tegra-kbc.txt | 17 +++++++++++------ drivers/input/keyboard/tegra-kbc.c | 21 ++++++++++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/input/tegra-kbc.txt b/Documentation/devicetree/bindings/input/tegra-kbc.txt index 5ecfa99089b4..72683be6de35 100644 --- a/Documentation/devicetree/bindings/input/tegra-kbc.txt +++ b/Documentation/devicetree/bindings/input/tegra-kbc.txt @@ -3,16 +3,21 @@ Required properties: - compatible: "nvidia,tegra20-kbc" -Optional properties: -- debounce-delay: delay in milliseconds per row scan for debouncing -- repeat-delay: delay in milliseconds before repeat starts -- ghost-filter: enable ghost filtering for this device -- wakeup-source: configure keyboard as a wakeup source for suspend/resume +Optional properties, in addition to those specified by the shared +matrix-keyboard bindings: + +- linux,fn-keymap: a second keymap, same specification as the + matrix-keyboard-controller spec but to be used when the KEY_FN modifier + key is pressed. +- nvidia,debounce-delay-ms: delay in milliseconds per row scan for debouncing +- nvidia,repeat-delay-ms: delay in milliseconds before repeat starts +- nvidia,ghost-filter: enable ghost filtering for this device +- nvidia,wakeup-source: configure keyboard as a wakeup source for suspend/resume Example: keyboard: keyboard { compatible = "nvidia,tegra20-kbc"; reg = <0x7000e200 0x100>; - ghost-filter; + nvidia,ghost-filter; }; diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index dc19432c665b..21c42f852343 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -624,6 +624,8 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) { struct tegra_kbc_platform_data *pdata; struct device_node *np = pdev->dev.of_node; + u32 prop; + int i; if (!np) return NULL; @@ -631,16 +633,16 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) if (!pdata) return NULL; - if (!of_property_read_u32(np, "debounce-delay", &prop)) + if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) pdata->debounce_cnt = prop; - if (!of_property_read_u32(np, "repeat-delay", &prop)) + if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) pdata->repeat_cnt = prop; - if (of_find_property(np, "needs-ghost-filter", NULL)) + if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) pdata->use_ghost_filter = true; - if (of_find_property(np, "wakeup-source", NULL)) + if (of_find_property(np, "nvidia,wakeup-source", NULL)) pdata->wakeup = true; /* @@ -657,6 +659,10 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; } + pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap"); + + /* FIXME: Add handling of linux,fn-keymap here */ + return pdata; } #else @@ -792,6 +798,9 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, pdata->wakeup); + if (!pdev->dev.platform_data) + matrix_keyboard_of_free_keymap(pdata->keymap_data); + return 0; err_free_irq: @@ -806,8 +815,10 @@ err_free_mem: input_free_device(input_dev); kfree(kbc); err_free_pdata: - if (!pdev->dev.platform_data) + if (!pdev->dev.platform_data) { + matrix_keyboard_of_free_keymap(pdata->keymap_data); kfree(pdata); + } return err; } -- cgit v1.2.3 From 770d7c39af940da24dd4c2c048576d778ac0abd4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 28 Jan 2012 12:12:36 +0800 Subject: of/mtd/nand: add generic bindings and helpers - nand-ecc-mode : String, operation mode of the NAND ecc mode. Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", "soft_bch". - nand-bus-width : 8 or 16 bus width if not present 8 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Grant Likely Acked-by: Stefan Roese --- Documentation/devicetree/bindings/mtd/nand.txt | 7 +++ drivers/of/Kconfig | 4 ++ drivers/of/Makefile | 1 + drivers/of/of_mtd.c | 85 ++++++++++++++++++++++++++ include/linux/of_mtd.h | 19 ++++++ 5 files changed, 116 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/nand.txt create mode 100644 drivers/of/of_mtd.c create mode 100644 include/linux/of_mtd.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt new file mode 100644 index 000000000000..03855c8c492a --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -0,0 +1,7 @@ +* MTD generic binding + +- nand-ecc-mode : String, operation mode of the NAND ecc mode. + Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", + "soft_bch". +- nand-bus-width : 8 or 16 bus width if not present 8 +- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 268163dd71c7..fa666a93540c 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -90,4 +90,8 @@ config OF_PCI_IRQ help OpenFirmware PCI IRQ routing helpers +config OF_MTD + depends on MTD + def_bool y + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index a73f5a51ff4c..aa90e602c8a7 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_OF_SELFTEST) += selftest.o obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o +obj-$(CONFIG_OF_MTD) += of_mtd.o diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c new file mode 100644 index 000000000000..e7cad627a5d1 --- /dev/null +++ b/drivers/of/of_mtd.c @@ -0,0 +1,85 @@ +/* + * Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD + * + * OF helpers for mtd. + * + * This file is released under the GPLv2 + * + */ +#include +#include +#include +#include + +/** + * It maps 'enum nand_ecc_modes_t' found in include/linux/mtd/nand.h + * into the device tree binding of 'nand-ecc', so that MTD + * device driver can get nand ecc from device tree. + */ +static const char *nand_ecc_modes[] = { + [NAND_ECC_NONE] = "none", + [NAND_ECC_SOFT] = "soft", + [NAND_ECC_HW] = "hw", + [NAND_ECC_HW_SYNDROME] = "hw_syndrome", + [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", + [NAND_ECC_SOFT_BCH] = "soft_bch", +}; + +/** + * of_get_nand_ecc_mode - Get nand ecc mode for given device_node + * @np: Pointer to the given device_node + * + * The function gets ecc mode string from property 'nand-ecc-mode', + * and return its index in nand_ecc_modes table, or errno in error case. + */ +const int of_get_nand_ecc_mode(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "nand-ecc-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++) + if (!strcasecmp(pm, nand_ecc_modes[i])) + return i; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode); + +/** + * of_get_nand_bus_width - Get nand bus witdh for given device_node + * @np: Pointer to the given device_node + * + * return bus width option, or errno in error case. + */ +int of_get_nand_bus_width(struct device_node *np) +{ + u32 val; + + if (of_property_read_u32(np, "nand-bus-width", &val)) + return 8; + + switch(val) { + case 8: + case 16: + return val; + default: + return -EIO; + } +} +EXPORT_SYMBOL_GPL(of_get_nand_bus_width); + +/** + * of_get_nand_on_flash_bbt - Get nand on flash bbt for given device_node + * @np: Pointer to the given device_node + * + * return true if present false other wise + */ +bool of_get_nand_on_flash_bbt(struct device_node *np) +{ + return of_property_read_bool(np, "nand-on-flash-bbt"); +} +EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt); diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h new file mode 100644 index 000000000000..bae1b6094c63 --- /dev/null +++ b/include/linux/of_mtd.h @@ -0,0 +1,19 @@ +/* + * Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD + * + * OF helpers for mtd. + * + * This file is released under the GPLv2 + */ + +#ifndef __LINUX_OF_MTD_H +#define __LINUX_OF_NET_H + +#ifdef CONFIG_OF_MTD +#include +extern const int of_get_nand_ecc_mode(struct device_node *np); +int of_get_nand_bus_width(struct device_node *np); +bool of_get_nand_on_flash_bbt(struct device_node *np); +#endif + +#endif /* __LINUX_OF_MTD_H */ -- cgit v1.2.3 From d6a016616ba834b7da7653effb98d413acde7aa2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 26 Jan 2012 02:11:06 +0800 Subject: atmel/nand: add DT support Use a local copy of board informatin and fill with DT data. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Grant Likely Cc: Nicolas Ferre --- .../devicetree/bindings/mtd/atmel-nand.txt | 41 ++++++++ arch/arm/boot/dts/at91sam9g20.dtsi | 16 +++ arch/arm/boot/dts/at91sam9g45.dtsi | 16 +++ arch/arm/boot/dts/at91sam9m10g45ek.dts | 25 ++++- arch/arm/boot/dts/usb_a9g20.dts | 44 +++++++- arch/arm/mach-at91/at91sam9x5.c | 5 - arch/arm/mach-at91/board-dt.c | 51 ---------- drivers/mtd/nand/atmel_nand.c | 113 +++++++++++++++++---- 8 files changed, 233 insertions(+), 78 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/atmel-nand.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt new file mode 100644 index 000000000000..5903ecf6e895 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -0,0 +1,41 @@ +Atmel NAND flash + +Required properties: +- compatible : "atmel,at91rm9200-nand". +- reg : should specify localbus address and size used for the chip, + and if availlable the ECC. +- atmel,nand-addr-offset : offset for the address latch. +- atmel,nand-cmd-offset : offset for the command latch. +- #address-cells, #size-cells : Must be present if the device has sub-nodes + representing partitions. + +- gpios : specifies the gpio pins to control the NAND device. detect is an + optional gpio and may be set to 0 if not present. + +Optional properties: +- nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default. + Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", + "soft_bch". +- nand-bus-width : 8 or 16 bus width if not present 8 +- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false + +Examples: +nand0: nand@40000000,0 { + compatible = "atmel,at91rm9200-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x40000000 0x10000000 + 0xffffe800 0x200 + >; + atmel,nand-addr-offset = <21>; + atmel,nand-cmd-offset = <22>; + nand-on-flash-bbt; + nand-ecc-mode = "soft"; + gpios = <&pioC 13 0 + &pioC 14 0 + 0 + >; + partition@0 { + ... + }; +}; diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index a100db03ec90..4b0dc99b9319 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -172,5 +172,21 @@ status = "disabled"; }; }; + + nand0: nand@40000000 { + compatible = "atmel,at91rm9200-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x40000000 0x10000000 + 0xffffe800 0x200 + >; + atmel,nand-addr-offset = <21>; + atmel,nand-cmd-offset = <22>; + gpios = <&pioC 13 0 + &pioC 14 0 + 0 + >; + status = "disabled"; + }; }; }; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index f779667159b1..d79021b831c4 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -180,5 +180,21 @@ status = "disabled"; }; }; + + nand0: nand@40000000 { + compatible = "atmel,at91rm9200-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x40000000 0x10000000 + 0xffffe200 0x200 + >; + atmel,nand-addr-offset = <21>; + atmel,nand-cmd-offset = <22>; + gpios = <&pioC 8 0 + &pioC 14 0 + 0 + >; + status = "disabled"; + }; }; }; diff --git a/arch/arm/boot/dts/at91sam9m10g45ek.dts b/arch/arm/boot/dts/at91sam9m10g45ek.dts index 15e25f903cad..fd4531135431 100644 --- a/arch/arm/boot/dts/at91sam9m10g45ek.dts +++ b/arch/arm/boot/dts/at91sam9m10g45ek.dts @@ -14,7 +14,7 @@ compatible = "atmel,at91sam9m10g45ek", "atmel,at91sam9g45", "atmel,at91sam9"; chosen { - bootargs = "mem=64M console=ttyS0,115200 mtdparts=atmel_nand:4M(bootstrap/uboot/kernel)ro,60M(rootfs),-(data) root=/dev/mtdblock1 rw rootfstype=jffs2"; + bootargs = "mem=64M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2"; }; memory@70000000 { @@ -36,6 +36,29 @@ status = "okay"; }; }; + + nand0: nand@40000000 { + nand-bus-width = <8>; + nand-ecc-mode = "soft"; + nand-on-flash-bbt; + status = "okay"; + + boot@0 { + label = "bootstrap/uboot/kernel"; + reg = <0x0 0x400000>; + }; + + rootfs@400000 { + label = "rootfs"; + reg = <0x400000 0x3C00000>; + }; + + data@4000000 { + label = "data"; + reg = <0x4000000 0xC000000>; + }; + + }; }; leds { diff --git a/arch/arm/boot/dts/usb_a9g20.dts b/arch/arm/boot/dts/usb_a9g20.dts index d74545a2a77c..71d83ef316df 100644 --- a/arch/arm/boot/dts/usb_a9g20.dts +++ b/arch/arm/boot/dts/usb_a9g20.dts @@ -13,7 +13,7 @@ compatible = "calao,usb-a9g20", "atmel,at91sam9g20", "atmel,at91sam9"; chosen { - bootargs = "mem=64M console=ttyS0,115200 mtdparts=atmel_nand:128k(at91bootstrap),256k(barebox)ro,128k(bareboxenv),128k(bareboxenv2),4M(kernel),120M(rootfs),-(data) root=/dev/mtdblock5 rw rootfstype=ubifs"; + bootargs = "mem=64M console=ttyS0,115200 root=/dev/mtdblock5 rw rootfstype=ubifs"; }; memory@20000000 { @@ -31,6 +31,48 @@ status = "okay"; }; }; + + nand0: nand@40000000 { + nand-bus-width = <8>; + nand-ecc-mode = "soft"; + nand-on-flash-bbt; + status = "okay"; + + at91bootstrap@0 { + label = "at91bootstrap"; + reg = <0x0 0x20000>; + }; + + barebox@20000 { + label = "barebox"; + reg = <0x20000 0x40000>; + }; + + bareboxenv@60000 { + label = "bareboxenv"; + reg = <0x60000 0x20000>; + }; + + bareboxenv2@80000 { + label = "bareboxenv2"; + reg = <0x80000 0x20000>; + }; + + kernel@a0000 { + label = "kernel"; + reg = <0xa0000 0x400000>; + }; + + rootfs@4a0000 { + label = "rootfs"; + reg = <0x4a0000 0x7800000>; + }; + + data@7ca0000 { + label = "data"; + reg = <0x7ca0000 0x8360000>; + }; + }; }; leds { diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index a34d96afa746..7bec5a40d01a 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -313,11 +313,6 @@ void __init at91sam9x5_initialize(void) at91_gpio_init(NULL, 0); } -/* -------------------------------------------------------------------- - * AT91SAM9x5 devices (temporary before modification of code) - * -------------------------------------------------------------------- */ -void __init at91_add_device_nand(struct atmel_nand_data *data) {} - /* -------------------------------------------------------------------- * Interrupt initialization * -------------------------------------------------------------------- */ diff --git a/arch/arm/mach-at91/board-dt.c b/arch/arm/mach-at91/board-dt.c index 2eb294b2cc0e..9f729d6c8942 100644 --- a/arch/arm/mach-at91/board-dt.c +++ b/arch/arm/mach-at91/board-dt.c @@ -19,10 +19,7 @@ #include #include -#include #include -#include -#include #include #include @@ -30,7 +27,6 @@ #include #include -#include "sam9_smc.h" #include "generic.h" @@ -40,50 +36,6 @@ static void __init ek_init_early(void) at91_initialize(12000000); } -/* det_pin is not connected */ -static struct atmel_nand_data __initdata ek_nand_data = { - .ale = 21, - .cle = 22, - .det_pin = -EINVAL, - .rdy_pin = AT91_PIN_PC8, - .enable_pin = AT91_PIN_PC14, - .ecc_mode = NAND_ECC_SOFT, - .on_flash_bbt = 1, -}; - -static struct sam9_smc_config __initdata ek_nand_smc_config = { - .ncs_read_setup = 0, - .nrd_setup = 2, - .ncs_write_setup = 0, - .nwe_setup = 2, - - .ncs_read_pulse = 4, - .nrd_pulse = 4, - .ncs_write_pulse = 4, - .nwe_pulse = 4, - - .read_cycle = 7, - .write_cycle = 7, - - .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE, - .tdf_cycles = 3, -}; - -static void __init ek_add_device_nand(void) -{ - ek_nand_data.bus_width_16 = board_have_nand_16bit(); - /* setup bus-width (8 or 16) */ - if (ek_nand_data.bus_width_16) - ek_nand_smc_config.mode |= AT91_SMC_DBW_16; - else - ek_nand_smc_config.mode |= AT91_SMC_DBW_8; - - /* configure chip-select 3 (NAND) */ - sam9_smc_configure(0, 3, &ek_nand_smc_config); - - at91_add_device_nand(&ek_nand_data); -} - static const struct of_device_id irq_of_match[] __initconst = { { .compatible = "atmel,at91rm9200-aic", .data = at91_aic_of_init }, @@ -100,9 +52,6 @@ static void __init at91_dt_init_irq(void) static void __init at91_dt_device_init(void) { of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - - /* NAND */ - ek_add_device_nand(); } static const char *at91_dt_board_compat[] __initdata = { diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 045d174b8277..ae7e37d9ac17 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -83,7 +87,7 @@ struct atmel_nand_host { struct mtd_info mtd; void __iomem *io_base; dma_addr_t io_phys; - struct atmel_nand_data *board; + struct atmel_nand_data board; struct device *dev; void __iomem *ecc; @@ -101,8 +105,8 @@ static int cpu_has_dma(void) */ static void atmel_nand_enable(struct atmel_nand_host *host) { - if (gpio_is_valid(host->board->enable_pin)) - gpio_set_value(host->board->enable_pin, 0); + if (gpio_is_valid(host->board.enable_pin)) + gpio_set_value(host->board.enable_pin, 0); } /* @@ -110,8 +114,8 @@ static void atmel_nand_enable(struct atmel_nand_host *host) */ static void atmel_nand_disable(struct atmel_nand_host *host) { - if (gpio_is_valid(host->board->enable_pin)) - gpio_set_value(host->board->enable_pin, 1); + if (gpio_is_valid(host->board.enable_pin)) + gpio_set_value(host->board.enable_pin, 1); } /* @@ -132,9 +136,9 @@ static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl return; if (ctrl & NAND_CLE) - writeb(cmd, host->io_base + (1 << host->board->cle)); + writeb(cmd, host->io_base + (1 << host->board.cle)); else - writeb(cmd, host->io_base + (1 << host->board->ale)); + writeb(cmd, host->io_base + (1 << host->board.ale)); } /* @@ -145,8 +149,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) struct nand_chip *nand_chip = mtd->priv; struct atmel_nand_host *host = nand_chip->priv; - return gpio_get_value(host->board->rdy_pin) ^ - !!host->board->rdy_pin_active_low; + return gpio_get_value(host->board.rdy_pin) ^ + !!host->board.rdy_pin_active_low; } /* @@ -261,7 +265,7 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) return; - if (host->board->bus_width_16) + if (host->board.bus_width_16) atmel_read_buf16(mtd, buf, len); else atmel_read_buf8(mtd, buf, len); @@ -277,7 +281,7 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) return; - if (host->board->bus_width_16) + if (host->board.bus_width_16) atmel_write_buf16(mtd, buf, len); else atmel_write_buf8(mtd, buf, len); @@ -469,6 +473,56 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) } } +#if defined(CONFIG_OF) +static int __devinit atmel_of_init_port(struct atmel_nand_host *host, + struct device_node *np) +{ + u32 val; + int ecc_mode; + struct atmel_nand_data *board = &host->board; + enum of_gpio_flags flags; + + if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid addr-offset %u\n", val); + return -EINVAL; + } + board->ale = val; + } + + if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid cmd-offset %u\n", val); + return -EINVAL; + } + board->cle = val; + } + + ecc_mode = of_get_nand_ecc_mode(np); + + board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode; + + board->on_flash_bbt = of_get_nand_on_flash_bbt(np); + + if (of_get_nand_bus_width(np) == 16) + board->bus_width_16 = 1; + + board->rdy_pin = of_get_gpio_flags(np, 0, &flags); + board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW); + + board->enable_pin = of_get_gpio(np, 1); + board->det_pin = of_get_gpio(np, 2); + + return 0; +} +#else +static int __devinit atmel_of_init_port(struct atmel_nand_host *host, + struct device_node *np) +{ + return -EINVAL; +} +#endif + /* * Probe for the NAND device. */ @@ -479,6 +533,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) struct nand_chip *nand_chip; struct resource *regs; struct resource *mem; + struct mtd_part_parser_data ppdata = {}; int res; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -505,8 +560,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) mtd = &host->mtd; nand_chip = &host->nand_chip; - host->board = pdev->dev.platform_data; host->dev = &pdev->dev; + if (pdev->dev.of_node) { + res = atmel_of_init_port(host, pdev->dev.of_node); + if (res) + goto err_nand_ioremap; + } else { + memcpy(&host->board, pdev->dev.platform_data, + sizeof(struct atmel_nand_data)); + } nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; @@ -517,10 +579,10 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->IO_ADDR_W = host->io_base; nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; - if (gpio_is_valid(host->board->rdy_pin)) + if (gpio_is_valid(host->board.rdy_pin)) nand_chip->dev_ready = atmel_nand_device_ready; - nand_chip->ecc.mode = host->board->ecc_mode; + nand_chip->ecc.mode = host->board.ecc_mode; regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!regs && nand_chip->ecc.mode == NAND_ECC_HW) { @@ -545,7 +607,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->chip_delay = 20; /* 20us command delay time */ - if (host->board->bus_width_16) /* 16-bit bus width */ + if (host->board.bus_width_16) /* 16-bit bus width */ nand_chip->options |= NAND_BUSWIDTH_16; nand_chip->read_buf = atmel_read_buf; @@ -554,15 +616,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); atmel_nand_enable(host); - if (gpio_is_valid(host->board->det_pin)) { - if (gpio_get_value(host->board->det_pin)) { + if (gpio_is_valid(host->board.det_pin)) { + if (gpio_get_value(host->board.det_pin)) { printk(KERN_INFO "No SmartMedia card inserted.\n"); res = -ENXIO; goto err_no_card; } } - if (host->board->on_flash_bbt || on_flash_bbt) { + if (host->board.on_flash_bbt || on_flash_bbt) { printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); nand_chip->bbt_options |= NAND_BBT_USE_FLASH; } @@ -637,8 +699,9 @@ static int __init atmel_nand_probe(struct platform_device *pdev) } mtd->name = "atmel_nand"; - res = mtd_device_parse_register(mtd, NULL, 0, - host->board->parts, host->board->num_parts); + ppdata.of_node = pdev->dev.of_node; + res = mtd_device_parse_register(mtd, NULL, &ppdata, + host->board.parts, host->board.num_parts); if (!res) return res; @@ -682,11 +745,21 @@ static int __exit atmel_nand_remove(struct platform_device *pdev) return 0; } +#if defined(CONFIG_OF) +static const struct of_device_id atmel_nand_dt_ids[] = { + { .compatible = "atmel,at91rm9200-nand" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids); +#endif + static struct platform_driver atmel_nand_driver = { .remove = __exit_p(atmel_nand_remove), .driver = { .name = "atmel_nand", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_nand_dt_ids), }, }; -- cgit v1.2.3 From 8ffaa0f40db22564efc44588a9d861d78a1fae02 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 5 Feb 2012 18:22:34 +0800 Subject: i2c/gpio: add DT support To achieve DT support, we need to populate a custom platform_data in a private struct from DT information. To simplify code, the adapter and algorithm are also put into the private struct. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Grant Likely Acked-by: Rob Herring Acked-by: Wolfram Sang Cc: Nicolas Ferre --- .../devicetree/bindings/gpio/gpio_i2c.txt | 32 +++++++ drivers/i2c/busses/i2c-gpio.c | 98 +++++++++++++++++----- 2 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio_i2c.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio_i2c.txt b/Documentation/devicetree/bindings/gpio/gpio_i2c.txt new file mode 100644 index 000000000000..4f8ec947c6bd --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio_i2c.txt @@ -0,0 +1,32 @@ +Device-Tree bindings for i2c gpio driver + +Required properties: + - compatible = "i2c-gpio"; + - gpios: sda and scl gpio + + +Optional properties: + - i2c-gpio,sda-open-drain: sda as open drain + - i2c-gpio,scl-open-drain: scl as open drain + - i2c-gpio,scl-output-only: scl as output only + - i2c-gpio,delay-us: delay between GPIO operations (may depend on each platform) + - i2c-gpio,timeout-ms: timeout to get data + +Example nodes: + +i2c@0 { + compatible = "i2c-gpio"; + gpios = <&pioA 23 0 /* sda */ + &pioA 24 0 /* scl */ + >; + i2c-gpio,sda-open-drain; + i2c-gpio,scl-open-drain; + i2c-gpio,delay-us = <2>; /* ~100 kHz */ + #address-cells = <1>; + #size-cells = <0>; + + rv3029c2@56 { + compatible = "rv3029c2"; + reg = <0x56>; + }; +}; diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index a651779d9ff7..c0330a41db03 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -14,8 +14,15 @@ #include #include #include - -#include +#include +#include +#include + +struct i2c_gpio_private_data { + struct i2c_adapter adap; + struct i2c_algo_bit_data bit_data; + struct i2c_gpio_platform_data pdata; +}; /* Toggle SDA by changing the direction of the pin */ static void i2c_gpio_setsda_dir(void *data, int state) @@ -78,24 +85,62 @@ static int i2c_gpio_getscl(void *data) return gpio_get_value(pdata->scl_pin); } +static int __devinit of_i2c_gpio_probe(struct device_node *np, + struct i2c_gpio_platform_data *pdata) +{ + u32 reg; + + if (of_gpio_count(np) < 2) + return -ENODEV; + + pdata->sda_pin = of_get_gpio(np, 0); + pdata->scl_pin = of_get_gpio(np, 1); + + if (!gpio_is_valid(pdata->sda_pin) || !gpio_is_valid(pdata->scl_pin)) { + pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n", + np->full_name, pdata->sda_pin, pdata->scl_pin); + return -ENODEV; + } + + of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay); + + if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", ®)) + pdata->timeout = msecs_to_jiffies(reg); + + pdata->sda_is_open_drain = + of_property_read_bool(np, "i2c-gpio,sda-open-drain"); + pdata->scl_is_open_drain = + of_property_read_bool(np, "i2c-gpio,scl-open-drain"); + pdata->scl_is_output_only = + of_property_read_bool(np, "i2c-gpio,scl-output-only"); + + return 0; +} + static int __devinit i2c_gpio_probe(struct platform_device *pdev) { + struct i2c_gpio_private_data *priv; struct i2c_gpio_platform_data *pdata; struct i2c_algo_bit_data *bit_data; struct i2c_adapter *adap; int ret; - pdata = pdev->dev.platform_data; - if (!pdata) - return -ENXIO; - - ret = -ENOMEM; - adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); - if (!adap) - goto err_alloc_adap; - bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL); - if (!bit_data) - goto err_alloc_bit_data; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + adap = &priv->adap; + bit_data = &priv->bit_data; + pdata = &priv->pdata; + + if (pdev->dev.of_node) { + ret = of_i2c_gpio_probe(pdev->dev.of_node, pdata); + if (ret) + return ret; + } else { + if (!pdev->dev.platform_data) + return -ENXIO; + memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); + } ret = gpio_request(pdata->sda_pin, "sda"); if (ret) @@ -143,6 +188,7 @@ static int __devinit i2c_gpio_probe(struct platform_device *pdev) adap->algo_data = bit_data; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; /* * If "dev->id" is negative we consider it as zero. @@ -154,7 +200,9 @@ static int __devinit i2c_gpio_probe(struct platform_device *pdev) if (ret) goto err_add_bus; - platform_set_drvdata(pdev, adap); + of_i2c_register_devices(adap); + + platform_set_drvdata(pdev, priv); dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n", pdata->sda_pin, pdata->scl_pin, @@ -168,34 +216,40 @@ err_add_bus: err_request_scl: gpio_free(pdata->sda_pin); err_request_sda: - kfree(bit_data); -err_alloc_bit_data: - kfree(adap); -err_alloc_adap: return ret; } static int __devexit i2c_gpio_remove(struct platform_device *pdev) { + struct i2c_gpio_private_data *priv; struct i2c_gpio_platform_data *pdata; struct i2c_adapter *adap; - adap = platform_get_drvdata(pdev); - pdata = pdev->dev.platform_data; + priv = platform_get_drvdata(pdev); + adap = &priv->adap; + pdata = &priv->pdata; i2c_del_adapter(adap); gpio_free(pdata->scl_pin); gpio_free(pdata->sda_pin); - kfree(adap->algo_data); - kfree(adap); return 0; } +#if defined(CONFIG_OF) +static const struct of_device_id i2c_gpio_dt_ids[] = { + { .compatible = "i2c-gpio", }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids); +#endif + static struct platform_driver i2c_gpio_driver = { .driver = { .name = "i2c-gpio", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_gpio_dt_ids), }, .probe = i2c_gpio_probe, .remove = __devexit_p(i2c_gpio_remove), -- cgit v1.2.3 From eb5e76ffd4e626655944e99bb85b07e17172620d Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 2 Mar 2012 20:44:23 +0800 Subject: ARM: at91: add pmc DT support Specified the main Oscillator via clock binding. This will allow to do not hardcode it anymore in the DT board at 12MHz. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Rob Herring Acked-by: Nicolas Ferre --- .../devicetree/bindings/arm/atmel-pmc.txt | 11 +++++ arch/arm/boot/dts/at91sam9g20.dtsi | 5 ++ arch/arm/boot/dts/at91sam9g45.dtsi | 5 ++ arch/arm/boot/dts/at91sam9m10g45ek.dts | 11 +++++ arch/arm/boot/dts/at91sam9x5.dtsi | 5 ++ arch/arm/boot/dts/at91sam9x5cm.dtsi | 11 +++++ arch/arm/boot/dts/usb_a9g20.dts | 11 +++++ arch/arm/mach-at91/clock.c | 56 ++++++++++++++++++++-- arch/arm/mach-at91/generic.h | 1 + arch/arm/mach-at91/setup.c | 3 +- 10 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/atmel-pmc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-pmc.txt b/Documentation/devicetree/bindings/arm/atmel-pmc.txt new file mode 100644 index 000000000000..389bed5056e8 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/atmel-pmc.txt @@ -0,0 +1,11 @@ +* Power Management Controller (PMC) + +Required properties: +- compatible: Should be "atmel,at91rm9200-pmc" +- reg: Should contain PMC registers location and length + +Examples: + pmc: pmc@fffffc00 { + compatible = "atmel,at91rm9200-pmc"; + reg = <0xfffffc00 0x100>; + }; diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index a885a30d1c82..dd5d114a0e1d 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -59,6 +59,11 @@ reg = <0xfffff000 0x200>; }; + pmc: pmc@fffffc00 { + compatible = "atmel,at91rm9200-pmc"; + reg = <0xfffffc00 0x100>; + }; + pit: timer@fffffd30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffd30 0xf>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 92fe5a5c0eee..621a329307d6 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -60,6 +60,11 @@ reg = <0xfffff000 0x200>; }; + pmc: pmc@fffffc00 { + compatible = "atmel,at91rm9200-pmc"; + reg = <0xfffffc00 0x100>; + }; + pit: timer@fffffd30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffd30 0xf>; diff --git a/arch/arm/boot/dts/at91sam9m10g45ek.dts b/arch/arm/boot/dts/at91sam9m10g45ek.dts index fd4531135431..a8958241f1d7 100644 --- a/arch/arm/boot/dts/at91sam9m10g45ek.dts +++ b/arch/arm/boot/dts/at91sam9m10g45ek.dts @@ -21,6 +21,17 @@ reg = <0x70000000 0x4000000>; }; + clocks { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + main_clock: clock@0 { + compatible = "atmel,osc", "fixed-clock"; + clock-frequency = <12000000>; + }; + }; + ahb { apb { dbgu: serial@ffffee00 { diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index f0104f4e6ab4..3855843fc038 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -58,6 +58,11 @@ reg = <0xfffff000 0x200>; }; + pmc: pmc@fffffc00 { + compatible = "atmel,at91rm9200-pmc"; + reg = <0xfffffc00 0x100>; + }; + pit: timer@fffffe30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffe30 0xf>; diff --git a/arch/arm/boot/dts/at91sam9x5cm.dtsi b/arch/arm/boot/dts/at91sam9x5cm.dtsi index 5b37033bed56..67936f83c694 100644 --- a/arch/arm/boot/dts/at91sam9x5cm.dtsi +++ b/arch/arm/boot/dts/at91sam9x5cm.dtsi @@ -12,6 +12,17 @@ reg = <0x20000000 0x8000000>; }; + clocks { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + main_clock: clock@0 { + compatible = "atmel,osc", "fixed-clock"; + clock-frequency = <12000000>; + }; + }; + ahb { nand0: nand@40000000 { nand-bus-width = <8>; diff --git a/arch/arm/boot/dts/usb_a9g20.dts b/arch/arm/boot/dts/usb_a9g20.dts index 0ea90b5be512..73f1dc48f307 100644 --- a/arch/arm/boot/dts/usb_a9g20.dts +++ b/arch/arm/boot/dts/usb_a9g20.dts @@ -20,6 +20,17 @@ reg = <0x20000000 0x4000000>; }; + clocks { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + main_clock: clock@0 { + compatible = "atmel,osc", "fixed-clock"; + clock-frequency = <12000000>; + }; + }; + ahb { apb { dbgu: serial@fffff200 { diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c index be51ca7f694d..a0f4d7424cdc 100644 --- a/arch/arm/mach-at91/clock.c +++ b/arch/arm/mach-at91/clock.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -671,16 +672,12 @@ static void __init at91_upll_usbfs_clock_init(unsigned long main_clock) uhpck.rate_hz /= 1 + ((at91_pmc_read(AT91_PMC_USB) & AT91_PMC_OHCIUSBDIV) >> 8); } -int __init at91_clock_init(unsigned long main_clock) +static int __init at91_pmc_init(unsigned long main_clock) { unsigned tmp, freq, mckr; int i; int pll_overclock = false; - at91_pmc_base = ioremap(AT91_PMC, 256); - if (!at91_pmc_base) - panic("Impossible to ioremap AT91_PMC 0x%x\n", AT91_PMC); - /* * When the bootloader initialized the main oscillator correctly, * there's no problem using the cycle counter. But if it didn't, @@ -802,6 +799,55 @@ int __init at91_clock_init(unsigned long main_clock) return 0; } +#if defined(CONFIG_OF) +static struct of_device_id pmc_ids[] = { + { .compatible = "atmel,at91rm9200-pmc" }, + { /*sentinel*/ } +}; + +static struct of_device_id osc_ids[] = { + { .compatible = "atmel,osc" }, + { /*sentinel*/ } +}; + +int __init at91_dt_clock_init(void) +{ + struct device_node *np; + u32 main_clock = 0; + + np = of_find_matching_node(NULL, pmc_ids); + if (!np) + panic("unable to find compatible pmc node in dtb\n"); + + at91_pmc_base = of_iomap(np, 0); + if (!at91_pmc_base) + panic("unable to map pmc cpu registers\n"); + + of_node_put(np); + + /* retrieve the freqency of fixed clocks from device tree */ + np = of_find_matching_node(NULL, osc_ids); + if (np) { + u32 rate; + if (!of_property_read_u32(np, "clock-frequency", &rate)) + main_clock = rate; + } + + of_node_put(np); + + return at91_pmc_init(main_clock); +} +#endif + +int __init at91_clock_init(unsigned long main_clock) +{ + at91_pmc_base = ioremap(AT91_PMC, 256); + if (!at91_pmc_base) + panic("Impossible to ioremap AT91_PMC 0x%x\n", AT91_PMC); + + return at91_pmc_init(main_clock); +} + /* * Several unused clocks may be active. Turn them off. */ diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index d5f5083880f1..dd9b346c451d 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -53,6 +53,7 @@ extern void __init at91sam9rl_set_console_clock(int id); extern void __init at91sam9g45_set_console_clock(int id); #ifdef CONFIG_AT91_PMC_UNIT extern int __init at91_clock_init(unsigned long main_clock); +extern int __init at91_dt_clock_init(void); #else static int inline at91_clock_init(unsigned long main_clock) { return 0; } #endif diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index c0bd5a625695..d7abc25f6c64 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -292,9 +292,8 @@ void __init at91_dt_initialize(void) /* temporary until have the ramc binding*/ at91_boot_soc.ioremap_registers(); - /* temporary until have the pmc binding */ /* Init clock subsystem */ - at91_clock_init(12000000); + at91_dt_clock_init(); /* Register the processor-specific clocks */ at91_boot_soc.register_clocks(); -- cgit v1.2.3 From c8082d344ac4c05932fec1766e5e9ce72cf286ed Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 3 Mar 2012 03:16:27 +0800 Subject: ARM: at91: add RSTC (Reset Controller) dt support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Rob Herring Acked-by: Nicolas Ferre --- .../devicetree/bindings/arm/atmel-at91.txt | 12 +++++++++ arch/arm/boot/dts/at91sam9g20.dtsi | 5 ++++ arch/arm/boot/dts/at91sam9g45.dtsi | 5 ++++ arch/arm/boot/dts/at91sam9x5.dtsi | 5 ++++ arch/arm/mach-at91/at91sam9x5.c | 1 - arch/arm/mach-at91/setup.c | 30 ++++++++++++++++++++++ 6 files changed, 57 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 1aeaf6f2a1ba..a64f86717b5d 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -30,3 +30,15 @@ One interrupt per TC channel in a TC block: reg = <0xfffdc000 0x100>; interrupts = <26 4 27 4 28 4>; }; + +RSTC Reset Controller required properties: +- compatible: Should be "atmel,-rstc". + can be "at91sam9260" or "at91sam9g45" +- reg: Should contain registers location and length + +Example: + + rstc@fffffd00 { + compatible = "atmel,at91sam9260-rstc"; + reg = <0xfffffd00 0x10>; + }; diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index dd5d114a0e1d..bcad6e7dccce 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -64,6 +64,11 @@ reg = <0xfffffc00 0x100>; }; + rstc@fffffd00 { + compatible = "atmel,at91sam9260-rstc"; + reg = <0xfffffd00 0x10>; + }; + pit: timer@fffffd30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffd30 0xf>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 621a329307d6..faccd4f5aace 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -65,6 +65,11 @@ reg = <0xfffffc00 0x100>; }; + rstc@fffffd00 { + compatible = "atmel,at91sam9g45-rstc"; + reg = <0xfffffd00 0x10>; + }; + pit: timer@fffffd30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffd30 0xf>; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 3855843fc038..d9a93fdd35a5 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -63,6 +63,11 @@ reg = <0xfffffc00 0x100>; }; + rstc@fffffe00 { + compatible = "atmel,at91sam9g45-rstc"; + reg = <0xfffffe00 0x10>; + }; + pit: timer@fffffe30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffe30 0xf>; diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index 7bec5a40d01a..c121fe5fabbd 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -306,7 +306,6 @@ static void __init at91sam9x5_ioremap_registers(void) void __init at91sam9x5_initialize(void) { - arm_pm_restart = at91sam9g45_restart; at91_extern_irq = (1 << AT91SAM9X5_ID_IRQ0); /* Register GPIO subsystem (using DT) */ diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index d7abc25f6c64..3e48b59dfa74 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -287,8 +287,38 @@ void __init at91_ioremap_matrix(u32 base_addr) } #if defined(CONFIG_OF) +static struct of_device_id rstc_ids[] = { + { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9_alt_restart }, + { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, + { /*sentinel*/ } +}; + +static void at91_dt_rstc(void) +{ + struct device_node *np; + const struct of_device_id *of_id; + + np = of_find_matching_node(NULL, rstc_ids); + if (!np) + panic("unable to find compatible rstc node in dtb\n"); + + at91_rstc_base = of_iomap(np, 0); + if (!at91_rstc_base) + panic("unable to map rstc cpu registers\n"); + + of_id = of_match_node(rstc_ids, np); + if (!of_id) + panic("AT91: rtsc no restart function availlable\n"); + + arm_pm_restart = of_id->data; + + of_node_put(np); +} + void __init at91_dt_initialize(void) { + at91_dt_rstc(); + /* temporary until have the ramc binding*/ at91_boot_soc.ioremap_registers(); -- cgit v1.2.3 From a7776ec625c8ca90d050953946a5b72eaf41c21c Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 2 Mar 2012 20:54:37 +0800 Subject: ARM: at91: add ram controller DT support We can now drop the call to ioremap_registers() as we have the binding for the SDRAM/DDR Controller. Drop ioremap_registers() for sam9x5 too. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Rob Herring Acked-by: Nicolas Ferre --- .../devicetree/bindings/arm/atmel-at91.txt | 19 ++++++++++ arch/arm/boot/dts/at91sam9g20.dtsi | 5 +++ arch/arm/boot/dts/at91sam9g45.dtsi | 6 ++++ arch/arm/boot/dts/at91sam9x5.dtsi | 5 +++ arch/arm/mach-at91/at91sam9x5.c | 6 ---- arch/arm/mach-at91/include/mach/at91sam9x5.h | 5 --- arch/arm/mach-at91/pm.c | 13 ------- arch/arm/mach-at91/setup.c | 40 ++++++++++++++++++++-- 8 files changed, 72 insertions(+), 27 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index a64f86717b5d..1f8782077433 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -42,3 +42,22 @@ Example: compatible = "atmel,at91sam9260-rstc"; reg = <0xfffffd00 0x10>; }; + +RAMC SDRAM/DDR Controller required properties: +- compatible: Should be "atmel,at91sam9260-sdramc", + "atmel,at91sam9g45-ddramc", +- reg: Should contain registers location and length + For at91sam9263 and at91sam9g45 you must specify 2 entries. + +Examples: + + ramc0: ramc@ffffe800 { + compatible = "atmel,at91sam9g45-ddramc"; + reg = <0xffffe800 0x200>; + }; + + ramc0: ramc@ffffe400 { + compatible = "atmel,at91sam9g45-ddramc"; + reg = <0xffffe400 0x200 + 0xffffe600 0x200>; + }; diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index bcad6e7dccce..0a1df8d9bfb6 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -59,6 +59,11 @@ reg = <0xfffff000 0x200>; }; + ramc0: ramc@ffffea00 { + compatible = "atmel,at91sam9260-sdramc"; + reg = <0xffffea00 0x200>; + }; + pmc: pmc@fffffc00 { compatible = "atmel,at91rm9200-pmc"; reg = <0xfffffc00 0x100>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index faccd4f5aace..587a1913c062 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -60,6 +60,12 @@ reg = <0xfffff000 0x200>; }; + ramc0: ramc@ffffe400 { + compatible = "atmel,at91sam9g45-ddramc"; + reg = <0xffffe400 0x200 + 0xffffe600 0x200>; + }; + pmc: pmc@fffffc00 { compatible = "atmel,at91rm9200-pmc"; reg = <0xfffffc00 0x100>; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index d9a93fdd35a5..73c46e3dffab 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -58,6 +58,11 @@ reg = <0xfffff000 0x200>; }; + ramc0: ramc@ffffe800 { + compatible = "atmel,at91sam9g45-ddramc"; + reg = <0xffffe800 0x200>; + }; + pmc: pmc@fffffc00 { compatible = "atmel,at91rm9200-pmc"; reg = <0xfffffc00 0x100>; diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index c121fe5fabbd..01b2bd816a9a 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -299,11 +299,6 @@ static void __init at91sam9x5_map_io(void) at91_init_sram(0, AT91SAM9X5_SRAM_BASE, AT91SAM9X5_SRAM_SIZE); } -static void __init at91sam9x5_ioremap_registers(void) -{ - at91_ioremap_ramc(0, AT91SAM9X5_BASE_DDRSDRC0, 512); -} - void __init at91sam9x5_initialize(void) { at91_extern_irq = (1 << AT91SAM9X5_ID_IRQ0); @@ -356,7 +351,6 @@ static unsigned int at91sam9x5_default_irq_priority[NR_AIC_IRQS] __initdata = { struct at91_init_soc __initdata at91sam9x5_soc = { .map_io = at91sam9x5_map_io, .default_irq_priority = at91sam9x5_default_irq_priority, - .ioremap_registers = at91sam9x5_ioremap_registers, .register_clocks = at91sam9x5_register_clocks, .init = at91sam9x5_initialize, }; diff --git a/arch/arm/mach-at91/include/mach/at91sam9x5.h b/arch/arm/mach-at91/include/mach/at91sam9x5.h index a297a77d88e2..88e43d534cdf 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9x5.h +++ b/arch/arm/mach-at91/include/mach/at91sam9x5.h @@ -54,11 +54,6 @@ #define AT91SAM9X5_BASE_USART1 0xf8020000 #define AT91SAM9X5_BASE_USART2 0xf8024000 -/* - * System Peripherals - */ -#define AT91SAM9X5_BASE_DDRSDRC0 0xffffe800 - /* * Base addresses for early serial code (uncompress.h) */ diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 6c9d5e69ac28..f630250c6b87 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -197,19 +197,6 @@ extern void at91_slow_clock(void __iomem *pmc, void __iomem *ramc0, extern u32 at91_slow_clock_sz; #endif -void __iomem *at91_ramc_base[2]; - -void __init at91_ioremap_ramc(int id, u32 addr, u32 size) -{ - if (id < 0 || id > 1) { - pr_emerg("Wrong RAM controller id (%d), cannot continue\n", id); - BUG(); - } - at91_ramc_base[id] = ioremap(addr, size); - if (!at91_ramc_base[id]) - panic("Impossible to ioremap ramc.%d 0x%x\n", id, addr); -} - static int at91_pm_enter(suspend_state_t state) { at91_gpio_suspend(); diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 3e48b59dfa74..46d0a56ba825 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -52,6 +52,19 @@ void __init at91_init_interrupts(unsigned int *priority) at91_gpio_irq_setup(); } +void __iomem *at91_ramc_base[2]; + +void __init at91_ioremap_ramc(int id, u32 addr, u32 size) +{ + if (id < 0 || id > 1) { + pr_emerg("Wrong RAM controller id (%d), cannot continue\n", id); + BUG(); + } + at91_ramc_base[id] = ioremap(addr, size); + if (!at91_ramc_base[id]) + panic("Impossible to ioremap ramc.%d 0x%x\n", id, addr); +} + static struct map_desc sram_desc[2] __initdata; void __init at91_init_sram(int bank, unsigned long base, unsigned int length) @@ -315,12 +328,33 @@ static void at91_dt_rstc(void) of_node_put(np); } +static struct of_device_id ramc_ids[] = { + { .compatible = "atmel,at91sam9260-sdramc" }, + { .compatible = "atmel,at91sam9g45-ddramc" }, + { /*sentinel*/ } +}; + +static void at91_dt_ramc(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, ramc_ids); + if (!np) + panic("unable to find compatible ram conroller node in dtb\n"); + + at91_ramc_base[0] = of_iomap(np, 0); + if (!at91_ramc_base[0]) + panic("unable to map ramc[0] cpu registers\n"); + /* the controller may have 2 banks */ + at91_ramc_base[1] = of_iomap(np, 1); + + of_node_put(np); +} + void __init at91_dt_initialize(void) { at91_dt_rstc(); - - /* temporary until have the ramc binding*/ - at91_boot_soc.ioremap_registers(); + at91_dt_ramc(); /* Init clock subsystem */ at91_dt_clock_init(); -- cgit v1.2.3 From 82015c4eae2ac67cfed8e98f8d9a4ee77a2d26ca Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 2 Mar 2012 21:01:00 +0800 Subject: ARM: at91: add Shutdown Controller (SHDWC) DT support Use a string to specific the wakeup mode to make it more readable. Add the Real-time Clock Wake-up support too for sam9g45 and sam9x5. Add AT91_SHDW_CPTWK0_MAX to specific the Max of the Wakeup Counter. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Rob Herring Acked-by: Nicolas Ferre --- .../devicetree/bindings/arm/atmel-at91.txt | 29 ++++++++ arch/arm/boot/dts/at91sam9g20.dtsi | 5 ++ arch/arm/boot/dts/at91sam9g45.dtsi | 5 ++ arch/arm/boot/dts/at91sam9x5.dtsi | 5 ++ arch/arm/mach-at91/include/mach/at91_shdwc.h | 4 +- arch/arm/mach-at91/setup.c | 77 ++++++++++++++++++++++ 6 files changed, 124 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 1f8782077433..ecc81e368715 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -61,3 +61,32 @@ Examples: reg = <0xffffe400 0x200 0xffffe600 0x200>; }; + +SHDWC Shutdown Controller + +required properties: +- compatible: Should be "atmel,-shdwc". + can be "at91sam9260", "at91sam9rl" or "at91sam9x5". +- reg: Should contain registers location and length + +optional properties: +- atmel,wakeup-mode: String, operation mode of the wakeup mode. + Supported values are: "none", "high", "low", "any". +- atmel,wakeup-counter: Counter on Wake-up 0 (between 0x0 and 0xf). + +optional at91sam9260 properties: +- atmel,wakeup-rtt-timer: boolean to enable Real-time Timer Wake-up. + +optional at91sam9rl properties: +- atmel,wakeup-rtc-timer: boolean to enable Real-time Clock Wake-up. +- atmel,wakeup-rtt-timer: boolean to enable Real-time Timer Wake-up. + +optional at91sam9x5 properties: +- atmel,wakeup-rtc-timer: boolean to enable Real-time Clock Wake-up. + +Example: + + rstc@fffffd00 { + compatible = "atmel,at91sam9260-rstc"; + reg = <0xfffffd00 0x10>; + }; diff --git a/arch/arm/boot/dts/at91sam9g20.dtsi b/arch/arm/boot/dts/at91sam9g20.dtsi index 0a1df8d9bfb6..9a0647bb387f 100644 --- a/arch/arm/boot/dts/at91sam9g20.dtsi +++ b/arch/arm/boot/dts/at91sam9g20.dtsi @@ -74,6 +74,11 @@ reg = <0xfffffd00 0x10>; }; + shdwc@fffffd10 { + compatible = "atmel,at91sam9260-shdwc"; + reg = <0xfffffd10 0x10>; + }; + pit: timer@fffffd30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffd30 0xf>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 587a1913c062..8908f078c307 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -83,6 +83,11 @@ }; + shdwc@fffffd10 { + compatible = "atmel,at91sam9rl-shdwc"; + reg = <0xfffffd10 0x10>; + }; + tcb0: timer@fff7c000 { compatible = "atmel,at91rm9200-tcb"; reg = <0xfff7c000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 73c46e3dffab..20155ccbbe14 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -73,6 +73,11 @@ reg = <0xfffffe00 0x10>; }; + shdwc@fffffe10 { + compatible = "atmel,at91sam9x5-shdwc"; + reg = <0xfffffe10 0x10>; + }; + pit: timer@fffffe30 { compatible = "atmel,at91sam9260-pit"; reg = <0xfffffe30 0xf>; diff --git a/arch/arm/mach-at91/include/mach/at91_shdwc.h b/arch/arm/mach-at91/include/mach/at91_shdwc.h index 1d4fe822c77a..60478ea8bd46 100644 --- a/arch/arm/mach-at91/include/mach/at91_shdwc.h +++ b/arch/arm/mach-at91/include/mach/at91_shdwc.h @@ -36,9 +36,11 @@ extern void __iomem *at91_shdwc_base; #define AT91_SHDW_WKMODE0_HIGH 1 #define AT91_SHDW_WKMODE0_LOW 2 #define AT91_SHDW_WKMODE0_ANYLEVEL 3 -#define AT91_SHDW_CPTWK0 (0xf << 4) /* Counter On Wake Up 0 */ +#define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */ +#define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */ #define AT91_SHDW_CPTWK0_(x) ((x) << 4) #define AT91_SHDW_RTTWKEN (1 << 16) /* Real Time Timer Wake-up Enable */ +#define AT91_SHDW_RTCWKEN (1 << 17) /* Real Time Clock Wake-up Enable */ #define AT91_SHDW_SR 0x08 /* Shut Down Status Register */ #define AT91_SHDW_WAKEUP0 (1 << 0) /* Wake-up 0 Status */ diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 46d0a56ba825..1083739e3065 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -351,10 +351,87 @@ static void at91_dt_ramc(void) of_node_put(np); } +static struct of_device_id shdwc_ids[] = { + { .compatible = "atmel,at91sam9260-shdwc", }, + { .compatible = "atmel,at91sam9rl-shdwc", }, + { .compatible = "atmel,at91sam9x5-shdwc", }, + { /*sentinel*/ } +}; + +static const char *shdwc_wakeup_modes[] = { + [AT91_SHDW_WKMODE0_NONE] = "none", + [AT91_SHDW_WKMODE0_HIGH] = "high", + [AT91_SHDW_WKMODE0_LOW] = "low", + [AT91_SHDW_WKMODE0_ANYLEVEL] = "any", +}; + +const int at91_dtget_shdwc_wakeup_mode(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "atmel,wakeup-mode", &pm); + if (err < 0) + return AT91_SHDW_WKMODE0_ANYLEVEL; + + for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++) + if (!strcasecmp(pm, shdwc_wakeup_modes[i])) + return i; + + return -ENODEV; +} + +static void at91_dt_shdwc(void) +{ + struct device_node *np; + int wakeup_mode; + u32 reg; + u32 mode = 0; + + np = of_find_matching_node(NULL, shdwc_ids); + if (!np) { + pr_debug("AT91: unable to find compatible shutdown (shdwc) conroller node in dtb\n"); + return; + } + + at91_shdwc_base = of_iomap(np, 0); + if (!at91_shdwc_base) + panic("AT91: unable to map shdwc cpu registers\n"); + + wakeup_mode = at91_dtget_shdwc_wakeup_mode(np); + if (wakeup_mode < 0) { + pr_warn("AT91: shdwc unknown wakeup mode\n"); + goto end; + } + + if (!of_property_read_u32(np, "atmel,wakeup-counter", ®)) { + if (reg > AT91_SHDW_CPTWK0_MAX) { + pr_warn("AT91: shdwc wakeup conter 0x%x > 0x%x reduce it to 0x%x\n", + reg, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX); + reg = AT91_SHDW_CPTWK0_MAX; + } + mode |= AT91_SHDW_CPTWK0_(reg); + } + + if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) + mode |= AT91_SHDW_RTCWKEN; + + if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) + mode |= AT91_SHDW_RTTWKEN; + + at91_shdwc_write(AT91_SHDW_MR, wakeup_mode | mode); + +end: + pm_power_off = at91sam9_poweroff; + + of_node_put(np); +} + void __init at91_dt_initialize(void) { at91_dt_rstc(); at91_dt_ramc(); + at91_dt_shdwc(); /* Init clock subsystem */ at91_dt_clock_init(); -- cgit v1.2.3 From 2419730f8f8ce04cce9e39a715c149283210ce27 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Mon, 21 Nov 2011 06:55:18 +0800 Subject: ARM: at91: usb ohci add dt support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Cc: Nicolas Ferre Acked-by: Grant Likely Acked-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/atmel-usb.txt | 19 ++++ drivers/usb/host/ohci-at91.c | 101 ++++++++++++++++++++- 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/usb/atmel-usb.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt new file mode 100644 index 000000000000..6c7f728a362e --- /dev/null +++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt @@ -0,0 +1,19 @@ +Atmel SOC USB controllers + +OHCI + +Required properties: + - compatible: Should be "atmel,at91rm9200-ohci" for USB controllers + used in host mode. + - num-ports: Number of ports. + - atmel,vbus-gpio: If present, specifies a gpio that needs to be + activated for the bus to be powered. + - atmel,oc-gpio: If present, specifies a gpio that needs to be + activated for the overcurrent detection. + +usb0: ohci@00500000 { + compatible = "atmel,at91rm9200-ohci", "usb-ohci"; + reg = <0x00500000 0x100000>; + interrupts = <20 4>; + num-ports = <2>; +}; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 8e855eb0bf89..db8963f5fbce 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include @@ -477,13 +479,109 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) return IRQ_HANDLED; } +#ifdef CONFIG_OF +static const struct of_device_id at91_ohci_dt_ids[] = { + { .compatible = "atmel,at91rm9200-ohci" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, at91_ohci_dt_ids); + +static u64 at91_ohci_dma_mask = DMA_BIT_MASK(32); + +static int __devinit ohci_at91_of_init(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int i, ret, gpio; + enum of_gpio_flags flags; + struct at91_usbh_data *pdata; + u32 ports; + + if (!np) + return 0; + + /* Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &at91_ohci_dma_mask; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (!of_property_read_u32(np, "num-ports", &ports)) + pdata->ports = ports; + + for (i = 0; i < 2; i++) { + gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i, &flags); + pdata->vbus_pin[i] = gpio; + if (!gpio_is_valid(gpio)) + continue; + pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW; + ret = gpio_request(gpio, "ohci_vbus"); + if (ret) { + dev_warn(&pdev->dev, "can't request vbus gpio %d", gpio); + continue; + } + ret = gpio_direction_output(gpio, !(flags & OF_GPIO_ACTIVE_LOW) ^ 1); + if (ret) + dev_warn(&pdev->dev, "can't put vbus gpio %d as output %d", + !(flags & OF_GPIO_ACTIVE_LOW) ^ 1, gpio); + } + + for (i = 0; i < 2; i++) { + gpio = of_get_named_gpio_flags(np, "atmel,oc-gpio", i, &flags); + pdata->overcurrent_pin[i] = gpio; + if (!gpio_is_valid(gpio)) + continue; + ret = gpio_request(gpio, "ohci_overcurrent"); + if (ret) { + dev_err(&pdev->dev, "can't request overcurrent gpio %d", gpio); + continue; + } + + ret = gpio_direction_input(gpio); + if (ret) { + dev_err(&pdev->dev, "can't configure overcurrent gpio %d as input", gpio); + continue; + } + + ret = request_irq(gpio_to_irq(gpio), + ohci_hcd_at91_overcurrent_irq, + IRQF_SHARED, "ohci_overcurrent", pdev); + if (ret) { + gpio_free(gpio); + dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n"); + } + } + + pdev->dev.platform_data = pdata; + + return 0; +} +#else +static int __devinit ohci_at91_of_init(struct platform_device *pdev) +{ + return 0; +} +#endif + /*-------------------------------------------------------------------------*/ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) { - struct at91_usbh_data *pdata = pdev->dev.platform_data; + struct at91_usbh_data *pdata; int i; + i = ohci_at91_of_init(pdev); + + if (i) + return i; + + pdata = pdev->dev.platform_data; + if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { if (!gpio_is_valid(pdata->vbus_pin[i])) @@ -596,5 +694,6 @@ static struct platform_driver ohci_hcd_at91_driver = { .driver = { .name = "at91_ohci", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_ohci_dt_ids), }, }; -- cgit v1.2.3 From 9d843003357f0e4948ac624a99a411a2dc37dfaf Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Tue, 22 Nov 2011 12:11:13 +0800 Subject: ARM: at91: usb ehci add dt support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Cc: Nicolas Ferre Acked-by: Grant Likely Acked-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/atmel-usb.txt | 12 +++++++++++ drivers/usb/host/ehci-atmel.c | 24 +++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt index 6c7f728a362e..0143d7c5b4b9 100644 --- a/Documentation/devicetree/bindings/usb/atmel-usb.txt +++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt @@ -17,3 +17,15 @@ usb0: ohci@00500000 { interrupts = <20 4>; num-ports = <2>; }; + +EHCI + +Required properties: + - compatible: Should be "atmel,at91sam9g45-ehci" for USB controllers + used in host mode. + +usb1: ehci@00800000 { + compatible = "atmel,at91sam9g45-ehci", "usb-ehci"; + reg = <0x00800000 0x100000>; + interrupts = <22 4>; +}; diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index a5a3ef1f0096..19f318ababa2 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -13,6 +13,7 @@ #include #include +#include /* interface and function clocks */ static struct clk *iclk, *fclk; @@ -115,6 +116,8 @@ static const struct hc_driver ehci_atmel_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static u64 at91_ehci_dma_mask = DMA_BIT_MASK(32); + static int __devinit ehci_atmel_drv_probe(struct platform_device *pdev) { struct usb_hcd *hcd; @@ -137,6 +140,13 @@ static int __devinit ehci_atmel_drv_probe(struct platform_device *pdev) goto fail_create_hcd; } + /* Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &at91_ehci_dma_mask; + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; @@ -225,9 +235,21 @@ static int __devexit ehci_atmel_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id atmel_ehci_dt_ids[] = { + { .compatible = "atmel,at91sam9g45-ehci" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids); +#endif + static struct platform_driver ehci_atmel_driver = { .probe = ehci_atmel_drv_probe, .remove = __devexit_p(ehci_atmel_drv_remove), .shutdown = usb_hcd_platform_shutdown, - .driver.name = "atmel-ehci", + .driver = { + .name = "atmel-ehci", + .of_match_table = of_match_ptr(atmel_ehci_dt_ids), + }, }; -- cgit v1.2.3 From d1494a340807c9b77aa44bc8d8166353df4cf1c3 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 28 Jan 2012 22:35:36 +0800 Subject: USB: at91: Device udc add dt support Allow to compile it if AT91 is enable. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/atmel-usb.txt | 18 ++++++++++ drivers/usb/gadget/Kconfig | 2 +- drivers/usb/gadget/at91_udc.c | 40 ++++++++++++++++++++-- 3 files changed, 57 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt index 0143d7c5b4b9..60bd2150a3e6 100644 --- a/Documentation/devicetree/bindings/usb/atmel-usb.txt +++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt @@ -29,3 +29,21 @@ usb1: ehci@00800000 { reg = <0x00800000 0x100000>; interrupts = <22 4>; }; + +AT91 USB device controller + +Required properties: + - compatible: Should be "atmel,at91rm9200-udc" + - reg: Address and length of the register set for the device + - interrupts: Should contain macb interrupt + +Optional properties: + - atmel,vbus-gpio: If present, specifies a gpio that needs to be + activated for the bus to be powered. + +usb1: gadget@fffa4000 { + compatible = "atmel,at91rm9200-udc"; + reg = <0xfffa4000 0x4000>; + interrupts = <10 4>; + atmel,vbus-gpio = <&pioC 5 0>; +}; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 85ae4b46bb68..edf114412135 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -137,7 +137,7 @@ choice config USB_AT91 tristate "Atmel AT91 USB Device Port" - depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91SAM9G45 + depends on ARCH_AT91 help Many Atmel AT91 processors (such as the AT91RM2000) have a full speed USB Device Port with support for five configurable diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index f99b3dc745bd..4063209fe8da 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -1707,7 +1709,27 @@ static void at91udc_shutdown(struct platform_device *dev) spin_unlock_irqrestore(&udc->lock, flags); } -static int __init at91udc_probe(struct platform_device *pdev) +static void __devinit at91udc_of_init(struct at91_udc *udc, + struct device_node *np) +{ + struct at91_udc_data *board = &udc->board; + u32 val; + enum of_gpio_flags flags; + + if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0) + board->vbus_polled = 1; + + board->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0, + &flags); + board->vbus_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + + board->pullup_pin = of_get_named_gpio_flags(np, "atmel,pullup-gpio", 0, + &flags); + + board->pullup_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; +} + +static int __devinit at91udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct at91_udc *udc; @@ -1742,7 +1764,11 @@ static int __init at91udc_probe(struct platform_device *pdev) /* init software state */ udc = &controller; udc->gadget.dev.parent = dev; - udc->board = *(struct at91_udc_data *) dev->platform_data; + if (pdev->dev.of_node) + at91udc_of_init(udc, pdev->dev.of_node); + else + memcpy(&udc->board, dev->platform_data, + sizeof(struct at91_udc_data)); udc->pdev = pdev; udc->enabled = 0; spin_lock_init(&udc->lock); @@ -1971,6 +1997,15 @@ static int at91udc_resume(struct platform_device *pdev) #define at91udc_resume NULL #endif +#if defined(CONFIG_OF) +static const struct of_device_id at91_udc_dt_ids[] = { + { .compatible = "atmel,at91rm9200-udc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); +#endif + static struct platform_driver at91_udc_driver = { .remove = __exit_p(at91udc_remove), .shutdown = at91udc_shutdown, @@ -1979,6 +2014,7 @@ static struct platform_driver at91_udc_driver = { .driver = { .name = (char *) driver_name, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_udc_dt_ids), }, }; -- cgit v1.2.3 From 9652e8bd16e73f7a34cabf1ab114aaa5c97db660 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 16 Mar 2012 14:03:23 +0100 Subject: ARM: SPEAr600: Add device-tree support to SPEAr600 boards This patch adds a generic target for SPEAr600 board that can be configured via the device-tree. Currently the following devices are supported via the devicetree: - VIC interrupts - PL011 UART - PL061 GPIO - Synopsys DW I2C - Synopsys DW ethernet Other peripheral devices (e.g. SMI flash, FSMC NAND flash etc) will follow in later patches. Only the spear600-evb is currently supported. Other SPEAr600 based boards will follow later. Since the current mainline SPEAr600 code only supports the SPEAr600 evaluation board, with nearly zero peripheral devices (only UART and GPIO), it makes sense to switch over to DT based configuration completely now. So this patch also removes all non-DT stuff, mainly platform device data. The files spear600.c and spear600_evb.c are removed completely. Signed-off-by: Stefan Roese Acked-by: Viresh Kumar Acked-by: Jean-Christophe PLAGNIOL-VILLARD Reviewed-by: Arnd Bergmann Signed-off-by: Arnd Bergmann --- Documentation/devicetree/bindings/arm/spear.txt | 8 ++ arch/arm/boot/dts/spear600-evb.dts | 47 +++++++ arch/arm/boot/dts/spear600.dtsi | 174 ++++++++++++++++++++++++ arch/arm/mach-spear6xx/Kconfig | 7 +- arch/arm/mach-spear6xx/Makefile | 6 - arch/arm/mach-spear6xx/clock.c | 14 +- arch/arm/mach-spear6xx/spear600.c | 25 ---- arch/arm/mach-spear6xx/spear600_evb.c | 54 -------- arch/arm/mach-spear6xx/spear6xx.c | 132 +++++------------- 9 files changed, 276 insertions(+), 191 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/spear.txt create mode 100644 arch/arm/boot/dts/spear600-evb.dts create mode 100644 arch/arm/boot/dts/spear600.dtsi delete mode 100644 arch/arm/mach-spear6xx/spear600.c delete mode 100644 arch/arm/mach-spear6xx/spear600_evb.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/spear.txt b/Documentation/devicetree/bindings/arm/spear.txt new file mode 100644 index 000000000000..f8e54f092328 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/spear.txt @@ -0,0 +1,8 @@ +ST SPEAr Platforms Device Tree Bindings +--------------------------------------- + +Boards with the ST SPEAr600 SoC shall have the following properties: + +Required root node property: + +compatible = "st,spear600"; diff --git a/arch/arm/boot/dts/spear600-evb.dts b/arch/arm/boot/dts/spear600-evb.dts new file mode 100644 index 000000000000..636292e18c90 --- /dev/null +++ b/arch/arm/boot/dts/spear600-evb.dts @@ -0,0 +1,47 @@ +/* + * Copyright 2012 Stefan Roese + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; +/include/ "spear600.dtsi" + +/ { + model = "ST SPEAr600 Evaluation Board"; + compatible = "st,spear600-evb", "st,spear600"; + #address-cells = <1>; + #size-cells = <1>; + + memory { + device_type = "memory"; + reg = <0 0x10000000>; + }; + + ahb { + gmac: ethernet@e0800000 { + phy-mode = "gmii"; + status = "okay"; + }; + + apb { + serial@d0000000 { + status = "okay"; + }; + + serial@d0080000 { + status = "okay"; + }; + + i2c@d0200000 { + clock-frequency = <400000>; + status = "okay"; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/spear600.dtsi b/arch/arm/boot/dts/spear600.dtsi new file mode 100644 index 000000000000..ebe0885a2b98 --- /dev/null +++ b/arch/arm/boot/dts/spear600.dtsi @@ -0,0 +1,174 @@ +/* + * Copyright 2012 Stefan Roese + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/include/ "skeleton.dtsi" + +/ { + compatible = "st,spear600"; + + cpus { + cpu@0 { + compatible = "arm,arm926ejs"; + }; + }; + + memory { + device_type = "memory"; + reg = <0 0x40000000>; + }; + + ahb { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + ranges = <0xd0000000 0xd0000000 0x30000000>; + + vic0: interrupt-controller@f1100000 { + compatible = "arm,pl190-vic"; + interrupt-controller; + reg = <0xf1100000 0x1000>; + #interrupt-cells = <1>; + }; + + vic1: interrupt-controller@f1000000 { + compatible = "arm,pl190-vic"; + interrupt-controller; + reg = <0xf1000000 0x1000>; + #interrupt-cells = <1>; + }; + + gmac: ethernet@e0800000 { + compatible = "st,spear600-gmac"; + reg = <0xe0800000 0x8000>; + interrupt-parent = <&vic1>; + interrupts = <24 23>; + interrupt-names = "macirq", "eth_wake_irq"; + status = "disabled"; + }; + + fsmc: flash@d1800000 { + compatible = "st,spear600-fsmc-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xd1800000 0x1000 /* FSMC Register */ + 0xd2000000 0x4000>; /* NAND Base */ + reg-names = "fsmc_regs", "nand_data"; + st,ale-off = <0x20000>; + st,cle-off = <0x10000>; + status = "disabled"; + }; + + smi: flash@fc000000 { + compatible = "st,spear600-smi"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xfc000000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <12>; + status = "disabled"; + }; + + ehci@e1800000 { + compatible = "st,spear600-ehci", "usb-ehci"; + reg = <0xe1800000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <27>; + status = "disabled"; + }; + + ehci@e2000000 { + compatible = "st,spear600-ehci", "usb-ehci"; + reg = <0xe2000000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <29>; + status = "disabled"; + }; + + ohci@e1900000 { + compatible = "st,spear600-ohci", "usb-ohci"; + reg = <0xe1900000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <26>; + status = "disabled"; + }; + + ohci@e2100000 { + compatible = "st,spear600-ohci", "usb-ohci"; + reg = <0xe2100000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <28>; + status = "disabled"; + }; + + apb { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + ranges = <0xd0000000 0xd0000000 0x30000000>; + + serial@d0000000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xd0000000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <24>; + status = "disabled"; + }; + + serial@d0080000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xd0080000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <25>; + status = "disabled"; + }; + + /* local/cpu GPIO */ + gpio0: gpio@f0100000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xf0100000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <18>; + }; + + /* basic GPIO */ + gpio1: gpio@fc980000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfc980000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <19>; + }; + + /* appl GPIO */ + gpio2: gpio@d8100000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xd8100000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <4>; + }; + + i2c@d0200000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,designware-i2c"; + reg = <0xd0200000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <28>; + status = "disabled"; + }; + }; + }; +}; diff --git a/arch/arm/mach-spear6xx/Kconfig b/arch/arm/mach-spear6xx/Kconfig index ff4ae5ba00f1..fbe298bd1d92 100644 --- a/arch/arm/mach-spear6xx/Kconfig +++ b/arch/arm/mach-spear6xx/Kconfig @@ -5,11 +5,12 @@ if ARCH_SPEAR6XX menu "SPEAr6xx Implementations" -config BOARD_SPEAR600_EVB - bool "SPEAr600 Evaluation Board" +config BOARD_SPEAR600_DT + bool "SPEAr600 generic board configured via device-tree" select MACH_SPEAR600 + select USE_OF help - Supports ST SPEAr600 Evaluation Board + Supports ST SPEAr600 boards configured via the device-tree endmenu diff --git a/arch/arm/mach-spear6xx/Makefile b/arch/arm/mach-spear6xx/Makefile index cc1a4d82d459..76e5750552fc 100644 --- a/arch/arm/mach-spear6xx/Makefile +++ b/arch/arm/mach-spear6xx/Makefile @@ -4,9 +4,3 @@ # common files obj-y += clock.o spear6xx.o - -# spear600 specific files -obj-$(CONFIG_MACH_SPEAR600) += spear600.o - -# spear600 boards files -obj-$(CONFIG_BOARD_SPEAR600_EVB) += spear600_evb.o diff --git a/arch/arm/mach-spear6xx/clock.c b/arch/arm/mach-spear6xx/clock.c index ac70e0d88fef..358f2800f17b 100644 --- a/arch/arm/mach-spear6xx/clock.c +++ b/arch/arm/mach-spear6xx/clock.c @@ -641,8 +641,8 @@ static struct clk_lookup spear_clk_lookups[] = { { .con_id = "gpt0_synth_clk", .clk = &gpt0_synth_clk}, { .con_id = "gpt2_synth_clk", .clk = &gpt2_synth_clk}, { .con_id = "gpt3_synth_clk", .clk = &gpt3_synth_clk}, - { .dev_id = "uart0", .clk = &uart0_clk}, - { .dev_id = "uart1", .clk = &uart1_clk}, + { .dev_id = "d0000000.serial", .clk = &uart0_clk}, + { .dev_id = "d0080000.serial", .clk = &uart1_clk}, { .dev_id = "firda", .clk = &firda_clk}, { .dev_id = "clcd", .clk = &clcd_clk}, { .dev_id = "gpt0", .clk = &gpt0_clk}, @@ -655,20 +655,20 @@ static struct clk_lookup spear_clk_lookups[] = { { .con_id = "usbh.1_clk", .clk = &usbh1_clk}, /* clock derived from ahb clk */ { .con_id = "apb_clk", .clk = &apb_clk}, - { .dev_id = "i2c_designware.0", .clk = &i2c_clk}, + { .dev_id = "d0200000.i2c", .clk = &i2c_clk}, { .dev_id = "dma", .clk = &dma_clk}, { .dev_id = "jpeg", .clk = &jpeg_clk}, { .dev_id = "gmac", .clk = &gmac_clk}, { .dev_id = "smi", .clk = &smi_clk}, - { .con_id = "fsmc", .clk = &fsmc_clk}, + { .dev_id = "fsmc-nand", .clk = &fsmc_clk}, /* clock derived from apb clk */ { .dev_id = "adc", .clk = &adc_clk}, { .dev_id = "ssp-pl022.0", .clk = &ssp0_clk}, { .dev_id = "ssp-pl022.1", .clk = &ssp1_clk}, { .dev_id = "ssp-pl022.2", .clk = &ssp2_clk}, - { .dev_id = "gpio0", .clk = &gpio0_clk}, - { .dev_id = "gpio1", .clk = &gpio1_clk}, - { .dev_id = "gpio2", .clk = &gpio2_clk}, + { .dev_id = "f0100000.gpio", .clk = &gpio0_clk}, + { .dev_id = "fc980000.gpio", .clk = &gpio1_clk}, + { .dev_id = "d8100000.gpio", .clk = &gpio2_clk}, }; void __init spear6xx_clk_init(void) diff --git a/arch/arm/mach-spear6xx/spear600.c b/arch/arm/mach-spear6xx/spear600.c deleted file mode 100644 index d0e6eeae9b04..000000000000 --- a/arch/arm/mach-spear6xx/spear600.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * arch/arm/mach-spear6xx/spear600.c - * - * SPEAr600 machine source file - * - * Copyright (C) 2009 ST Microelectronics - * Rajeev Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include - -/* Add spear600 specific devices here */ - -void __init spear600_init(void) -{ - /* call spear6xx family common init function */ - spear6xx_init(); -} diff --git a/arch/arm/mach-spear6xx/spear600_evb.c b/arch/arm/mach-spear6xx/spear600_evb.c deleted file mode 100644 index c6e4254741cc..000000000000 --- a/arch/arm/mach-spear6xx/spear600_evb.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * arch/arm/mach-spear6xx/spear600_evb.c - * - * SPEAr600 evaluation board source file - * - * Copyright (C) 2009 ST Microelectronics - * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include - -static struct amba_device *amba_devs[] __initdata = { - &gpio_device[0], - &gpio_device[1], - &gpio_device[2], - &uart_device[0], - &uart_device[1], -}; - -static struct platform_device *plat_devs[] __initdata = { -}; - -static void __init spear600_evb_init(void) -{ - unsigned int i; - - /* call spear600 machine init function */ - spear600_init(); - - /* Add Platform Devices */ - platform_add_devices(plat_devs, ARRAY_SIZE(plat_devs)); - - /* Add Amba Devices */ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) - amba_device_register(amba_devs[i], &iomem_resource); -} - -MACHINE_START(SPEAR600, "ST-SPEAR600-EVB") - .atag_offset = 0x100, - .map_io = spear6xx_map_io, - .init_irq = spear6xx_init_irq, - .handle_irq = vic_handle_irq, - .timer = &spear6xx_timer, - .init_machine = spear600_evb_init, - .restart = spear_restart, -MACHINE_END diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c index e0f6628c8b2c..2ed8b14c82c8 100644 --- a/arch/arm/mach-spear6xx/spear6xx.c +++ b/arch/arm/mach-spear6xx/spear6xx.c @@ -6,111 +6,21 @@ * Copyright (C) 2009 ST Microelectronics * Rajeev Kumar * + * Copyright 2012 Stefan Roese + * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ -#include -#include -#include -#include +#include +#include +#include +#include #include -#include #include #include #include -#include - -/* Add spear6xx machines common devices here */ -/* uart device registration */ -struct amba_device uart_device[] = { - { - .dev = { - .init_name = "uart0", - }, - .res = { - .start = SPEAR6XX_ICM1_UART0_BASE, - .end = SPEAR6XX_ICM1_UART0_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - .irq = {IRQ_UART_0, NO_IRQ}, - }, { - .dev = { - .init_name = "uart1", - }, - .res = { - .start = SPEAR6XX_ICM1_UART1_BASE, - .end = SPEAR6XX_ICM1_UART1_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - .irq = {IRQ_UART_1, NO_IRQ}, - } -}; - -/* gpio device registration */ -static struct pl061_platform_data gpio_plat_data[] = { - { - .gpio_base = 0, - .irq_base = SPEAR_GPIO0_INT_BASE, - }, { - .gpio_base = 8, - .irq_base = SPEAR_GPIO1_INT_BASE, - }, { - .gpio_base = 16, - .irq_base = SPEAR_GPIO2_INT_BASE, - }, -}; - -struct amba_device gpio_device[] = { - { - .dev = { - .init_name = "gpio0", - .platform_data = &gpio_plat_data[0], - }, - .res = { - .start = SPEAR6XX_CPU_GPIO_BASE, - .end = SPEAR6XX_CPU_GPIO_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - .irq = {IRQ_LOCAL_GPIO, NO_IRQ}, - }, { - .dev = { - .init_name = "gpio1", - .platform_data = &gpio_plat_data[1], - }, - .res = { - .start = SPEAR6XX_ICM3_GPIO_BASE, - .end = SPEAR6XX_ICM3_GPIO_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - .irq = {IRQ_BASIC_GPIO, NO_IRQ}, - }, { - .dev = { - .init_name = "gpio2", - .platform_data = &gpio_plat_data[2], - }, - .res = { - .start = SPEAR6XX_ICM2_GPIO_BASE, - .end = SPEAR6XX_ICM2_GPIO_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - .irq = {IRQ_APPL_GPIO, NO_IRQ}, - } -}; - -/* This will add devices, and do machine specific tasks */ -void __init spear6xx_init(void) -{ - /* nothing to do for now */ -} - -/* This will initialize vic */ -void __init spear6xx_init_irq(void) -{ - vic_init((void __iomem *)VA_SPEAR6XX_CPU_VIC_PRI_BASE, 0, ~0, 0); - vic_init((void __iomem *)VA_SPEAR6XX_CPU_VIC_SEC_BASE, 32, ~0, 0); -} /* Following will create static virtual/physical mappings */ static struct map_desc spear6xx_io_desc[] __initdata = { @@ -181,3 +91,33 @@ static void __init spear6xx_timer_init(void) struct sys_timer spear6xx_timer = { .init = spear6xx_timer_init, }; + +static void __init spear600_dt_init(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static const char *spear600_dt_board_compat[] = { + "st,spear600", + NULL +}; + +static const struct of_device_id vic_of_match[] __initconst = { + { .compatible = "arm,pl190-vic", .data = vic_of_init, }, + { /* Sentinel */ } +}; + +static void __init spear6xx_dt_init_irq(void) +{ + of_irq_init(vic_of_match); +} + +DT_MACHINE_START(SPEAR600_DT, "ST SPEAr600 (Flattened Device Tree)") + .map_io = spear6xx_map_io, + .init_irq = spear6xx_dt_init_irq, + .handle_irq = vic_handle_irq, + .timer = &spear6xx_timer, + .init_machine = spear600_dt_init, + .restart = spear_restart, + .dt_compat = spear600_dt_board_compat, +MACHINE_END -- cgit v1.2.3 From b8589e2a8065b8e7773742b60ae96b63b757bb69 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Wed, 29 Feb 2012 22:51:32 +0100 Subject: gpio/twl: Add DT support to gpio-twl4030 driver Add the DT support for the I2C GPIO expander inside the twl4030. Note: The pdata parameters still have to be properly adapted using dedicated bindings. Signed-off-by: Benoit Cousson Acked-by: Felipe Balbi Acked-by: Grant Likely Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/gpio/gpio-twl4030.txt | 23 +++++++ drivers/gpio/gpio-twl4030.c | 79 ++++++++++++++-------- 2 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-twl4030.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt b/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt new file mode 100644 index 000000000000..16695d9cf1e8 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt @@ -0,0 +1,23 @@ +twl4030 GPIO controller bindings + +Required properties: +- compatible: + - "ti,twl4030-gpio" for twl4030 GPIO controller +- #gpio-cells : Should be two. + - first cell is the pin number + - second cell is used to specify optional parameters (unused) +- gpio-controller : Marks the device node as a GPIO controller. +- #interrupt-cells : Should be 2. +- interrupt-controller: Mark the device node as an interrupt controller + The first cell is the GPIO number. + The second cell is not used. + +Example: + +twl_gpio: gpio { + compatible = "ti,twl4030-gpio"; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; +}; diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 49e5c6eb403a..94256fe7bf36 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include @@ -256,7 +258,8 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) * and vMMC2 power supplies based on card presence. */ pdata = chip->dev->platform_data; - value |= pdata->mmc_cd & 0x03; + if (pdata) + value |= pdata->mmc_cd & 0x03; status = gpio_twl4030_write(REG_GPIO_CTRL, value); } @@ -395,6 +398,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev); static int __devinit gpio_twl4030_probe(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; + struct device_node *node = pdev->dev.of_node; int ret, irq_base; /* maybe setup IRQs */ @@ -409,6 +413,9 @@ static int __devinit gpio_twl4030_probe(struct platform_device *pdev) return irq_base; } + irq_domain_add_legacy(node, TWL4030_GPIO_MAX, irq_base, 0, + &irq_domain_simple_ops, NULL); + ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base); if (ret < 0) return ret; @@ -416,40 +423,45 @@ static int __devinit gpio_twl4030_probe(struct platform_device *pdev) twl4030_gpio_irq_base = irq_base; no_irqs: - /* - * NOTE: boards may waste power if they don't set pullups - * and pulldowns correctly ... default for non-ULPI pins is - * pulldown, and some other pins may have external pullups - * or pulldowns. Careful! - */ - ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); - if (ret) - dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", - pdata->pullups, pdata->pulldowns, - ret); - - ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); - if (ret) - dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", - pdata->debounce, pdata->mmc_cd, - ret); - - twl_gpiochip.base = pdata->gpio_base; + twl_gpiochip.base = -1; twl_gpiochip.ngpio = TWL4030_GPIO_MAX; twl_gpiochip.dev = &pdev->dev; - /* NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, - * is (still) clear if use_leds is set. - */ - if (pdata->use_leds) - twl_gpiochip.ngpio += 2; + if (pdata) { + twl_gpiochip.base = pdata->gpio_base; + + /* + * NOTE: boards may waste power if they don't set pullups + * and pulldowns correctly ... default for non-ULPI pins is + * pulldown, and some other pins may have external pullups + * or pulldowns. Careful! + */ + ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); + if (ret) + dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", + pdata->pullups, pdata->pulldowns, + ret); + + ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); + if (ret) + dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", + pdata->debounce, pdata->mmc_cd, + ret); + + /* + * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, + * is (still) clear if use_leds is set. + */ + if (pdata->use_leds) + twl_gpiochip.ngpio += 2; + } ret = gpiochip_add(&twl_gpiochip); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); twl_gpiochip.ngpio = 0; gpio_twl4030_remove(pdev); - } else if (pdata->setup) { + } else if (pdata && pdata->setup) { int status; status = pdata->setup(&pdev->dev, @@ -467,7 +479,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev) struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; int status; - if (pdata->teardown) { + if (pdata && pdata->teardown) { status = pdata->teardown(&pdev->dev, pdata->gpio_base, TWL4030_GPIO_MAX); if (status) { @@ -488,12 +500,21 @@ static int gpio_twl4030_remove(struct platform_device *pdev) return -EIO; } +static const struct of_device_id twl_gpio_match[] = { + { .compatible = "ti,twl4030-gpio", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_gpio_match); + /* Note: this hardware lives inside an I2C-based multi-function device. */ MODULE_ALIAS("platform:twl4030_gpio"); static struct platform_driver gpio_twl4030_driver = { - .driver.name = "twl4030_gpio", - .driver.owner = THIS_MODULE, + .driver = { + .name = "twl4030_gpio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl_gpio_match), + }, .probe = gpio_twl4030_probe, .remove = gpio_twl4030_remove, }; -- cgit v1.2.3 From 46856a68dcb5f67c779d211fd6bcb5d7a2a7f19b Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Mon, 12 Mar 2012 20:32:37 +0530 Subject: mmc: omap_hsmmc: Convert hsmmc driver to use device tree Define dt bindings for the ti-omap-hsmmc, and adapt the driver to extract data (which was earlier passed as platform_data) from device tree. Signed-off-by: Rajendra Nayak Acked-by: Rob Herring Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 33 ++++++++++ drivers/mmc/host/omap_hsmmc.c | 73 ++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt new file mode 100644 index 000000000000..dbd4368ab8cc --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -0,0 +1,33 @@ +* TI Highspeed MMC host controller for OMAP + +The Highspeed MMC Host Controller on TI OMAP family +provides an interface for MMC, SD, and SDIO types of memory cards. + +Required properties: +- compatible: + Should be "ti,omap2-hsmmc", for OMAP2 controllers + Should be "ti,omap3-hsmmc", for OMAP3 controllers + Should be "ti,omap4-hsmmc", for OMAP4 controllers +- ti,hwmods: Must be "mmc", n is controller instance starting 1 +- reg : should contain hsmmc registers location and length + +Optional properties: +ti,dual-volt: boolean, supports dual voltage cards +-supply: phandle to the regulator device tree node +"supply-name" examples are "vmmc", "vmmc_aux" etc +ti,bus-width: Number of data lines, default assumed is 1 if the property is missing. +cd-gpios: GPIOs for card detection +wp-gpios: GPIOs for write protection +ti,non-removable: non-removable slot (like eMMC) +ti,needs-special-reset: Requires a special softreset sequence + +Example: + mmc1: mmc@0x4809c000 { + compatible = "ti,omap4-hsmmc"; + reg = <0x4809c000 0x400>; + ti,hwmods = "mmc1"; + ti,dual-volt; + ti,bus-width = <4>; + vmmc-supply = <&vmmc>; /* phandle to regulator node */ + ti,non-removable; + }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 0306aea329d4..e0808d4a7681 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -1710,6 +1713,65 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc) #endif +#ifdef CONFIG_OF +static u16 omap4_reg_offset = 0x100; + +static const struct of_device_id omap_mmc_of_match[] = { + { + .compatible = "ti,omap2-hsmmc", + }, + { + .compatible = "ti,omap3-hsmmc", + }, + { + .compatible = "ti,omap4-hsmmc", + .data = &omap4_reg_offset, + }, + {}, +} +MODULE_DEVICE_TABLE(of, omap_mmc_of_match); + +static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) +{ + struct omap_mmc_platform_data *pdata; + struct device_node *np = dev->of_node; + u32 bus_width; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; /* out of memory */ + + if (of_find_property(np, "ti,dual-volt", NULL)) + pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; + + /* This driver only supports 1 slot */ + pdata->nr_slots = 1; + pdata->slots[0].switch_pin = of_get_named_gpio(np, "cd-gpios", 0); + pdata->slots[0].gpio_wp = of_get_named_gpio(np, "wp-gpios", 0); + + if (of_find_property(np, "ti,non-removable", NULL)) { + pdata->slots[0].nonremovable = true; + pdata->slots[0].no_regulator_off_init = true; + } + of_property_read_u32(np, "ti,bus-width", &bus_width); + if (bus_width == 4) + pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA; + else if (bus_width == 8) + pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA; + + if (of_find_property(np, "ti,needs-special-reset", NULL)) + pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET; + + return pdata; +} +#else +static inline struct omap_mmc_platform_data + *of_get_hsmmc_pdata(struct device *dev) +{ + return NULL; +} +#endif + static int __init omap_hsmmc_probe(struct platform_device *pdev) { struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; @@ -1717,6 +1779,16 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) struct omap_hsmmc_host *host = NULL; struct resource *res; int ret, irq; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev); + if (match) { + pdata = of_get_hsmmc_pdata(&pdev->dev); + if (match->data) { + u16 *offsetp = match->data; + pdata->reg_offset = *offsetp; + } + } if (pdata == NULL) { dev_err(&pdev->dev, "Platform Data is missing\n"); @@ -2122,6 +2194,7 @@ static struct platform_driver omap_hsmmc_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .pm = &omap_hsmmc_dev_pm_ops, + .of_match_table = of_match_ptr(omap_mmc_of_match), }, }; -- cgit v1.2.3 From 83619ea08e9abe0f5ebcfc569a829d1105a1685e Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Tue, 27 Mar 2012 22:54:15 +0100 Subject: mtd: Move fdt partition documentation to a seperate file Partitions are described in the same way for all mtd devices when using devicetree, move the documentation to a separate file and add references to it. Signed-off-by: Jamie Lentin Acked-by: Arnd Bergmann Acked-by: Jason Cooper Signed-off-by: Grant Likely --- .../devicetree/bindings/mtd/arm-versatile.txt | 4 +-- .../devicetree/bindings/mtd/atmel-dataflash.txt | 3 ++ .../devicetree/bindings/mtd/fsl-upm-nand.txt | 4 +++ .../devicetree/bindings/mtd/gpio-control-nand.txt | 3 ++ .../devicetree/bindings/mtd/mtd-physmap.txt | 23 ++----------- .../devicetree/bindings/mtd/partition.txt | 38 ++++++++++++++++++++++ 6 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/partition.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/arm-versatile.txt b/Documentation/devicetree/bindings/mtd/arm-versatile.txt index 476845db94d0..beace4b89daa 100644 --- a/Documentation/devicetree/bindings/mtd/arm-versatile.txt +++ b/Documentation/devicetree/bindings/mtd/arm-versatile.txt @@ -4,5 +4,5 @@ Required properties: - compatible : must be "arm,versatile-flash"; - bank-width : width in bytes of flash interface. -Optional properties: -- Subnode partition map from mtd flash binding +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. diff --git a/Documentation/devicetree/bindings/mtd/atmel-dataflash.txt b/Documentation/devicetree/bindings/mtd/atmel-dataflash.txt index ef66ddd01da0..1889a4db5b7c 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-dataflash.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-dataflash.txt @@ -3,6 +3,9 @@ Required properties: - compatible : "atmel,", "atmel,", "atmel,dataflash". +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + Example: flash@1 { diff --git a/Documentation/devicetree/bindings/mtd/fsl-upm-nand.txt b/Documentation/devicetree/bindings/mtd/fsl-upm-nand.txt index 00f1f546b32e..fce4894f5a98 100644 --- a/Documentation/devicetree/bindings/mtd/fsl-upm-nand.txt +++ b/Documentation/devicetree/bindings/mtd/fsl-upm-nand.txt @@ -19,6 +19,10 @@ Optional properties: read registers (tR). Required if property "gpios" is not used (R/B# pins not connected). +Each flash chip described may optionally contain additional sub-nodes +describing partitions of the address space. See partition.txt for more +detail. + Examples: upm@1,0 { diff --git a/Documentation/devicetree/bindings/mtd/gpio-control-nand.txt b/Documentation/devicetree/bindings/mtd/gpio-control-nand.txt index 719f4dc58df7..36ef07d3c90f 100644 --- a/Documentation/devicetree/bindings/mtd/gpio-control-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpio-control-nand.txt @@ -25,6 +25,9 @@ Optional properties: GPIO state and before and after command byte writes, this register will be read to ensure that the GPIO accesses have completed. +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + Examples: gpio-nand@1,0 { diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt index 80152cb567d9..a63c2bd7de2b 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt @@ -23,27 +23,8 @@ are defined: - vendor-id : Contains the flash chip's vendor id (1 byte). - device-id : Contains the flash chip's device id (1 byte). -In addition to the information on the mtd bank itself, the -device tree may optionally contain additional information -describing partitions of the address space. This can be -used on platforms which have strong conventions about which -portions of a flash are used for what purposes, but which don't -use an on-flash partition table such as RedBoot. - -Each partition is represented as a sub-node of the mtd device. -Each node's name represents the name of the corresponding -partition of the mtd device. - -Flash partitions - - reg : The partition's offset and size within the mtd bank. - - label : (optional) The label / name for this partition. - If omitted, the label is taken from the node name (excluding - the unit address). - - read-only : (optional) This parameter, if present, is a hint to - Linux that this partition should only be mounted - read-only. This is usually used for flash partitions - containing early-boot firmware images or data which should not - be clobbered. +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. Example: diff --git a/Documentation/devicetree/bindings/mtd/partition.txt b/Documentation/devicetree/bindings/mtd/partition.txt new file mode 100644 index 000000000000..f114ce1657c2 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/partition.txt @@ -0,0 +1,38 @@ +Representing flash partitions in devicetree + +Partitions can be represented by sub-nodes of an mtd device. This can be used +on platforms which have strong conventions about which portions of a flash are +used for what purposes, but which don't use an on-flash partition table such +as RedBoot. + +#address-cells & #size-cells must both be present in the mtd device and be +equal to 1. + +Required properties: +- reg : The partition's offset and size within the mtd bank. + +Optional properties: +- label : The label / name for this partition. If omitted, the label is taken + from the node name (excluding the unit address). +- read-only : This parameter, if present, is a hint to Linux that this + partition should only be mounted read-only. This is usually used for flash + partitions containing early-boot firmware images or data which should not be + clobbered. + +Examples: + + +flash@0 { + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0000000 0x100000>; + read-only; + }; + + uimage@100000 { + reg = <0x0100000 0x200000>; + }; +]; -- cgit v1.2.3 From 31134efc681a5440e2b952eed3bf9a5306a95062 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 4 Nov 2011 11:51:22 -0400 Subject: dt: Linux DT usage model documentation v2: 2nd draft - Editorial cleanups (Randy Dunlap and Stephen Warren) - Added missing Microblaze reference (Stephen Neuendorffer) - Make example of platform_device creation clearer (Shawn Guo) - Expand on PowerPC history and mention i2c mess (David Gibson) - convert to plain text (remove bits of html formating) Signed-off-by: Grant Likely --- Documentation/devicetree/usage-model.txt | 412 +++++++++++++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 Documentation/devicetree/usage-model.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/usage-model.txt b/Documentation/devicetree/usage-model.txt new file mode 100644 index 000000000000..c5a80099b71c --- /dev/null +++ b/Documentation/devicetree/usage-model.txt @@ -0,0 +1,412 @@ +Linux and the Device Tree +------------------------- +The Linux usage model for device tree data + +Author: Grant Likely + +This article describes how Linux uses the device tree. An overview of +the device tree data format can be found on the device tree usage page +at devicetree.org[1]. + +[1] http://devicetree.org/Device_Tree_Usage + +The "Open Firmware Device Tree", or simply Device Tree (DT), is a data +structure and language for describing hardware. More specifically, it +is a description of hardware that is readable by an operating system +so that the operating system doesn't need to hard code details of the +machine. + +Structurally, the DT is a tree, or acyclic graph with named nodes, and +nodes may have an arbitrary number of named properties encapsulating +arbitrary data. A mechanism also exists to create arbitrary +links from one node to another outside of the natural tree structure. + +Conceptually, a common set of usage conventions, called 'bindings', +is defined for how data should appear in the tree to describe typical +hardware characteristics including data busses, interrupt lines, GPIO +connections, and peripheral devices. + +As much as possible, hardware is described using existing bindings to +maximize use of existing support code, but since property and node +names are simply text strings, it is easy to extend existing bindings +or create new ones by defining new nodes and properties. Be wary, +however, of creating a new binding without first doing some homework +about what already exists. There are currently two different, +incompatible, bindings for i2c busses that came about because the new +binding was created without first investigating how i2c devices were +already being enumerated in existing systems. + +1. History +---------- +The DT was originally created by Open Firmware as part of the +communication method for passing data from Open Firmware to a client +program (like to an operating system). An operating system used the +Device Tree to discover the topology of the hardware at runtime, and +thereby support a majority of available hardware without hard coded +information (assuming drivers were available for all devices). + +Since Open Firmware is commonly used on PowerPC and SPARC platforms, +the Linux support for those architectures has for a long time used the +Device Tree. + +In 2005, when PowerPC Linux began a major cleanup and to merge 32-bit +and 64-bit support, the decision was made to require DT support on all +powerpc platforms, regardless of whether or not they used Open +Firmware. To do this, a DT representation called the Flattened Device +Tree (FDT) was created which could be passed to the kernel as a binary +blob without requiring a real Open Firmware implementation. U-Boot, +kexec, and other bootloaders were modified to support both passing a +Device Tree Binary (dtb) and to modify a dtb at boot time. DT was +also added to the PowerPC boot wrapper (arch/powerpc/boot/*) so that +a dtb could be wrapped up with the kernel image to support booting +existing non-DT aware firmware. + +Some time later, FDT infrastructure was generalized to be usable by +all architectures. At the time of this writing, 6 mainlined +architectures (arm, microblaze, mips, powerpc, sparc, and x86) and 1 +out of mainline (nios) have some level of DT support. + +2. Data Model +------------- +If you haven't already read the Device Tree Usage[1] page, +then go read it now. It's okay, I'll wait.... + +2.1 High Level View +------------------- +The most important thing to understand is that the DT is simply a data +structure that describes the hardware. There is nothing magical about +it, and it doesn't magically make all hardware configuration problems +go away. What it does do is provide a language for decoupling the +hardware configuration from the board and device driver support in the +Linux kernel (or any other operating system for that matter). Using +it allows board and device support to become data driven; to make +setup decisions based on data passed into the kernel instead of on +per-machine hard coded selections. + +Ideally, data driven platform setup should result in less code +duplication and make it easier to support a wide range of hardware +with a single kernel image. + +Linux uses DT data for three major purposes: +1) platform identification, +2) runtime configuration, and +3) device population. + +2.2 Platform Identification +--------------------------- +First and foremost, the kernel will use data in the DT to identify the +specific machine. In a perfect world, the specific platform shouldn't +matter to the kernel because all platform details would be described +perfectly by the device tree in a consistent and reliable manner. +Hardware is not perfect though, and so the kernel must identify the +machine during early boot so that it has the opportunity to run +machine-specific fixups. + +In the majority of cases, the machine identity is irrelevant, and the +kernel will instead select setup code based on the machine's core +CPU or SoC. On ARM for example, setup_arch() in +arch/arm/kernel/setup.c will call setup_machine_fdt() in +arch/arm/kernel/devicetree.c which searches through the machine_desc +table and selects the machine_desc which best matches the device tree +data. It determines the best match by looking at the 'compatible' +property in the root device tree node, and comparing it with the +dt_compat list in struct machine_desc. + +The 'compatible' property contains a sorted list of strings starting +with the exact name of the machine, followed by an optional list of +boards it is compatible with sorted from most compatible to least. For +example, the root compatible properties for the TI BeagleBoard and its +successor, the BeagleBoard xM board might look like: + + compatible = "ti,omap3-beagleboard", "ti,omap3450", "ti,omap3"; + compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3"; + +Where "ti,omap3-beagleboard-xm" specifies the exact model, it also +claims that it compatible with the OMAP 3450 SoC, and the omap3 family +of SoCs in general. You'll notice that the list is sorted from most +specific (exact board) to least specific (SoC family). + +Astute readers might point out that the Beagle xM could also claim +compatibility with the original Beagle board. However, one should be +cautioned about doing so at the board level since there is typically a +high level of change from one board to another, even within the same +product line, and it is hard to nail down exactly what is meant when one +board claims to be compatible with another. For the top level, it is +better to err on the side of caution and not claim one board is +compatible with another. The notable exception would be when one +board is a carrier for another, such as a CPU module attached to a +carrier board. + +One more note on compatible values. Any string used in a compatible +property must be documented as to what it indicates. Add +documentation for compatible strings in Documentation/devicetree/bindings. + +Again on ARM, for each machine_desc, the kernel looks to see if +any of the dt_compat list entries appear in the compatible property. +If one does, then that machine_desc is a candidate for driving the +machine. After searching the entire table of machine_descs, +setup_machine_fdt() returns the 'most compatible' machine_desc based +on which entry in the compatible property each machine_desc matches +against. If no matching machine_desc is found, then it returns NULL. + +The reasoning behind this scheme is the observation that in the majority +of cases, a single machine_desc can support a large number of boards +if they all use the same SoC, or same family of SoCs. However, +invariably there will be some exceptions where a specific board will +require special setup code that is not useful in the generic case. +Special cases could be handled by explicitly checking for the +troublesome board(s) in generic setup code, but doing so very quickly +becomes ugly and/or unmaintainable if it is more than just a couple of +cases. + +Instead, the compatible list allows a generic machine_desc to provide +support for a wide common set of boards by specifying "less +compatible" value in the dt_compat list. In the example above, +generic board support can claim compatibility with "ti,omap3" or +"ti,omap3450". If a bug was discovered on the original beagleboard +that required special workaround code during early boot, then a new +machine_desc could be added which implements the workarounds and only +matches on "ti,omap3-beagleboard". + +PowerPC uses a slightly different scheme where it calls the .probe() +hook from each machine_desc, and the first one returning TRUE is used. +However, this approach does not take into account the priority of the +compatible list, and probably should be avoided for new architecture +support. + +2.3 Runtime configuration +------------------------- +In most cases, a DT will be the sole method of communicating data from +firmware to the kernel, so also gets used to pass in runtime and +configuration data like the kernel parameters string and the location +of an initrd image. + +Most of this data is contained in the /chosen node, and when booting +Linux it will look something like this: + + chosen { + bootargs = "console=ttyS0,115200 loglevel=8"; + initrd-start = <0xc8000000>; + initrd-end = <0xc8200000>; + }; + +The bootargs property contains the kernel arguments, and the initrd-* +properties define the address and size of an initrd blob. The +chosen node may also optionally contain an arbitrary number of +additional properties for platform-specific configuration data. + +During early boot, the architecture setup code calls of_scan_flat_dt() +several times with different helper callbacks to parse device tree +data before paging is setup. The of_scan_flat_dt() code scans through +the device tree and uses the helpers to extract information required +during early boot. Typically the early_init_dt_scan_chosen() helper +is used to parse the chosen node including kernel parameters, +early_init_dt_scan_root() to initialize the DT address space model, +and early_init_dt_scan_memory() to determine the size and +location of usable RAM. + +On ARM, the function setup_machine_fdt() is responsible for early +scanning of the device tree after selecting the correct machine_desc +that supports the board. + +2.4 Device population +--------------------- +After the board has been identified, and after the early configuration data +has been parsed, then kernel initialization can proceed in the normal +way. At some point in this process, unflatten_device_tree() is called +to convert the data into a more efficient runtime representation. +This is also when machine-specific setup hooks will get called, like +the machine_desc .init_early(), .init_irq() and .init_machine() hooks +on ARM. The remainder of this section uses examples from the ARM +implementation, but all architectures will do pretty much the same +thing when using a DT. + +As can be guessed by the names, .init_early() is used for any machine- +specific setup that needs to be executed early in the boot process, +and .init_irq() is used to set up interrupt handling. Using a DT +doesn't materially change the behaviour of either of these functions. +If a DT is provided, then both .init_early() and .init_irq() are able +to call any of the DT query functions (of_* in include/linux/of*.h) to +get additional data about the platform. + +The most interesting hook in the DT context is .init_machine() which +is primarily responsible for populating the Linux device model with +data about the platform. Historically this has been implemented on +embedded platforms by defining a set of static clock structures, +platform_devices, and other data in the board support .c file, and +registering it en-masse in .init_machine(). When DT is used, then +instead of hard coding static devices for each platform, the list of +devices can be obtained by parsing the DT, and allocating device +structures dynamically. + +The simplest case is when .init_machine() is only responsible for +registering a block of platform_devices. A platform_device is a concept +used by Linux for memory or I/O mapped devices which cannot be detected +by hardware, and for 'composite' or 'virtual' devices (more on those +later). While there is no 'platform device' terminology for the DT, +platform devices roughly correspond to device nodes at the root of the +tree and children of simple memory mapped bus nodes. + +About now is a good time to lay out an example. Here is part of the +device tree for the NVIDIA Tegra board. + +/{ + compatible = "nvidia,harmony", "nvidia,tegra20"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&intc>; + + chosen { }; + aliases { }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x40000000>; + }; + + soc { + compatible = "nvidia,tegra20-soc", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + intc: interrupt-controller@50041000 { + compatible = "nvidia,tegra20-gic"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0x50041000 0x1000>, < 0x50040100 0x0100 >; + }; + + serial@70006300 { + compatible = "nvidia,tegra20-uart"; + reg = <0x70006300 0x100>; + interrupts = <122>; + }; + + i2s1: i2s@70002800 { + compatible = "nvidia,tegra20-i2s"; + reg = <0x70002800 0x100>; + interrupts = <77>; + codec = <&wm8903>; + }; + + i2c@7000c000 { + compatible = "nvidia,tegra20-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x7000c000 0x100>; + interrupts = <70>; + + wm8903: codec@1a { + compatible = "wlf,wm8903"; + reg = <0x1a>; + interrupts = <347>; + }; + }; + }; + + sound { + compatible = "nvidia,harmony-sound"; + i2s-controller = <&i2s1>; + i2s-codec = <&wm8903>; + }; +}; + +At .machine_init() time, Tegra board support code will need to look at +this DT and decide which nodes to create platform_devices for. +However, looking at the tree, it is not immediately obvious what kind +of device each node represents, or even if a node represents a device +at all. The /chosen, /aliases, and /memory nodes are informational +nodes that don't describe devices (although arguably memory could be +considered a device). The children of the /soc node are memory mapped +devices, but the codec@1a is an i2c device, and the sound node +represents not a device, but rather how other devices are connected +together to create the audio subsystem. I know what each device is +because I'm familiar with the board design, but how does the kernel +know what to do with each node? + +The trick is that the kernel starts at the root of the tree and looks +for nodes that have a 'compatible' property. First, it is generally +assumed that any node with a 'compatible' property represents a device +of some kind, and second, it can be assumed that any node at the root +of the tree is either directly attached to the processor bus, or is a +miscellaneous system device that cannot be described any other way. +For each of these nodes, Linux allocates and registers a +platform_device, which in turn may get bound to a platform_driver. + +Why is using a platform_device for these nodes a safe assumption? +Well, for the way that Linux models devices, just about all bus_types +assume that its devices are children of a bus controller. For +example, each i2c_client is a child of an i2c_master. Each spi_device +is a child of an SPI bus. Similarly for USB, PCI, MDIO, etc. The +same hierarchy is also found in the DT, where I2C device nodes only +ever appear as children of an I2C bus node. Ditto for SPI, MDIO, USB, +etc. The only devices which do not require a specific type of parent +device are platform_devices (and amba_devices, but more on that +later), which will happily live at the base of the Linux /sys/devices +tree. Therefore, if a DT node is at the root of the tree, then it +really probably is best registered as a platform_device. + +Linux board support code calls of_platform_populate(NULL, NULL, NULL) +to kick off discovery of devices at the root of the tree. The +parameters are all NULL because when starting from the root of the +tree, there is no need to provide a starting node (the first NULL), a +parent struct device (the last NULL), and we're not using a match +table (yet). For a board that only needs to register devices, +.init_machine() can be completely empty except for the +of_platform_populate() call. + +In the Tegra example, this accounts for the /soc and /sound nodes, but +what about the children of the SoC node? Shouldn't they be registered +as platform devices too? For Linux DT support, the generic behaviour +is for child devices to be registered by the parent's device driver at +driver .probe() time. So, an i2c bus device driver will register a +i2c_client for each child node, an SPI bus driver will register +its spi_device children, and similarly for other bus_types. +According to that model, a driver could be written that binds to the +SoC node and simply registers platform_devices for each of its +children. The board support code would allocate and register an SoC +device, a (theoretical) SoC device driver could bind to the SoC device, +and register platform_devices for /soc/interrupt-controller, /soc/serial, +/soc/i2s, and /soc/i2c in its .probe() hook. Easy, right? + +Actually, it turns out that registering children of some +platform_devices as more platform_devices is a common pattern, and the +device tree support code reflects that and makes the above example +simpler. The second argument to of_platform_populate() is an +of_device_id table, and any node that matches an entry in that table +will also get its child nodes registered. In the tegra case, the code +can look something like this: + +static void __init harmony_init_machine(void) +{ + /* ... */ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +"simple-bus" is defined in the ePAPR 1.0 specification as a property +meaning a simple memory mapped bus, so the of_platform_populate() code +could be written to just assume simple-bus compatible nodes will +always be traversed. However, we pass it in as an argument so that +board support code can always override the default behaviour. + +[Need to add discussion of adding i2c/spi/etc child devices] + +Appendix A: AMBA devices +------------------------ + +ARM Primecells are a certain kind of device attached to the ARM AMBA +bus which include some support for hardware detection and power +management. In Linux, struct amba_device and the amba_bus_type is +used to represent Primecell devices. However, the fiddly bit is that +not all devices on an AMBA bus are Primecells, and for Linux it is +typical for both amba_device and platform_device instances to be +siblings of the same bus segment. + +When using the DT, this creates problems for of_platform_populate() +because it must decide whether to register each node as either a +platform_device or an amba_device. This unfortunately complicates the +device creation model a little bit, but the solution turns out not to +be too invasive. If a node is compatible with "arm,amba-primecell", then +of_platform_populate() will register it as an amba_device instead of a +platform_device. -- cgit v1.2.3