[lantiq] bump to v3.8
authorJohn Crispin <john@openwrt.org>
Thu, 14 Mar 2013 18:42:29 +0000 (18:42 +0000)
committerJohn Crispin <john@openwrt.org>
Thu, 14 Mar 2013 18:42:29 +0000 (18:42 +0000)
Signed-off-by: John Crispin <blogic@openwrt.org>
SVN-Revision: 36014

43 files changed:
target/linux/lantiq/Makefile
target/linux/lantiq/config-default [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0001-MTD-m25p80-allow-loading-mtd-name-from-OF.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0002-SPI-MIPS-lantiq-make-use-of-spi_finalize_current_mes.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0003-SPI-MIPS-lantiq-set-SPI_MASTER_HALF_DUPLEX-flag.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0004-Document-devicetree-add-OF-documents-for-lantiq-seri.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0005-PINCTRL-lantiq-pinconf-uses-port-instead-of-pin.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0006-PINCTRL-lantiq-faulty-bit-inversion.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0007-PINCTRL-lantiq-add-pin_config_group_set-support.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0008-PINCTRL-lantiq-add-output-pinconf-parameter.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0009-PINCTRL-lantiq-the-pinconf-OD-parameter-argument-was.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0010-PINCTRL-lantiq-only-probe-available-pad-controllers.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0011-PINCTRL-lantiq-one-of-the-boot-leds-was-defined-inco.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0012-PINCTRL-lantiq-fix-pinconfig-parameters.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0013-PINCTRL-lantiq-add-functionality-to-falcon_pinconf_d.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0014-PINCTRL-lantiq-fix-pin-availability-check.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0015-PINCTRL-lantiq-fix-pin-number-in-ltq_pmx_gpio_reques.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0016-MIPS-lantiq-trivial-typo-fix.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0017-MIPS-lantiq-adds-static-clock-for-PP32.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0018-MIPS-lantiq-improve-pci-reset-gpio-handling.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0019-MIPS-lantiq-rework-external-irq-code.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0020-MIPS-lantiq-adds-4dword-burst-length-for-dma.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0021-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0022-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0023-USB-fix-roothub-for-IFXHCD.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0024-SPI-MIPS-lantiq-adds-spi-xway.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0025-NET-MIPS-lantiq-adds-xrx200-net.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0026-NET-MIPS-lantiq-update-etop-driver-for-devicetree.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0027-NET-PHY-adds-driver-for-lantiq-PHY11G.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0028-NET-lantiq-adds-PHY11G-firmware-blobs.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0029-NET-lantiq-adds-gphy-clock.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0030-MIPS-lantiq-add-pcie-driver.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0031-MIPS-lantiq-adds-minimal-dcdc-driver.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0032-MTD-lantiq-Add-NAND-support-on-Lantiq-Falcon-SoC.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0033-MTD-lantiq-xway-make-nand-actually-work.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0034-MTD-lantiq-handle-NO_XIP-on-cfi0001-flash.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0035-owrt-generic-dtb-image-hack.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0036-owrt-lantiq-dtb-image-hack.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0037-owrt-lantiq-wifi-and-ethernet-eeprom-handling.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0038-owrt-lantiq-handle-vmmc-memory-reservation.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0039-owrt-lantiq-backport-old-timer-code.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0040-owrt-lantiq-add-atm-hack.patch [new file with mode: 0644]
target/linux/lantiq/patches-3.8/0041-owrt-mtd-split.patch [new file with mode: 0644]

index ce0f0dc..83ef83b 100644 (file)
@@ -11,7 +11,7 @@ BOARDNAME:=Lantiq GPON/XWAY/SVIP
 FEATURES:=squashfs jffs2
 SUBTARGETS=xway ase falcon
 
-LINUX_VERSION:=3.7.10
+LINUX_VERSION:=3.8.2
 
 CFLAGS=-Os -pipe -mips32r2 -mtune=mips32r2 -fno-caller-saves -mno-branch-likely
 
diff --git a/target/linux/lantiq/config-default b/target/linux/lantiq/config-default
new file mode 100644 (file)
index 0000000..a86d286
--- /dev/null
@@ -0,0 +1,163 @@
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_ARCH_DISCARD_MEMBLOCK=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_ARCH_SUPPORTS_MSI=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_CEVT_R4K=y
+CONFIG_CEVT_R4K_LIB=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_CPU_GENERIC_DUMP_TLB=y
+CONFIG_CPU_HAS_PREFETCH=y
+CONFIG_CPU_HAS_SYNC=y
+CONFIG_CPU_MIPS32=y
+# CONFIG_CPU_MIPS32_R1 is not set
+CONFIG_CPU_MIPS32_R2=y
+CONFIG_CPU_MIPSR2=y
+CONFIG_CPU_R4K_CACHE_TLB=y
+CONFIG_CPU_R4K_FPU=y
+CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
+CONFIG_CPU_SUPPORTS_HIGHMEM=y
+CONFIG_CSRC_R4K=y
+CONFIG_CSRC_R4K_LIB=y
+# CONFIG_DEBUG_PINCTRL is not set
+CONFIG_DECOMPRESS_LZMA=y
+CONFIG_DMA_NONCOHERENT=y
+CONFIG_DTC=y
+CONFIG_DT_EASY50712=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_ETHERNET_PACKET_MANGLE=y
+CONFIG_GENERIC_ATOMIC64=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_MM_LANTIQ=y
+CONFIG_GPIO_STP_XWAY=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_HARDWARE_WATCHPOINTS=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_WORK=y
+CONFIG_HAVE_MACH_CLKDEV=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HW_HAS_PCI=y
+CONFIG_HW_RANDOM=y
+CONFIG_HZ=250
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_I2C_MUX_PINCTRL is not set
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IRQ_CPU=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_LANTIQ=y
+CONFIG_LANTIQ_ETOP=y
+CONFIG_LANTIQ_PHY=y
+CONFIG_LANTIQ_WDT=y
+CONFIG_LANTIQ_XRX200=y
+CONFIG_LEDS_GPIO=y
+CONFIG_MDIO_BOARDINFO=y
+CONFIG_MIPS=y
+CONFIG_MIPS_L1_CACHE_SHIFT=5
+# CONFIG_MIPS_MACHINE is not set
+CONFIG_MIPS_MT_DISABLED=y
+# CONFIG_MIPS_MT_SMP is not set
+# CONFIG_MIPS_MT_SMTC is not set
+# CONFIG_MIPS_SEAD3 is not set
+# CONFIG_MIPS_VPE_LOADER is not set
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_LANTIQ=y
+CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_UIMAGE_SPLIT=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEED_PER_CPU_KM=y
+CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_DEVICE=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_MTD=y
+CONFIG_OF_NET=y
+CONFIG_OF_PCI=y
+CONFIG_OF_PCI_IRQ=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PCI=y
+# CONFIG_PCIE_LANTIQ is not set
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_LANTIQ=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PHYLIB=y
+CONFIG_PINCONF=y
+CONFIG_PINCTRL=y
+# CONFIG_PINCTRL_EXYNOS4 is not set
+CONFIG_PINCTRL_LANTIQ=y
+# CONFIG_PINCTRL_SAMSUNG is not set
+# CONFIG_PINCTRL_SINGLE is not set
+CONFIG_PINCTRL_XWAY=y
+CONFIG_PINMUX=y
+# CONFIG_PREEMPT_RCU is not set
+CONFIG_PROC_DEVICETREE=y
+CONFIG_PSB6970_PHY=y
+CONFIG_RTL8366RB_PHY=y
+CONFIG_RTL8366_SMI=y
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_LANTIQ=y
+# CONFIG_SOC_AMAZON_SE is not set
+# CONFIG_SOC_FALCON is not set
+# CONFIG_SOC_SVIP is not set
+CONFIG_SOC_TYPE_XWAY=y
+CONFIG_SOC_XWAY=y
+CONFIG_SWAP_IO_SPACE=y
+CONFIG_SWCONFIG=y
+CONFIG_SYS_HAS_CPU_MIPS32_R1=y
+CONFIG_SYS_HAS_CPU_MIPS32_R2=y
+CONFIG_SYS_HAS_EARLY_PRINTK=y
+CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
+CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
+CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
+CONFIG_SYS_SUPPORTS_MULTITHREADING=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_UIDGID_CONVERTED=y
+CONFIG_USB_ARCH_HAS_XHCI=y
+CONFIG_USE_OF=y
+CONFIG_XRX200_PHY_FW=y
+CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/lantiq/patches-3.8/0001-MTD-m25p80-allow-loading-mtd-name-from-OF.patch b/target/linux/lantiq/patches-3.8/0001-MTD-m25p80-allow-loading-mtd-name-from-OF.patch
new file mode 100644 (file)
index 0000000..42cf833
--- /dev/null
@@ -0,0 +1,44 @@
+From e65ecb8f256b5839690a240d9b14e303686f9ede Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Jan 2013 21:11:55 +0100
+Subject: [PATCH 01/40] MTD: m25p80: allow loading mtd name from OF
+
+In accordance with the physmap flash we should honour the linux,mtd-name
+property when deciding what name the mtd device has.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/mtd/devices/m25p80.c |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
+index 4eeeb2d..b12da33 100644
+--- a/drivers/mtd/devices/m25p80.c
++++ b/drivers/mtd/devices/m25p80.c
+@@ -810,10 +810,13 @@ static int m25p_probe(struct spi_device *spi)
+       unsigned                        i;
+       struct mtd_part_parser_data     ppdata;
+       struct device_node __maybe_unused *np = spi->dev.of_node;
++      const char __maybe_unused       *of_mtd_name = NULL;
+ #ifdef CONFIG_MTD_OF_PARTS
+       if (!of_device_is_available(np))
+               return -ENODEV;
++      of_property_read_string(spi->dev.of_node,
++                                      "linux,mtd-name", &of_mtd_name);
+ #endif
+       /* Platform data helps sort out which chip type we have, as
+@@ -889,6 +892,8 @@ static int m25p_probe(struct spi_device *spi)
+       if (data && data->name)
+               flash->mtd.name = data->name;
++      else if (of_mtd_name)
++              flash->mtd.name = of_mtd_name;
+       else
+               flash->mtd.name = dev_name(&spi->dev);
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0002-SPI-MIPS-lantiq-make-use-of-spi_finalize_current_mes.patch b/target/linux/lantiq/patches-3.8/0002-SPI-MIPS-lantiq-make-use-of-spi_finalize_current_mes.patch
new file mode 100644 (file)
index 0000000..1f3e7a1
--- /dev/null
@@ -0,0 +1,31 @@
+From 8b921ffd449431543832b0e76389eb289cc78bb8 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Jan 2013 21:24:17 +0100
+Subject: [PATCH 02/40] SPI: MIPS: lantiq: make use of
+ spi_finalize_current_message
+
+Rather than calling m->complete() directly we choose the sane way and call
+spi_finalize_current_message instead.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/spi/spi-falcon.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
+index 6a6f62e..f9c66c2 100644
+--- a/drivers/spi/spi-falcon.c
++++ b/drivers/spi/spi-falcon.c
+@@ -398,7 +398,7 @@ static int falcon_sflash_xfer_one(struct spi_master *master,
+       }
+       m->status = ret;
+-      m->complete(m->context);
++      spi_finalize_current_message(master);
+       return 0;
+ }
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0003-SPI-MIPS-lantiq-set-SPI_MASTER_HALF_DUPLEX-flag.patch b/target/linux/lantiq/patches-3.8/0003-SPI-MIPS-lantiq-set-SPI_MASTER_HALF_DUPLEX-flag.patch
new file mode 100644 (file)
index 0000000..2bf6dc3
--- /dev/null
@@ -0,0 +1,29 @@
+From a50f7db5b527c317c4bf2ae44a4ccdc8c7e598ab Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Jan 2013 21:26:39 +0100
+Subject: [PATCH 03/40] SPI: MIPS: lantiq: set SPI_MASTER_HALF_DUPLEX flag
+
+Due to hardware limitations of the spi/flash frontend of the EBU we need to set
+the SPI_MASTER_HALF_DUPLEX flag.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/spi/spi-falcon.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
+index f9c66c2..c7a74f0 100644
+--- a/drivers/spi/spi-falcon.c
++++ b/drivers/spi/spi-falcon.c
+@@ -423,6 +423,7 @@ static int falcon_sflash_probe(struct platform_device *pdev)
+       master->mode_bits = SPI_MODE_3;
+       master->num_chipselect = 1;
++      master->flags = SPI_MASTER_HALF_DUPLEX;
+       master->bus_num = -1;
+       master->setup = falcon_sflash_setup;
+       master->prepare_transfer_hardware = falcon_sflash_prepare_xfer;
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0004-Document-devicetree-add-OF-documents-for-lantiq-seri.patch b/target/linux/lantiq/patches-3.8/0004-Document-devicetree-add-OF-documents-for-lantiq-seri.patch
new file mode 100644 (file)
index 0000000..92c35c5
--- /dev/null
@@ -0,0 +1,39 @@
+From 8cd55f4107a3acda4793ed282a114af2f4cb4983 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Fri, 20 Jul 2012 18:58:34 +0200
+Subject: [PATCH 04/40] Document: devicetree: add OF documents for lantiq
+ serial port
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+Cc: Rob Herring <rob.herring@calxeda.com>
+Cc: devicetree-discuss@lists.ozlabs.org
+---
+ .../devicetree/bindings/serial/lantiq_asc.txt          |   16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/serial/lantiq_asc.txt
+
+diff --git a/Documentation/devicetree/bindings/serial/lantiq_asc.txt b/Documentation/devicetree/bindings/serial/lantiq_asc.txt
+new file mode 100644
+index 0000000..5b78591
+--- /dev/null
++++ b/Documentation/devicetree/bindings/serial/lantiq_asc.txt
+@@ -0,0 +1,16 @@
++Lantiq SoC ASC serial controller
++
++Required properties:
++- compatible : Should be "lantiq,asc"
++- reg : Address and length of the register set for the device
++- interrupts: the 3 (tx rx err) interrupt numbers. The interrupt specifier
++  depends on the interrupt-parent interrupt controller.
++
++Example:
++
++asc1: serial@E100C00 {
++      compatible = "lantiq,asc";
++      reg = <0xE100C00 0x400>;
++      interrupt-parent = <&icu0>;
++      interrupts = <112 113 114>;
++};
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0005-PINCTRL-lantiq-pinconf-uses-port-instead-of-pin.patch b/target/linux/lantiq/patches-3.8/0005-PINCTRL-lantiq-pinconf-uses-port-instead-of-pin.patch
new file mode 100644 (file)
index 0000000..88eb804
--- /dev/null
@@ -0,0 +1,92 @@
+From 5e19578b807e7ef6e7baf05fb1f69433d5e74667 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Fri, 30 Nov 2012 21:11:22 +0100
+Subject: [PATCH 05/40] PINCTRL: lantiq: pinconf uses port instead of pin
+
+The XWAY pinctrl driver invalidly uses the port and not the pin number to work
+out the registeres and bits to be set for the opendrain and pullup/down
+resistors.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-xway.c |   28 ++++++++++++++--------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
+index 5f0eb04..69dec9b 100644
+--- a/drivers/pinctrl/pinctrl-xway.c
++++ b/drivers/pinctrl/pinctrl-xway.c
+@@ -441,17 +441,17 @@ static int xway_pinconf_get(struct pinctrl_dev *pctldev,
+               if (port == PORT3)
+                       reg = GPIO3_OD;
+               else
+-                      reg = GPIO_OD(port);
++                      reg = GPIO_OD(pin);
+               *config = LTQ_PINCONF_PACK(param,
+-                      !!gpio_getbit(info->membase[0], reg, PORT_PIN(port)));
++                      !!gpio_getbit(info->membase[0], reg, PORT_PIN(pin)));
+               break;
+       case LTQ_PINCONF_PARAM_PULL:
+               if (port == PORT3)
+                       reg = GPIO3_PUDEN;
+               else
+-                      reg = GPIO_PUDEN(port);
+-              if (!gpio_getbit(info->membase[0], reg, PORT_PIN(port))) {
++                      reg = GPIO_PUDEN(pin);
++              if (!gpio_getbit(info->membase[0], reg, PORT_PIN(pin))) {
+                       *config = LTQ_PINCONF_PACK(param, 0);
+                       break;
+               }
+@@ -459,8 +459,8 @@ static int xway_pinconf_get(struct pinctrl_dev *pctldev,
+               if (port == PORT3)
+                       reg = GPIO3_PUDSEL;
+               else
+-                      reg = GPIO_PUDSEL(port);
+-              if (!gpio_getbit(info->membase[0], reg, PORT_PIN(port)))
++                      reg = GPIO_PUDSEL(pin);
++              if (!gpio_getbit(info->membase[0], reg, PORT_PIN(pin)))
+                       *config = LTQ_PINCONF_PACK(param, 2);
+               else
+                       *config = LTQ_PINCONF_PACK(param, 1);
+@@ -488,29 +488,29 @@ static int xway_pinconf_set(struct pinctrl_dev *pctldev,
+               if (port == PORT3)
+                       reg = GPIO3_OD;
+               else
+-                      reg = GPIO_OD(port);
+-              gpio_setbit(info->membase[0], reg, PORT_PIN(port));
++                      reg = GPIO_OD(pin);
++              gpio_setbit(info->membase[0], reg, PORT_PIN(pin));
+               break;
+       case LTQ_PINCONF_PARAM_PULL:
+               if (port == PORT3)
+                       reg = GPIO3_PUDEN;
+               else
+-                      reg = GPIO_PUDEN(port);
++                      reg = GPIO_PUDEN(pin);
+               if (arg == 0) {
+-                      gpio_clearbit(info->membase[0], reg, PORT_PIN(port));
++                      gpio_clearbit(info->membase[0], reg, PORT_PIN(pin));
+                       break;
+               }
+-              gpio_setbit(info->membase[0], reg, PORT_PIN(port));
++              gpio_setbit(info->membase[0], reg, PORT_PIN(pin));
+               if (port == PORT3)
+                       reg = GPIO3_PUDSEL;
+               else
+-                      reg = GPIO_PUDSEL(port);
++                      reg = GPIO_PUDSEL(pin);
+               if (arg == 1)
+-                      gpio_clearbit(info->membase[0], reg, PORT_PIN(port));
++                      gpio_clearbit(info->membase[0], reg, PORT_PIN(pin));
+               else if (arg == 2)
+-                      gpio_setbit(info->membase[0], reg, PORT_PIN(port));
++                      gpio_setbit(info->membase[0], reg, PORT_PIN(pin));
+               else
+                       dev_err(pctldev->dev, "Invalid pull value %d\n", arg);
+               break;
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0006-PINCTRL-lantiq-faulty-bit-inversion.patch b/target/linux/lantiq/patches-3.8/0006-PINCTRL-lantiq-faulty-bit-inversion.patch
new file mode 100644 (file)
index 0000000..713e2e7
--- /dev/null
@@ -0,0 +1,28 @@
+From 694063bb1049c6ff460f137a8011607103bad81b Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 18:14:23 +0100
+Subject: [PATCH 06/40] PINCTRL: lantiq: faulty bit inversion
+
+The logic of the OD bit was inverted when calling the pinconf get methode.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-xway.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
+index 69dec9b..c49c9db 100644
+--- a/drivers/pinctrl/pinctrl-xway.c
++++ b/drivers/pinctrl/pinctrl-xway.c
+@@ -443,7 +443,7 @@ static int xway_pinconf_get(struct pinctrl_dev *pctldev,
+               else
+                       reg = GPIO_OD(pin);
+               *config = LTQ_PINCONF_PACK(param,
+-                      !!gpio_getbit(info->membase[0], reg, PORT_PIN(pin)));
++                      !gpio_getbit(info->membase[0], reg, PORT_PIN(pin)));
+               break;
+       case LTQ_PINCONF_PARAM_PULL:
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0007-PINCTRL-lantiq-add-pin_config_group_set-support.patch b/target/linux/lantiq/patches-3.8/0007-PINCTRL-lantiq-add-pin_config_group_set-support.patch
new file mode 100644 (file)
index 0000000..67a9b1b
--- /dev/null
@@ -0,0 +1,157 @@
+From 325ba34823f454c35c814d72022ba736ad56a1d4 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 18:21:48 +0100
+Subject: [PATCH 07/40] PINCTRL: lantiq: add pin_config_group_set support
+
+While converting all the boards supported by OpenWrt to OF I noticed that this
+feature is missing. Adding it makes the devicetrees more readable.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-lantiq.c |   54 ++++++++++++++++++++++++--------------
+ drivers/pinctrl/pinctrl-xway.c   |   15 +++++++++++
+ 2 files changed, 49 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-lantiq.c b/drivers/pinctrl/pinctrl-lantiq.c
+index 15f501d..7d11072 100644
+--- a/drivers/pinctrl/pinctrl-lantiq.c
++++ b/drivers/pinctrl/pinctrl-lantiq.c
+@@ -64,11 +64,13 @@ static void ltq_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
+       seq_printf(s, " %s", dev_name(pctldev->dev));
+ }
+-static int ltq_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
++static void ltq_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+                               struct device_node *np,
+                               struct pinctrl_map **map)
+ {
+       struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctldev);
++      struct property *pins = of_find_property(np, "lantiq,pins", NULL);
++      struct property *groups = of_find_property(np, "lantiq,groups", NULL);
+       unsigned long configs[3];
+       unsigned num_configs = 0;
+       struct property *prop;
+@@ -76,8 +78,20 @@ static int ltq_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+       const char *function;
+       int ret, i;
++      if (!pins && !groups) {
++              dev_err(pctldev->dev, "%s defines neither pins nor groups\n",
++                      np->name);
++              return;
++      }
++
++      if (pins && groups) {
++              dev_err(pctldev->dev, "%s defines both pins and groups\n",
++                      np->name);
++              return;
++      }
++
+       ret = of_property_read_string(np, "lantiq,function", &function);
+-      if (!ret) {
++      if (groups && !ret) {
+               of_property_for_each_string(np, "lantiq,groups", prop, group) {
+                       (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
+                       (*map)->name = function;
+@@ -85,11 +99,6 @@ static int ltq_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+                       (*map)->data.mux.function = function;
+                       (*map)++;
+               }
+-              if (of_find_property(np, "lantiq,pins", NULL))
+-                      dev_err(pctldev->dev,
+-                              "%s mixes pins and groups settings\n",
+-                              np->name);
+-              return 0;
+       }
+       for (i = 0; i < info->num_params; i++) {
+@@ -103,7 +112,7 @@ static int ltq_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+       }
+       if (!num_configs)
+-              return -EINVAL;
++              return;
+       of_property_for_each_string(np, "lantiq,pins", prop, pin) {
+               (*map)->data.configs.configs = kmemdup(configs,
+@@ -115,7 +124,16 @@ static int ltq_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+               (*map)->data.configs.num_configs = num_configs;
+               (*map)++;
+       }
+-      return 0;
++      of_property_for_each_string(np, "lantiq,groups", prop, group) {
++              (*map)->data.configs.configs = kmemdup(configs,
++                                      num_configs * sizeof(unsigned long),
++                                      GFP_KERNEL);
++              (*map)->type = PIN_MAP_TYPE_CONFIGS_GROUP;
++              (*map)->name = group;
++              (*map)->data.configs.group_or_pin = group;
++              (*map)->data.configs.num_configs = num_configs;
++              (*map)++;
++      }
+ }
+ static int ltq_pinctrl_dt_subnode_size(struct device_node *np)
+@@ -135,23 +153,19 @@ static int ltq_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+ {
+       struct pinctrl_map *tmp;
+       struct device_node *np;
+-      int ret;
++      int max_maps = 0;
+-      *num_maps = 0;
+       for_each_child_of_node(np_config, np)
+-              *num_maps += ltq_pinctrl_dt_subnode_size(np);
+-      *map = kzalloc(*num_maps * sizeof(struct pinctrl_map), GFP_KERNEL);
++              max_maps += ltq_pinctrl_dt_subnode_size(np);
++      *map = kzalloc(max_maps * sizeof(struct pinctrl_map) * 2, GFP_KERNEL);
+       if (!*map)
+               return -ENOMEM;
+       tmp = *map;
+-      for_each_child_of_node(np_config, np) {
+-              ret = ltq_pinctrl_dt_subnode_to_map(pctldev, np, &tmp);
+-              if (ret < 0) {
+-                      ltq_pinctrl_dt_free_map(pctldev, *map, *num_maps);
+-                      return ret;
+-              }
+-      }
++      for_each_child_of_node(np_config, np)
++              ltq_pinctrl_dt_subnode_to_map(pctldev, np, &tmp);
++      *num_maps = ((int)(tmp - *map));
++
+       return 0;
+ }
+diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
+index c49c9db..aa4c8b8 100644
+--- a/drivers/pinctrl/pinctrl-xway.c
++++ b/drivers/pinctrl/pinctrl-xway.c
+@@ -522,9 +522,24 @@ static int xway_pinconf_set(struct pinctrl_dev *pctldev,
+       return 0;
+ }
++int xway_pinconf_group_set(struct pinctrl_dev *pctldev,
++                      unsigned selector,
++                      unsigned long config)
++{
++      struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctldev);
++      int i, ret = 0;
++
++      for (i = 0; i < info->grps[selector].npins && !ret; i++)
++              ret = xway_pinconf_set(pctldev,
++                              info->grps[selector].pins[i], config);
++
++      return ret;
++}
++
+ static struct pinconf_ops xway_pinconf_ops = {
+       .pin_config_get = xway_pinconf_get,
+       .pin_config_set = xway_pinconf_set,
++      .pin_config_group_set = xway_pinconf_group_set,
+ };
+ static struct pinctrl_desc xway_pctrl_desc = {
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0008-PINCTRL-lantiq-add-output-pinconf-parameter.patch b/target/linux/lantiq/patches-3.8/0008-PINCTRL-lantiq-add-output-pinconf-parameter.patch
new file mode 100644 (file)
index 0000000..30e83ff
--- /dev/null
@@ -0,0 +1,68 @@
+From f9441b4f98b5b28f3d2cbebd0a70b227c35451d9 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 18:33:39 +0100
+Subject: [PATCH 08/40] PINCTRL: lantiq: add output pinconf parameter
+
+While converting the boards inside OpenWrt to OF I noticed that the we are
+missing a pinconf parameter to set a pin to output.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-lantiq.h |    1 +
+ drivers/pinctrl/pinctrl-xway.c   |   14 ++++++++++++++
+ 2 files changed, 15 insertions(+)
+
+diff --git a/drivers/pinctrl/pinctrl-lantiq.h b/drivers/pinctrl/pinctrl-lantiq.h
+index 4419d32..6d07f02 100644
+--- a/drivers/pinctrl/pinctrl-lantiq.h
++++ b/drivers/pinctrl/pinctrl-lantiq.h
+@@ -34,6 +34,7 @@ enum ltq_pinconf_param {
+       LTQ_PINCONF_PARAM_OPEN_DRAIN,
+       LTQ_PINCONF_PARAM_DRIVE_CURRENT,
+       LTQ_PINCONF_PARAM_SLEW_RATE,
++      LTQ_PINCONF_PARAM_OUTPUT,
+ };
+ struct ltq_cfg_param {
+diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
+index aa4c8b8..b23b895 100644
+--- a/drivers/pinctrl/pinctrl-xway.c
++++ b/drivers/pinctrl/pinctrl-xway.c
+@@ -466,6 +466,11 @@ static int xway_pinconf_get(struct pinctrl_dev *pctldev,
+                       *config = LTQ_PINCONF_PACK(param, 1);
+               break;
++      case LTQ_PINCONF_PARAM_OUTPUT:
++              reg = GPIO_DIR(pin);
++              *config = LTQ_PINCONF_PACK(param,
++                      gpio_getbit(info->membase[0], reg, PORT_PIN(pin)));
++              break;
+       default:
+               dev_err(pctldev->dev, "Invalid config param %04x\n", param);
+               return -ENOTSUPP;
+@@ -515,6 +520,14 @@ static int xway_pinconf_set(struct pinctrl_dev *pctldev,
+                       dev_err(pctldev->dev, "Invalid pull value %d\n", arg);
+               break;
++      case LTQ_PINCONF_PARAM_OUTPUT:
++              reg = GPIO_DIR(pin);
++              if (arg == 0)
++                      gpio_clearbit(info->membase[0], reg, PORT_PIN(pin));
++              else
++                      gpio_setbit(info->membase[0], reg, PORT_PIN(pin));
++              break;
++
+       default:
+               dev_err(pctldev->dev, "Invalid config param %04x\n", param);
+               return -ENOTSUPP;
+@@ -573,6 +586,7 @@ static inline int xway_mux_apply(struct pinctrl_dev *pctrldev,
+ static const struct ltq_cfg_param xway_cfg_params[] = {
+       {"lantiq,pull",         LTQ_PINCONF_PARAM_PULL},
+       {"lantiq,open-drain",   LTQ_PINCONF_PARAM_OPEN_DRAIN},
++      {"lantiq,output",       LTQ_PINCONF_PARAM_OUTPUT},
+ };
+ static struct ltq_pinmux_info xway_info = {
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0009-PINCTRL-lantiq-the-pinconf-OD-parameter-argument-was.patch b/target/linux/lantiq/patches-3.8/0009-PINCTRL-lantiq-the-pinconf-OD-parameter-argument-was.patch
new file mode 100644 (file)
index 0000000..ca546e7
--- /dev/null
@@ -0,0 +1,33 @@
+From 879fe8a24167983d2923f635cb37dc9e02f6cf57 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 18:39:34 +0100
+Subject: [PATCH 09/40] PINCTRL: lantiq: the pinconf OD parameter argument was
+ ignored
+
+When setting the OpenDrain bit we should really honour the argument passed
+inside the devicetree.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-xway.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
+index b23b895..53cb6a3 100644
+--- a/drivers/pinctrl/pinctrl-xway.c
++++ b/drivers/pinctrl/pinctrl-xway.c
+@@ -494,7 +494,10 @@ static int xway_pinconf_set(struct pinctrl_dev *pctldev,
+                       reg = GPIO3_OD;
+               else
+                       reg = GPIO_OD(pin);
+-              gpio_setbit(info->membase[0], reg, PORT_PIN(pin));
++              if (arg == 0)
++                      gpio_setbit(info->membase[0], reg, PORT_PIN(pin));
++              else
++                      gpio_clearbit(info->membase[0], reg, PORT_PIN(pin));
+               break;
+       case LTQ_PINCONF_PARAM_PULL:
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0010-PINCTRL-lantiq-only-probe-available-pad-controllers.patch b/target/linux/lantiq/patches-3.8/0010-PINCTRL-lantiq-only-probe-available-pad-controllers.patch
new file mode 100644 (file)
index 0000000..82249ea
--- /dev/null
@@ -0,0 +1,30 @@
+From 997390a8802e21b4b57d0ffcd91ad64651f1c2bf Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 20:02:06 +0100
+Subject: [PATCH 10/40] PINCTRL: lantiq: only probe available pad controllers
+
+The template falcon.dtsi lists all 6 pad controllers that can be loaded. Only
+probe those that have status = "okay"; inside the dts file.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-falcon.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c
+index 8ed20e8..6331c5c 100644
+--- a/drivers/pinctrl/pinctrl-falcon.c
++++ b/drivers/pinctrl/pinctrl-falcon.c
+@@ -398,6 +398,9 @@ static int pinctrl_falcon_probe(struct platform_device *pdev)
+               u32 avail;
+               int pins;
++              if (!of_device_is_available(np))
++                      continue;
++
+               if (!ppdev) {
+                       dev_err(&pdev->dev, "failed to find pad pdev\n");
+                       continue;
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0011-PINCTRL-lantiq-one-of-the-boot-leds-was-defined-inco.patch b/target/linux/lantiq/patches-3.8/0011-PINCTRL-lantiq-one-of-the-boot-leds-was-defined-inco.patch
new file mode 100644 (file)
index 0000000..ca8293f
--- /dev/null
@@ -0,0 +1,30 @@
+From b416a5be614733792cf5fbfce31b6733c37ffa3f Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 20:07:51 +0100
+Subject: [PATCH 11/40] PINCTRL: lantiq: one of the boot leds was defined
+ incorrectly
+
+On the Falcon SoC the bootleds are located on pins 9->14.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-falcon.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c
+index 6331c5c..249a405 100644
+--- a/drivers/pinctrl/pinctrl-falcon.c
++++ b/drivers/pinctrl/pinctrl-falcon.c
+@@ -170,7 +170,7 @@ static const unsigned pins_ntr[] = {GPIO4};
+ static const unsigned pins_ntr8k[] = {GPIO5};
+ static const unsigned pins_hrst[] = {GPIO6};
+ static const unsigned pins_mdio[] = {GPIO7, GPIO8};
+-static const unsigned pins_bled[] = {GPIO7, GPIO10, GPIO11,
++static const unsigned pins_bled[] = {GPIO9, GPIO10, GPIO11,
+                                       GPIO12, GPIO13, GPIO14};
+ static const unsigned pins_asc0[] = {GPIO32, GPIO33};
+ static const unsigned pins_spi[] = {GPIO34, GPIO35, GPIO36};
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0012-PINCTRL-lantiq-fix-pinconfig-parameters.patch b/target/linux/lantiq/patches-3.8/0012-PINCTRL-lantiq-fix-pinconfig-parameters.patch
new file mode 100644 (file)
index 0000000..4ac8d5a
--- /dev/null
@@ -0,0 +1,30 @@
+From ea1a25a2ca058e4b35c5763774e7fad6ab928418 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 20:10:20 +0100
+Subject: [PATCH 12/40] PINCTRL: lantiq: fix pinconfig parameters
+
+The Falcon driver only defined the pinconf parameters but did not pass them
+properly to the underlying api.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-falcon.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c
+index 249a405..c5a9868 100644
+--- a/drivers/pinctrl/pinctrl-falcon.c
++++ b/drivers/pinctrl/pinctrl-falcon.c
+@@ -360,6 +360,8 @@ static const struct ltq_cfg_param falcon_cfg_params[] = {
+ static struct ltq_pinmux_info falcon_info = {
+       .desc           = &falcon_pctrl_desc,
+       .apply_mux      = falcon_mux_apply,
++      .params         = falcon_cfg_params,
++      .num_params     = ARRAY_SIZE(falcon_cfg_params),
+ };
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0013-PINCTRL-lantiq-add-functionality-to-falcon_pinconf_d.patch b/target/linux/lantiq/patches-3.8/0013-PINCTRL-lantiq-add-functionality-to-falcon_pinconf_d.patch
new file mode 100644 (file)
index 0000000..0233dd0
--- /dev/null
@@ -0,0 +1,60 @@
+From 98d06bc9e2a2f534aaaf4229aaf871e394234d20 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 20:13:09 +0100
+Subject: [PATCH 13/40] PINCTRL: lantiq: add functionality to
+ falcon_pinconf_dbg_show
+
+The current code only has a stub for falcon_pinconf_dbg_show. This patch adds
+proper functionality.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-falcon.c |   31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c
+index c5a9868..4a0d54a 100644
+--- a/drivers/pinctrl/pinctrl-falcon.c
++++ b/drivers/pinctrl/pinctrl-falcon.c
+@@ -315,6 +315,37 @@ static int falcon_pinconf_set(struct pinctrl_dev *pctrldev,
+ static void falcon_pinconf_dbg_show(struct pinctrl_dev *pctrldev,
+                       struct seq_file *s, unsigned offset)
+ {
++      unsigned long config;
++      struct pin_desc *desc;
++
++      struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
++      int port = PORT(offset);
++
++      seq_printf(s, " (port %d) mux %d -- ", port,
++              pad_r32(info->membase[port], LTQ_PADC_MUX(PORT_PIN(offset))));
++
++      config = LTQ_PINCONF_PACK(LTQ_PINCONF_PARAM_PULL, 0);
++      if (!falcon_pinconf_get(pctrldev, offset, &config))
++              seq_printf(s, "pull %d ",
++                      (int)LTQ_PINCONF_UNPACK_ARG(config));
++
++      config = LTQ_PINCONF_PACK(LTQ_PINCONF_PARAM_DRIVE_CURRENT, 0);
++      if (!falcon_pinconf_get(pctrldev, offset, &config))
++              seq_printf(s, "drive-current %d ",
++                      (int)LTQ_PINCONF_UNPACK_ARG(config));
++
++      config = LTQ_PINCONF_PACK(LTQ_PINCONF_PARAM_SLEW_RATE, 0);
++      if (!falcon_pinconf_get(pctrldev, offset, &config))
++              seq_printf(s, "slew-rate %d ",
++                      (int)LTQ_PINCONF_UNPACK_ARG(config));
++
++      desc = pin_desc_get(pctrldev, offset);
++      if (desc) {
++              if (desc->gpio_owner)
++                      seq_printf(s, " owner: %s", desc->gpio_owner);
++      } else {
++              seq_printf(s, " not registered");
++      }
+ }
+ static void falcon_pinconf_group_dbg_show(struct pinctrl_dev *pctrldev,
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0014-PINCTRL-lantiq-fix-pin-availability-check.patch b/target/linux/lantiq/patches-3.8/0014-PINCTRL-lantiq-fix-pin-availability-check.patch
new file mode 100644 (file)
index 0000000..d1d2ff2
--- /dev/null
@@ -0,0 +1,43 @@
+From 51d5029bd9cd0ff85e1df87a4df57e544c52dc34 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 20:16:22 +0100
+Subject: [PATCH 14/40] PINCTRL: lantiq: fix pin availability check
+
+The clock needs to be activated for the check to work. In order to be compatible
+with future silicon make sure that at least 1 pin is available before probing
+the pad controller.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-falcon.c |   11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c
+index 4a0d54a..de9d1db 100644
+--- a/drivers/pinctrl/pinctrl-falcon.c
++++ b/drivers/pinctrl/pinctrl-falcon.c
+@@ -455,12 +455,17 @@ static int pinctrl_falcon_probe(struct platform_device *pdev)
+                               *bank);
+                       return -ENOMEM;
+               }
++              clk_activate(falcon_info.clk[*bank]);
+               avail = pad_r32(falcon_info.membase[*bank],
+                                       LTQ_PADC_AVAIL);
+               pins = fls(avail);
+-              lantiq_load_pin_desc(&falcon_pads[pad_count], *bank, pins);
+-              pad_count += pins;
+-              clk_enable(falcon_info.clk[*bank]);
++              if (pins) {
++                      lantiq_load_pin_desc(&falcon_pads[pad_count],
++                                                              *bank, pins);
++                      pad_count += pins;
++              } else {
++                      clk_deactivate(falcon_info.clk[*bank]);
++              }
+               dev_dbg(&pdev->dev, "found %s with %d pads\n",
+                               res.name, pins);
+       }
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0015-PINCTRL-lantiq-fix-pin-number-in-ltq_pmx_gpio_reques.patch b/target/linux/lantiq/patches-3.8/0015-PINCTRL-lantiq-fix-pin-number-in-ltq_pmx_gpio_reques.patch
new file mode 100644 (file)
index 0000000..9ea301b
--- /dev/null
@@ -0,0 +1,31 @@
+From 363f0d3dc146215744363db97606a357310fad3d Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Jan 2013 21:23:22 +0100
+Subject: [PATCH 15/40] PINCTRL: lantiq: fix pin number in
+ ltq_pmx_gpio_request_enable
+
+The mapping logic inside ltq_pmx_gpio_request_enable() was broken. This only
+effected Falcon SoC.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/pinctrl/pinctrl-lantiq.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/pinctrl/pinctrl-lantiq.c b/drivers/pinctrl/pinctrl-lantiq.c
+index 7d11072..a703846 100644
+--- a/drivers/pinctrl/pinctrl-lantiq.c
++++ b/drivers/pinctrl/pinctrl-lantiq.c
+@@ -294,7 +294,7 @@ static int ltq_pmx_gpio_request_enable(struct pinctrl_dev *pctrldev,
+                               unsigned pin)
+ {
+       struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+-      int mfp = match_mfp(info, pin + (range->id * 32));
++      int mfp = match_mfp(info, pin);
+       int pin_func;
+       if (mfp < 0) {
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0016-MIPS-lantiq-trivial-typo-fix.patch b/target/linux/lantiq/patches-3.8/0016-MIPS-lantiq-trivial-typo-fix.patch
new file mode 100644 (file)
index 0000000..a10aed8
--- /dev/null
@@ -0,0 +1,29 @@
+From cc64558db2d91e89e1de174b6ad4a159ac27bf8b Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Sat, 19 Jan 2013 08:54:23 +0000
+Subject: [PATCH 16/40] MIPS: lantiq: trivial typo fix
+
+"nodes" is written with a single "s"
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+Patchwork: http://patchwork.linux-mips.org/patch/4814/
+---
+ arch/mips/lantiq/xway/sysctrl.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
+index 3925e66..1aaa726 100644
+--- a/arch/mips/lantiq/xway/sysctrl.c
++++ b/arch/mips/lantiq/xway/sysctrl.c
+@@ -305,7 +305,7 @@ void __init ltq_soc_init(void)
+       /* check if all the core register ranges are available */
+       if (!np_pmu || !np_cgu || !np_ebu)
+-              panic("Failed to load core nodess from devicetree");
++              panic("Failed to load core nodes from devicetree");
+       if (of_address_to_resource(np_pmu, 0, &res_pmu) ||
+                       of_address_to_resource(np_cgu, 0, &res_cgu) ||
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0017-MIPS-lantiq-adds-static-clock-for-PP32.patch b/target/linux/lantiq/patches-3.8/0017-MIPS-lantiq-adds-static-clock-for-PP32.patch
new file mode 100644 (file)
index 0000000..07ae757
--- /dev/null
@@ -0,0 +1,219 @@
+From 46a704b1b093f4053eceaf8e5f0ab54949afa532 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Sat, 19 Jan 2013 08:54:24 +0000
+Subject: [PATCH 17/40] MIPS: lantiq: adds static clock for PP32
+
+The Lantiq DSL SoCs have an internal networking processor. Add code to read
+the static clock rate.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+Patchwork: http://patchwork.linux-mips.org/patch/4815/
+---
+ arch/mips/include/asm/mach-lantiq/lantiq.h |    1 +
+ arch/mips/lantiq/clk.c                     |   12 ++++++--
+ arch/mips/lantiq/clk.h                     |    7 ++++-
+ arch/mips/lantiq/falcon/sysctrl.c          |    4 +--
+ arch/mips/lantiq/xway/clk.c                |   43 ++++++++++++++++++++++++++++
+ arch/mips/lantiq/xway/sysctrl.c            |   12 ++++----
+ 6 files changed, 69 insertions(+), 10 deletions(-)
+
+diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h
+index 5e8a6e9..76be7a0 100644
+--- a/arch/mips/include/asm/mach-lantiq/lantiq.h
++++ b/arch/mips/include/asm/mach-lantiq/lantiq.h
+@@ -41,6 +41,7 @@ extern void clk_deactivate(struct clk *clk);
+ extern struct clk *clk_get_cpu(void);
+ extern struct clk *clk_get_fpi(void);
+ extern struct clk *clk_get_io(void);
++extern struct clk *clk_get_ppe(void);
+ /* find out what bootsource we have */
+ extern unsigned char ltq_boot_select(void);
+diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c
+index ce2f129..d903560 100644
+--- a/arch/mips/lantiq/clk.c
++++ b/arch/mips/lantiq/clk.c
+@@ -26,13 +26,15 @@
+ #include "prom.h"
+ /* lantiq socs have 3 static clocks */
+-static struct clk cpu_clk_generic[3];
++static struct clk cpu_clk_generic[4];
+-void clkdev_add_static(unsigned long cpu, unsigned long fpi, unsigned long io)
++void clkdev_add_static(unsigned long cpu, unsigned long fpi,
++                      unsigned long io, unsigned long ppe)
+ {
+       cpu_clk_generic[0].rate = cpu;
+       cpu_clk_generic[1].rate = fpi;
+       cpu_clk_generic[2].rate = io;
++      cpu_clk_generic[3].rate = ppe;
+ }
+ struct clk *clk_get_cpu(void)
+@@ -51,6 +53,12 @@ struct clk *clk_get_io(void)
+       return &cpu_clk_generic[2];
+ }
++struct clk *clk_get_ppe(void)
++{
++      return &cpu_clk_generic[3];
++}
++EXPORT_SYMBOL_GPL(clk_get_ppe);
++
+ static inline int clk_good(struct clk *clk)
+ {
+       return clk && !IS_ERR(clk);
+diff --git a/arch/mips/lantiq/clk.h b/arch/mips/lantiq/clk.h
+index fa67060..77e4bdb 100644
+--- a/arch/mips/lantiq/clk.h
++++ b/arch/mips/lantiq/clk.h
+@@ -27,12 +27,15 @@
+ #define CLOCK_167M    166666667
+ #define CLOCK_196_608M        196608000
+ #define CLOCK_200M    200000000
++#define CLOCK_222M    222000000
++#define CLOCK_240M    240000000
+ #define CLOCK_250M    250000000
+ #define CLOCK_266M    266666666
+ #define CLOCK_300M    300000000
+ #define CLOCK_333M    333333333
+ #define CLOCK_393M    393215332
+ #define CLOCK_400M    400000000
++#define CLOCK_450M    450000000
+ #define CLOCK_500M    500000000
+ #define CLOCK_600M    600000000
+@@ -64,15 +67,17 @@ struct clk {
+ };
+ extern void clkdev_add_static(unsigned long cpu, unsigned long fpi,
+-                              unsigned long io);
++                              unsigned long io, unsigned long ppe);
+ extern unsigned long ltq_danube_cpu_hz(void);
+ extern unsigned long ltq_danube_fpi_hz(void);
++extern unsigned long ltq_danube_pp32_hz(void);
+ extern unsigned long ltq_ar9_cpu_hz(void);
+ extern unsigned long ltq_ar9_fpi_hz(void);
+ extern unsigned long ltq_vr9_cpu_hz(void);
+ extern unsigned long ltq_vr9_fpi_hz(void);
++extern unsigned long ltq_vr9_pp32_hz(void);
+ #endif
+diff --git a/arch/mips/lantiq/falcon/sysctrl.c b/arch/mips/lantiq/falcon/sysctrl.c
+index 2d4ced3..ff4894a 100644
+--- a/arch/mips/lantiq/falcon/sysctrl.c
++++ b/arch/mips/lantiq/falcon/sysctrl.c
+@@ -241,9 +241,9 @@ void __init ltq_soc_init(void)
+       /* get our 3 static rates for cpu, fpi and io clocks */
+       if (ltq_sys1_r32(SYS1_CPU0CC) & CPU0CC_CPUDIV)
+-              clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M);
++              clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M, 0);
+       else
+-              clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M);
++              clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M, 0);
+       /* add our clock domains */
+       clkdev_add_sys("1d810000.gpio", SYSCTL_SYSETH, ACTS_P0);
+diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c
+index 9aa17f7..1ab576d 100644
+--- a/arch/mips/lantiq/xway/clk.c
++++ b/arch/mips/lantiq/xway/clk.c
+@@ -53,6 +53,29 @@ unsigned long ltq_danube_cpu_hz(void)
+       }
+ }
++unsigned long ltq_danube_pp32_hz(void)
++{
++      unsigned int clksys = (ltq_cgu_r32(CGU_SYS) >> 7) & 3;
++      unsigned long clk;
++
++      switch (clksys) {
++      case 1:
++              clk = CLOCK_240M;
++              break;
++      case 2:
++              clk = CLOCK_222M;
++              break;
++      case 3:
++              clk = CLOCK_133M;
++              break;
++      default:
++              clk = CLOCK_266M;
++              break;
++      }
++
++      return clk;
++}
++
+ unsigned long ltq_ar9_sys_hz(void)
+ {
+       if (((ltq_cgu_r32(CGU_SYS) >> 3) & 0x3) == 0x2)
+@@ -149,3 +172,23 @@ unsigned long ltq_vr9_fpi_hz(void)
+       return clk;
+ }
++
++unsigned long ltq_vr9_pp32_hz(void)
++{
++      unsigned int clksys = (ltq_cgu_r32(CGU_SYS) >> 16) & 3;
++      unsigned long clk;
++
++      switch (clksys) {
++      case 1:
++              clk = CLOCK_450M;
++              break;
++      case 2:
++              clk = CLOCK_300M;
++              break;
++      default:
++              clk = CLOCK_500M;
++              break;
++      }
++
++      return clk;
++}
+diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
+index 1aaa726..3390fcd 100644
+--- a/arch/mips/lantiq/xway/sysctrl.c
++++ b/arch/mips/lantiq/xway/sysctrl.c
+@@ -356,14 +356,16 @@ void __init ltq_soc_init(void)
+       if (of_machine_is_compatible("lantiq,ase")) {
+               if (ltq_cgu_r32(CGU_SYS) & (1 << 5))
+-                      clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M);
++                      clkdev_add_static(CLOCK_266M, CLOCK_133M,
++                                              CLOCK_133M, CLOCK_266M);
+               else
+-                      clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M);
++                      clkdev_add_static(CLOCK_133M, CLOCK_133M,
++                                              CLOCK_133M, CLOCK_133M);
+               clkdev_add_cgu("1e180000.etop", "ephycgu", CGU_EPHY),
+               clkdev_add_pmu("1e180000.etop", "ephy", 0, PMU_EPHY);
+       } else if (of_machine_is_compatible("lantiq,vr9")) {
+               clkdev_add_static(ltq_vr9_cpu_hz(), ltq_vr9_fpi_hz(),
+-                              ltq_vr9_fpi_hz());
++                              ltq_vr9_fpi_hz(), ltq_vr9_pp32_hz());
+               clkdev_add_pmu("1d900000.pcie", "phy", 1, PMU1_PCIE_PHY);
+               clkdev_add_pmu("1d900000.pcie", "bus", 0, PMU_PCIE_CLK);
+               clkdev_add_pmu("1d900000.pcie", "msi", 1, PMU1_PCIE_MSI);
+@@ -376,10 +378,10 @@ void __init ltq_soc_init(void)
+                               PMU_PPE_QSB | PMU_PPE_TOP);
+       } else if (of_machine_is_compatible("lantiq,ar9")) {
+               clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(),
+-                              ltq_ar9_fpi_hz());
++                              ltq_ar9_fpi_hz(), CLOCK_250M);
+               clkdev_add_pmu("1e180000.etop", "switch", 0, PMU_SWITCH);
+       } else {
+               clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(),
+-                              ltq_danube_fpi_hz());
++                              ltq_danube_fpi_hz(), ltq_danube_pp32_hz());
+       }
+ }
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0018-MIPS-lantiq-improve-pci-reset-gpio-handling.patch b/target/linux/lantiq/patches-3.8/0018-MIPS-lantiq-improve-pci-reset-gpio-handling.patch
new file mode 100644 (file)
index 0000000..c41e8e1
--- /dev/null
@@ -0,0 +1,40 @@
+From b6684dd3036513e4f91986fe982356512458f711 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Sat, 19 Jan 2013 08:54:26 +0000
+Subject: [PATCH 18/40] MIPS: lantiq: improve pci reset gpio handling
+
+We need to make sure that the reset gpio is available and also set a sane
+default state.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+Patchwork: http://patchwork.linux-mips.org/patch/4817/
+---
+ arch/mips/pci/pci-lantiq.c |   12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c
+index 9568178..f32664b 100644
+--- a/arch/mips/pci/pci-lantiq.c
++++ b/arch/mips/pci/pci-lantiq.c
+@@ -129,8 +129,16 @@ static int ltq_pci_startup(struct platform_device *pdev)
+       /* setup reset gpio used by pci */
+       reset_gpio = of_get_named_gpio(node, "gpio-reset", 0);
+-      if (gpio_is_valid(reset_gpio))
+-              devm_gpio_request(&pdev->dev, reset_gpio, "pci-reset");
++      if (gpio_is_valid(reset_gpio)) {
++              int ret = devm_gpio_request(&pdev->dev,
++                                              reset_gpio, "pci-reset");
++              if (ret) {
++                      dev_err(&pdev->dev,
++                              "failed to request gpio %d\n", reset_gpio);
++                      return ret;
++              }
++              gpio_direction_output(reset_gpio, 1);
++      }
+       /* enable auto-switching between PCI and EBU */
+       ltq_pci_w32(0xa, PCI_CR_CLK_CTRL);
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0019-MIPS-lantiq-rework-external-irq-code.patch b/target/linux/lantiq/patches-3.8/0019-MIPS-lantiq-rework-external-irq-code.patch
new file mode 100644 (file)
index 0000000..f976765
--- /dev/null
@@ -0,0 +1,216 @@
+From d8f6bf3fb606ee8fdd5b7aff4aedb54e30792b84 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Sat, 19 Jan 2013 08:54:27 +0000
+Subject: [PATCH 19/40] MIPS: lantiq: rework external irq code
+
+This code makes the irqs used by the EIU loadable from the DT. Additionally we
+add a helper that allows the pinctrl layer to map external irqs to real irq
+numbers.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+Patchwork: http://patchwork.linux-mips.org/patch/4818/
+---
+ arch/mips/include/asm/mach-lantiq/lantiq.h |    1 +
+ arch/mips/lantiq/irq.c                     |  105 +++++++++++++++++++---------
+ 2 files changed, 74 insertions(+), 32 deletions(-)
+
+diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h
+index 76be7a0..f196cce 100644
+--- a/arch/mips/include/asm/mach-lantiq/lantiq.h
++++ b/arch/mips/include/asm/mach-lantiq/lantiq.h
+@@ -34,6 +34,7 @@ extern spinlock_t ebu_lock;
+ extern void ltq_disable_irq(struct irq_data *data);
+ extern void ltq_mask_and_ack_irq(struct irq_data *data);
+ extern void ltq_enable_irq(struct irq_data *data);
++extern int ltq_eiu_get_irq(int exin);
+ /* clock handling */
+ extern int clk_activate(struct clk *clk);
+diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c
+index a7935bf..5119487 100644
+--- a/arch/mips/lantiq/irq.c
++++ b/arch/mips/lantiq/irq.c
+@@ -33,17 +33,10 @@
+ /* register definitions - external irqs */
+ #define LTQ_EIU_EXIN_C                0x0000
+ #define LTQ_EIU_EXIN_INIC     0x0004
++#define LTQ_EIU_EXIN_INC      0x0008
+ #define LTQ_EIU_EXIN_INEN     0x000C
+-/* irq numbers used by the external interrupt unit (EIU) */
+-#define LTQ_EIU_IR0           (INT_NUM_IM4_IRL0 + 30)
+-#define LTQ_EIU_IR1           (INT_NUM_IM3_IRL0 + 31)
+-#define LTQ_EIU_IR2           (INT_NUM_IM1_IRL0 + 26)
+-#define LTQ_EIU_IR3           INT_NUM_IM1_IRL0
+-#define LTQ_EIU_IR4           (INT_NUM_IM1_IRL0 + 1)
+-#define LTQ_EIU_IR5           (INT_NUM_IM1_IRL0 + 2)
+-#define LTQ_EIU_IR6           (INT_NUM_IM2_IRL0 + 30)
+-#define XWAY_EXIN_COUNT               3
++/* number of external interrupts */
+ #define MAX_EIU                       6
+ /* the performance counter */
+@@ -72,20 +65,19 @@
+ int gic_present;
+ #endif
+-static unsigned short ltq_eiu_irq[MAX_EIU] = {
+-      LTQ_EIU_IR0,
+-      LTQ_EIU_IR1,
+-      LTQ_EIU_IR2,
+-      LTQ_EIU_IR3,
+-      LTQ_EIU_IR4,
+-      LTQ_EIU_IR5,
+-};
+-
+ static int exin_avail;
++static struct resource ltq_eiu_irq[MAX_EIU];
+ static void __iomem *ltq_icu_membase[MAX_IM];
+ static void __iomem *ltq_eiu_membase;
+ static struct irq_domain *ltq_domain;
++int ltq_eiu_get_irq(int exin)
++{
++      if (exin < exin_avail)
++              return ltq_eiu_irq[exin].start;
++      return -1;
++}
++
+ void ltq_disable_irq(struct irq_data *d)
+ {
+       u32 ier = LTQ_ICU_IM0_IER;
+@@ -128,19 +120,65 @@ void ltq_enable_irq(struct irq_data *d)
+       ltq_icu_w32(im, ltq_icu_r32(im, ier) | BIT(offset), ier);
+ }
++static int ltq_eiu_settype(struct irq_data *d, unsigned int type)
++{
++      int i;
++
++      for (i = 0; i < MAX_EIU; i++) {
++              if (d->hwirq == ltq_eiu_irq[i].start) {
++                      int val = 0;
++                      int edge = 0;
++
++                      switch (type) {
++                      case IRQF_TRIGGER_NONE:
++                              break;
++                      case IRQF_TRIGGER_RISING:
++                              val = 1;
++                              edge = 1;
++                              break;
++                      case IRQF_TRIGGER_FALLING:
++                              val = 2;
++                              edge = 1;
++                              break;
++                      case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
++                              val = 3;
++                              edge = 1;
++                              break;
++                      case IRQF_TRIGGER_HIGH:
++                              val = 5;
++                              break;
++                      case IRQF_TRIGGER_LOW:
++                              val = 6;
++                              break;
++                      default:
++                              pr_err("invalid type %d for irq %ld\n",
++                                      type, d->hwirq);
++                              return -EINVAL;
++                      }
++
++                      if (edge)
++                              irq_set_handler(d->hwirq, handle_edge_irq);
++
++                      ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
++                              (val << (i * 4)), LTQ_EIU_EXIN_C);
++              }
++      }
++
++      return 0;
++}
++
+ static unsigned int ltq_startup_eiu_irq(struct irq_data *d)
+ {
+       int i;
+       ltq_enable_irq(d);
+       for (i = 0; i < MAX_EIU; i++) {
+-              if (d->hwirq == ltq_eiu_irq[i]) {
+-                      /* low level - we should really handle set_type */
+-                      ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
+-                              (0x6 << (i * 4)), LTQ_EIU_EXIN_C);
++              if (d->hwirq == ltq_eiu_irq[i].start) {
++                      /* by default we are low level triggered */
++                      ltq_eiu_settype(d, IRQF_TRIGGER_LOW);
+                       /* clear all pending */
+-                      ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~BIT(i),
+-                              LTQ_EIU_EXIN_INIC);
++                      ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INC) & ~BIT(i),
++                              LTQ_EIU_EXIN_INC);
+                       /* enable */
+                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i),
+                               LTQ_EIU_EXIN_INEN);
+@@ -157,7 +195,7 @@ static void ltq_shutdown_eiu_irq(struct irq_data *d)
+       ltq_disable_irq(d);
+       for (i = 0; i < MAX_EIU; i++) {
+-              if (d->hwirq == ltq_eiu_irq[i]) {
++              if (d->hwirq == ltq_eiu_irq[i].start) {
+                       /* disable */
+                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i),
+                               LTQ_EIU_EXIN_INEN);
+@@ -186,6 +224,7 @@ static struct irq_chip ltq_eiu_type = {
+       .irq_ack = ltq_ack_irq,
+       .irq_mask = ltq_disable_irq,
+       .irq_mask_ack = ltq_mask_and_ack_irq,
++      .irq_set_type = ltq_eiu_settype,
+ };
+ static void ltq_hw_irqdispatch(int module)
+@@ -301,7 +340,7 @@ static int icu_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+               return 0;
+       for (i = 0; i < exin_avail; i++)
+-              if (hw == ltq_eiu_irq[i])
++              if (hw == ltq_eiu_irq[i].start)
+                       chip = &ltq_eiu_type;
+       irq_set_chip_and_handler(hw, chip, handle_level_irq);
+@@ -323,7 +362,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
+ {
+       struct device_node *eiu_node;
+       struct resource res;
+-      int i;
++      int i, ret;
+       for (i = 0; i < MAX_IM; i++) {
+               if (of_address_to_resource(node, i, &res))
+@@ -340,17 +379,19 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
+       }
+       /* the external interrupts are optional and xway only */
+-      eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu");
++      eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu-xway");
+       if (eiu_node && !of_address_to_resource(eiu_node, 0, &res)) {
+               /* find out how many external irq sources we have */
+-              const __be32 *count = of_get_property(node,
+-                                                      "lantiq,count", NULL);
++              exin_avail = of_irq_count(eiu_node);
+-              if (count)
+-                      exin_avail = *count;
+               if (exin_avail > MAX_EIU)
+                       exin_avail = MAX_EIU;
++              ret = of_irq_to_resource_table(eiu_node,
++                                              ltq_eiu_irq, exin_avail);
++              if (ret != exin_avail)
++                      panic("failed to load external irq resources\n");
++
+               if (request_mem_region(res.start, resource_size(&res),
+                                                       res.name) < 0)
+                       pr_err("Failed to request eiu memory");
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0020-MIPS-lantiq-adds-4dword-burst-length-for-dma.patch b/target/linux/lantiq/patches-3.8/0020-MIPS-lantiq-adds-4dword-burst-length-for-dma.patch
new file mode 100644 (file)
index 0000000..febc16a
--- /dev/null
@@ -0,0 +1,34 @@
+From 5be3837d01ea56e1455781f1b51764bd896973f2 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 6 Dec 2012 11:59:23 +0100
+Subject: [PATCH 20/40] MIPS: lantiq: adds 4dword burst length for dma
+
+---
+ arch/mips/lantiq/xway/dma.c |    4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c
+index e44a186..c7684c9 100644
+--- a/arch/mips/lantiq/xway/dma.c
++++ b/arch/mips/lantiq/xway/dma.c
+@@ -47,6 +47,7 @@
+ #define DMA_IRQ_ACK           0x7e            /* IRQ status register */
+ #define DMA_POLL              BIT(31)         /* turn on channel polling */
+ #define DMA_CLK_DIV4          BIT(6)          /* polling clock divider */
++#define DMA_4W_BURST          BIT(2)          /* 4 word burst length */
+ #define DMA_2W_BURST          BIT(1)          /* 2 word burst length */
+ #define DMA_MAX_CHANNEL               20              /* the soc has 20 channels */
+ #define DMA_ETOP_ENDIANNESS   (0xf << 8) /* endianness swap etop channels */
+@@ -195,7 +196,8 @@ ltq_dma_init_port(int p)
+                * Tell the DMA engine to swap the endianness of data frames and
+                * drop packets if the channel arbitration fails.
+                */
+-              ltq_dma_w32_mask(0, DMA_ETOP_ENDIANNESS | DMA_PDEN,
++              ltq_dma_w32_mask(0, (DMA_4W_BURST << 4) | (DMA_4W_BURST << 2) |
++                      DMA_ETOP_ENDIANNESS | DMA_PDEN,
+                       LTQ_DMA_PCTRL);
+               break;
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0021-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch b/target/linux/lantiq/patches-3.8/0021-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch
new file mode 100644 (file)
index 0000000..a6d3259
--- /dev/null
@@ -0,0 +1,404 @@
+From ff3d0dd789882ad552a5224d34e5db426e1c45fe Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Sat, 23 Jun 2012 15:32:33 +0200
+Subject: [PATCH 21/40] GPIO: MIPS: add gpio driver for falcon SoC
+
+Add driver for GPIO blocks found on Lantiq FALCON SoC. The SoC has 5 banks of
+up to 32 pads. The GPIO blocks have a per pin IRQs.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Cc: linux-kernel@vger.kernel.org
+---
+ drivers/gpio/Kconfig       |    5 +
+ drivers/gpio/Makefile      |    1 +
+ drivers/gpio/gpio-falcon.c |  349 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 355 insertions(+)
+ create mode 100644 drivers/gpio/gpio-falcon.c
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index 682de75..e8d84fa 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -133,6 +133,11 @@ config GPIO_EP93XX
+       depends on ARCH_EP93XX
+       select GPIO_GENERIC
++config GPIO_FALCON
++      def_bool y
++      depends on MIPS && SOC_FALCON
++      select GPIO_GENERIC
++
+ config GPIO_MM_LANTIQ
+       bool "Lantiq Memory mapped GPIOs"
+       depends on LANTIQ && SOC_XWAY
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index c5aebd0..9bdbb91 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -24,6 +24,7 @@ obj-$(CONFIG_GPIO_DA9055)    += gpio-da9055.o
+ obj-$(CONFIG_ARCH_DAVINCI)    += gpio-davinci.o
+ obj-$(CONFIG_GPIO_EM)         += gpio-em.o
+ obj-$(CONFIG_GPIO_EP93XX)     += gpio-ep93xx.o
++obj-$(CONFIG_GPIO_FALCON)     += gpio-falcon.o
+ obj-$(CONFIG_GPIO_GE_FPGA)    += gpio-ge.o
+ obj-$(CONFIG_GPIO_ICH)                += gpio-ich.o
+ obj-$(CONFIG_GPIO_IT8761E)    += gpio-it8761e.o
+diff --git a/drivers/gpio/gpio-falcon.c b/drivers/gpio/gpio-falcon.c
+new file mode 100644
+index 0000000..ae8b55d
+--- /dev/null
++++ b/drivers/gpio/gpio-falcon.c
+@@ -0,0 +1,349 @@
++/*
++ *  This program is free software; you can redistribute it and/or modify it
++ *  under the terms of the GNU General Public License version 2 as published
++ *  by the Free Software Foundation.
++ *
++ *  Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
++ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
++ */
++
++#include <linux/gpio.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/export.h>
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/pinctrl/pinctrl.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++
++#include <lantiq_soc.h>
++
++/* Data Output Register */
++#define GPIO_OUT            0x00000000
++/* Data Input Register */
++#define GPIO_IN             0x00000004
++/* Direction Register */
++#define GPIO_DIR            0x00000008
++/* External Interrupt Control Register 0 */
++#define GPIO_EXINTCR0       0x00000018
++/* External Interrupt Control Register 1 */
++#define GPIO_EXINTCR1       0x0000001C
++/* IRN Capture Register */
++#define GPIO_IRNCR          0x00000020
++/* IRN Interrupt Configuration Register */
++#define GPIO_IRNCFG           0x0000002C
++/* IRN Interrupt Enable Set Register */
++#define GPIO_IRNRNSET       0x00000030
++/* IRN Interrupt Enable Clear Register */
++#define GPIO_IRNENCLR       0x00000034
++/* Output Set Register */
++#define GPIO_OUTSET         0x00000040
++/* Output Cler Register */
++#define GPIO_OUTCLR         0x00000044
++/* Direction Clear Register */
++#define GPIO_DIRSET         0x00000048
++/* Direction Set Register */
++#define GPIO_DIRCLR         0x0000004C
++
++/* turn a gpio_chip into a falcon_gpio_port */
++#define ctop(c)               container_of(c, struct falcon_gpio_port, gpio_chip)
++/* turn a irq_data into a falcon_gpio_port */
++#define itop(i)               ((struct falcon_gpio_port *) irq_get_chip_data(i->irq))
++
++#define port_r32(p, reg)      ltq_r32(p->port + reg)
++#define port_w32(p, val, reg) ltq_w32(val, p->port + reg)
++#define port_w32_mask(p, clear, set, reg) \
++              port_w32(p, (port_r32(p, reg) & ~(clear)) | (set), reg)
++
++#define MAX_PORTS             5
++#define PINS_PER_PORT         32
++
++struct falcon_gpio_port {
++      struct gpio_chip gpio_chip;
++      void __iomem *port;
++      unsigned int irq_base;
++      unsigned int chained_irq;
++      struct clk *clk;
++      char name[6];
++};
++
++static int falcon_gpio_direction_input(struct gpio_chip *chip,
++                                      unsigned int offset)
++{
++      port_w32(ctop(chip), 1 << offset, GPIO_DIRCLR);
++
++      return 0;
++}
++
++static void falcon_gpio_set(struct gpio_chip *chip, unsigned int offset,
++                                      int value)
++{
++      if (value)
++              port_w32(ctop(chip), 1 << offset, GPIO_OUTSET);
++      else
++              port_w32(ctop(chip), 1 << offset, GPIO_OUTCLR);
++}
++
++static int falcon_gpio_direction_output(struct gpio_chip *chip,
++                                      unsigned int offset, int value)
++{
++      falcon_gpio_set(chip, offset, value);
++      port_w32(ctop(chip), 1 << offset, GPIO_DIRSET);
++
++      return 0;
++}
++
++static int falcon_gpio_get(struct gpio_chip *chip, unsigned int offset)
++{
++      if ((port_r32(ctop(chip), GPIO_DIR) >> offset) & 1)
++              return (port_r32(ctop(chip), GPIO_OUT) >> offset) & 1;
++      else
++              return (port_r32(ctop(chip), GPIO_IN) >> offset) & 1;
++}
++
++static int falcon_gpio_request(struct gpio_chip *chip, unsigned offset)
++{
++      int gpio = chip->base + offset;
++
++      return pinctrl_request_gpio(gpio);
++}
++
++static void falcon_gpio_free(struct gpio_chip *chip, unsigned offset)
++{
++      int gpio = chip->base + offset;
++
++      pinctrl_free_gpio(gpio);
++}
++
++static int falcon_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
++{
++      return ctop(chip)->irq_base + offset;
++}
++
++static void falcon_gpio_disable_irq(struct irq_data *d)
++{
++      unsigned int offset = d->irq - itop(d)->irq_base;
++
++      port_w32(itop(d), 1 << offset, GPIO_IRNENCLR);
++}
++
++static void falcon_gpio_enable_irq(struct irq_data *d)
++{
++      unsigned int offset = d->irq - itop(d)->irq_base;
++
++      port_w32(itop(d), 1 << offset, GPIO_IRNRNSET);
++}
++
++static void falcon_gpio_ack_irq(struct irq_data *d)
++{
++      unsigned int offset = d->irq - itop(d)->irq_base;
++
++      port_w32(itop(d), 1 << offset, GPIO_IRNCR);
++}
++
++static void falcon_gpio_mask_and_ack_irq(struct irq_data *d)
++{
++      unsigned int offset = d->irq - itop(d)->irq_base;
++
++      port_w32(itop(d), 1 << offset, GPIO_IRNENCLR);
++      port_w32(itop(d), 1 << offset, GPIO_IRNCR);
++}
++
++static struct irq_chip falcon_gpio_irq_chip;
++static int falcon_gpio_irq_type(struct irq_data *d, unsigned int type)
++{
++      unsigned int offset = d->irq - itop(d)->irq_base;
++      unsigned int mask = 1 << offset;
++
++      if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_NONE)
++              return 0;
++
++      if ((type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) != 0) {
++              /* level triggered */
++              port_w32_mask(itop(d), 0, mask, GPIO_IRNCFG);
++              irq_set_chip_and_handler_name(d->irq,
++                      &falcon_gpio_irq_chip, handle_level_irq, "mux");
++      } else {
++              /* edge triggered */
++              port_w32_mask(itop(d), mask, 0, GPIO_IRNCFG);
++              irq_set_chip_and_handler_name(d->irq,
++                      &falcon_gpio_irq_chip, handle_simple_irq, "mux");
++      }
++
++      if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
++              port_w32_mask(itop(d), mask, 0, GPIO_EXINTCR0);
++              port_w32_mask(itop(d), 0, mask, GPIO_EXINTCR1);
++      } else {
++              if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) != 0)
++                      /* positive logic: rising edge, high level */
++                      port_w32_mask(itop(d), mask, 0, GPIO_EXINTCR0);
++              else
++                      /* negative logic: falling edge, low level */
++                      port_w32_mask(itop(d), 0, mask, GPIO_EXINTCR0);
++              port_w32_mask(itop(d), mask, 0, GPIO_EXINTCR1);
++      }
++
++      return gpio_direction_input(itop(d)->gpio_chip.base + offset);
++}
++
++static void falcon_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
++{
++      struct falcon_gpio_port *gpio_port = irq_desc_get_handler_data(desc);
++      unsigned long irncr;
++      int offset;
++
++      /* acknowledge interrupt */
++      irncr = port_r32(gpio_port, GPIO_IRNCR);
++      port_w32(gpio_port, irncr, GPIO_IRNCR);
++
++      desc->irq_data.chip->irq_ack(&desc->irq_data);
++
++      for_each_set_bit(offset, &irncr, gpio_port->gpio_chip.ngpio)
++              generic_handle_irq(gpio_port->irq_base + offset);
++}
++
++static int falcon_gpio_irq_map(struct irq_domain *d, unsigned int irq,
++                              irq_hw_number_t hw)
++{
++      struct falcon_gpio_port *port = d->host_data;
++
++      irq_set_chip_and_handler_name(irq, &falcon_gpio_irq_chip,
++                      handle_simple_irq, "mux");
++      irq_set_chip_data(irq, port);
++
++      /* set to negative logic (falling edge, low level) */
++      port_w32_mask(port, 0, 1 << hw, GPIO_EXINTCR0);
++      return 0;
++}
++
++static struct irq_chip falcon_gpio_irq_chip = {
++      .name = "gpio_irq_mux",
++      .irq_mask = falcon_gpio_disable_irq,
++      .irq_unmask = falcon_gpio_enable_irq,
++      .irq_ack = falcon_gpio_ack_irq,
++      .irq_mask_ack = falcon_gpio_mask_and_ack_irq,
++      .irq_set_type = falcon_gpio_irq_type,
++};
++
++static const struct irq_domain_ops irq_domain_ops = {
++      .xlate = irq_domain_xlate_onetwocell,
++      .map = falcon_gpio_irq_map,
++};
++
++static struct irqaction gpio_cascade = {
++      .handler = no_action,
++      .flags = IRQF_DISABLED,
++      .name = "gpio_cascade",
++};
++
++static int falcon_gpio_probe(struct platform_device *pdev)
++{
++      struct pinctrl_gpio_range *gpio_range;
++      struct device_node *node = pdev->dev.of_node;
++      const __be32 *bank = of_get_property(node, "lantiq,bank", NULL);
++      struct falcon_gpio_port *gpio_port;
++      struct resource *gpiores, irqres;
++      int ret, size;
++
++      if (!bank || *bank >= MAX_PORTS)
++              return -ENODEV;
++
++      size = pinctrl_falcon_get_range_size(*bank);
++      if (size < 1) {
++              dev_err(&pdev->dev, "pad not loaded for bank %d\n", *bank);
++              return size;
++      }
++
++      gpiores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      if (!gpiores)
++              return -ENODEV;
++
++      gpio_range = devm_kzalloc(&pdev->dev, sizeof(struct pinctrl_gpio_range),
++                              GFP_KERNEL);
++      if (!gpio_range)
++              return -ENOMEM;
++
++      gpio_port = devm_kzalloc(&pdev->dev, sizeof(struct falcon_gpio_port),
++                              GFP_KERNEL);
++      if (!gpio_port)
++              return -ENOMEM;
++      snprintf(gpio_port->name, 6, "gpio%d", *bank);
++      gpio_port->gpio_chip.label = gpio_port->name;
++      gpio_port->gpio_chip.direction_input = falcon_gpio_direction_input;
++      gpio_port->gpio_chip.direction_output = falcon_gpio_direction_output;
++      gpio_port->gpio_chip.get = falcon_gpio_get;
++      gpio_port->gpio_chip.set = falcon_gpio_set;
++      gpio_port->gpio_chip.request = falcon_gpio_request;
++      gpio_port->gpio_chip.free = falcon_gpio_free;
++      gpio_port->gpio_chip.base = -1;
++      gpio_port->gpio_chip.ngpio = size;
++      gpio_port->gpio_chip.dev = &pdev->dev;
++
++      gpio_port->port = devm_request_and_ioremap(&pdev->dev, gpiores);
++      if (!gpio_port->port) {
++              dev_err(&pdev->dev, "Could not map io ranges\n");
++              return -ENOMEM;
++      }
++
++      gpio_port->clk = clk_get(&pdev->dev, NULL);
++      if (IS_ERR(gpio_port->clk)) {
++              dev_err(&pdev->dev, "Could not get clock\n");
++              return PTR_ERR(gpio_port->clk);
++      }
++      clk_enable(gpio_port->clk);
++
++      if (of_irq_to_resource_table(node, &irqres, 1) == 1) {
++              gpio_port->irq_base = INT_NUM_EXTRA_START + (32 * *bank);
++              gpio_port->gpio_chip.to_irq = falcon_gpio_to_irq;
++              gpio_port->chained_irq = irqres.start;
++              irq_domain_add_legacy(node, size, gpio_port->irq_base, 0,
++                                      &irq_domain_ops, gpio_port);
++              setup_irq(irqres.start, &gpio_cascade);
++              irq_set_handler_data(irqres.start, gpio_port);
++              irq_set_chained_handler(irqres.start, falcon_gpio_irq_handler);
++      }
++
++      ret = gpiochip_add(&gpio_port->gpio_chip);
++      if (!ret)
++              platform_set_drvdata(pdev, gpio_port);
++
++      gpio_range->name = "FALCON GPIO";
++      gpio_range->id = *bank;
++      gpio_range->base = gpio_port->gpio_chip.base;
++      gpio_range->npins = gpio_port->gpio_chip.ngpio;
++      gpio_range->gc = &gpio_port->gpio_chip;
++      pinctrl_falcon_add_gpio_range(gpio_range);
++
++      return ret;
++}
++
++static const struct of_device_id falcon_gpio_match[] = {
++      { .compatible = "lantiq,gpio-falcon" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, falcon_gpio_match);
++
++static struct platform_driver falcon_gpio_driver = {
++      .probe = falcon_gpio_probe,
++      .driver = {
++              .name = "gpio-falcon",
++              .owner = THIS_MODULE,
++              .of_match_table = falcon_gpio_match,
++      },
++};
++
++int __init falcon_gpio_init(void)
++{
++      int ret;
++
++      pr_info("FALC(tm) ON GPIO Driver, (C) 2012 Lantiq Deutschland Gmbh\n");
++      ret = platform_driver_register(&falcon_gpio_driver);
++      if (ret)
++              pr_err("falcon_gpio: Error registering platform driver!");
++      return ret;
++}
++
++subsys_initcall(falcon_gpio_init);
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0022-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch b/target/linux/lantiq/patches-3.8/0022-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch
new file mode 100644 (file)
index 0000000..9427758
--- /dev/null
@@ -0,0 +1,1047 @@
+From 09628160c97e9bd7dfbb39fd467cf17f1e43e85c Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Sun, 20 May 2012 00:42:39 +0200
+Subject: [PATCH 22/40] I2C: MIPS: lantiq: add FALC-ON i2c bus master
+
+This patch adds the driver needed to make the I2C bus work on FALC-ON SoCs.
+
+Signed-off-by: Thomas Langer <thomas.langer@lantiq.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/i2c/busses/Kconfig      |   10 +
+ drivers/i2c/busses/Makefile     |    1 +
+ drivers/i2c/busses/i2c-lantiq.c |  747 +++++++++++++++++++++++++++++++++++++++
+ drivers/i2c/busses/i2c-lantiq.h |  234 ++++++++++++
+ 4 files changed, 992 insertions(+)
+ create mode 100644 drivers/i2c/busses/i2c-lantiq.c
+ create mode 100644 drivers/i2c/busses/i2c-lantiq.h
+
+diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
+index bdca511..5e2994f 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -470,6 +470,16 @@ config I2C_IOP3XX
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-iop3xx.
++config I2C_LANTIQ
++      tristate "Lantiq I2C interface"
++      depends on LANTIQ && SOC_FALCON
++      help
++        If you say yes to this option, support will be included for the
++        Lantiq I2C core.
++
++        This driver can also be built as a module. If so, the module
++        will be called i2c-lantiq.
++
+ config I2C_MPC
+       tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx"
+       depends on PPC
+diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
+index 6181f3f..40ea7d8 100644
+--- a/drivers/i2c/busses/Makefile
++++ b/drivers/i2c/busses/Makefile
+@@ -46,6 +46,7 @@ obj-$(CONFIG_I2C_IBM_IIC)    += i2c-ibm_iic.o
+ obj-$(CONFIG_I2C_IMX)         += i2c-imx.o
+ obj-$(CONFIG_I2C_INTEL_MID)   += i2c-intel-mid.o
+ obj-$(CONFIG_I2C_IOP3XX)      += i2c-iop3xx.o
++obj-$(CONFIG_I2C_LANTIQ)      += i2c-lantiq.o
+ obj-$(CONFIG_I2C_MPC)         += i2c-mpc.o
+ obj-$(CONFIG_I2C_MV64XXX)     += i2c-mv64xxx.o
+ obj-$(CONFIG_I2C_MXS)         += i2c-mxs.o
+diff --git a/drivers/i2c/busses/i2c-lantiq.c b/drivers/i2c/busses/i2c-lantiq.c
+new file mode 100644
+index 0000000..9a5f58b
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-lantiq.c
+@@ -0,0 +1,747 @@
++
++/*
++ * Lantiq I2C bus adapter
++ *
++ * Parts based on i2c-designware.c and other i2c drivers from Linux 2.6.33
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h> /* for kzalloc, kfree */
++#include <linux/i2c.h>
++#include <linux/errno.h>
++#include <linux/completion.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/of_irq.h>
++#include <linux/of_i2c.h>
++
++#include <lantiq_soc.h>
++#include "i2c-lantiq.h"
++
++/*
++ * CURRENT ISSUES:
++ * - no high speed support
++ * - ten bit mode is not tested (no slave devices)
++ */
++
++/* access macros */
++#define i2c_r32(reg)  \
++      __raw_readl(&(priv->membase)->reg)
++#define i2c_w32(val, reg)     \
++      __raw_writel(val, &(priv->membase)->reg)
++#define i2c_w32_mask(clear, set, reg) \
++      i2c_w32((i2c_r32(reg) & ~(clear)) | (set), reg)
++
++#define DRV_NAME "i2c-lantiq"
++#define DRV_VERSION "1.00"
++
++#define LTQ_I2C_BUSY_TIMEOUT          20 /* ms */
++
++#ifdef DEBUG
++#define LTQ_I2C_XFER_TIMEOUT          (25*HZ)
++#else
++#define LTQ_I2C_XFER_TIMEOUT          HZ
++#endif
++
++#define LTQ_I2C_IMSC_DEFAULT_MASK     (I2C_IMSC_I2C_P_INT_EN | \
++                                       I2C_IMSC_I2C_ERR_INT_EN)
++
++#define LTQ_I2C_ARB_LOST              (1 << 0)
++#define LTQ_I2C_NACK                  (1 << 1)
++#define LTQ_I2C_RX_UFL                        (1 << 2)
++#define LTQ_I2C_RX_OFL                        (1 << 3)
++#define LTQ_I2C_TX_UFL                        (1 << 4)
++#define LTQ_I2C_TX_OFL                        (1 << 5)
++
++struct ltq_i2c {
++      struct mutex mutex;
++
++
++      /* active clock settings */
++      unsigned int input_clock;       /* clock input for i2c hardware block */
++      unsigned int i2c_clock;         /* approximated bus clock in kHz */
++
++      struct clk *clk_gate;
++      struct clk *clk_input;
++
++
++      /* resources (memory and interrupts) */
++      int irq_lb;                             /* last burst irq */
++
++      struct lantiq_reg_i2c __iomem *membase; /* base of mapped registers */
++
++      struct i2c_adapter adap;
++      struct device *dev;
++
++      struct completion cmd_complete;
++
++
++      /* message transfer data */
++      struct i2c_msg *current_msg;    /* current message */
++      int msgs_num;           /* number of messages to handle */
++      u8 *msg_buf;            /* current buffer */
++      u32 msg_buf_len;        /* remaining length of current buffer */
++      int msg_err;            /* error status of the current transfer */
++
++
++      /* master status codes */
++      enum {
++              STATUS_IDLE,
++              STATUS_ADDR,    /* address phase */
++              STATUS_WRITE,
++              STATUS_READ,
++              STATUS_READ_END,
++              STATUS_STOP
++      } status;
++};
++
++static irqreturn_t ltq_i2c_isr(int irq, void *dev_id);
++
++static inline void enable_burst_irq(struct ltq_i2c *priv)
++{
++      i2c_w32_mask(0, I2C_IMSC_LBREQ_INT_EN | I2C_IMSC_BREQ_INT_EN, imsc);
++}
++static inline void disable_burst_irq(struct ltq_i2c *priv)
++{
++      i2c_w32_mask(I2C_IMSC_LBREQ_INT_EN | I2C_IMSC_BREQ_INT_EN, 0, imsc);
++}
++
++static void prepare_msg_send_addr(struct ltq_i2c *priv)
++{
++      struct i2c_msg *msg = priv->current_msg;
++      int rd = !!(msg->flags & I2C_M_RD);     /* extends to 0 or 1 */
++      u16 addr = msg->addr;
++
++      /* new i2c_msg */
++      priv->msg_buf = msg->buf;
++      priv->msg_buf_len = msg->len;
++      if (rd)
++              priv->status = STATUS_READ;
++      else
++              priv->status = STATUS_WRITE;
++
++      /* send slave address */
++      if (msg->flags & I2C_M_TEN) {
++              i2c_w32(0xf0 | ((addr & 0x300) >> 7) | rd, txd);
++              i2c_w32(addr & 0xff, txd);
++      } else {
++              i2c_w32((addr & 0x7f) << 1 | rd, txd);
++      }
++}
++
++static void ltq_i2c_set_tx_len(struct ltq_i2c *priv)
++{
++      struct i2c_msg *msg = priv->current_msg;
++      int len = (msg->flags & I2C_M_TEN) ? 2 : 1;
++
++      pr_debug("set_tx_len %cX\n", (msg->flags & I2C_M_RD) ? 'R' : 'T');
++
++      priv->status = STATUS_ADDR;
++
++      if (!(msg->flags & I2C_M_RD))
++              len += msg->len;
++      else
++              /* set maximum received packet size (before rx int!) */
++              i2c_w32(msg->len, mrps_ctrl);
++      i2c_w32(len, tps_ctrl);
++      enable_burst_irq(priv);
++}
++
++static int ltq_i2c_hw_set_clock(struct i2c_adapter *adap)
++{
++      struct ltq_i2c *priv = i2c_get_adapdata(adap);
++      unsigned int input_clock = clk_get_rate(priv->clk_input);
++      u32 dec, inc = 1;
++
++      /* clock changed? */
++      if (priv->input_clock == input_clock)
++              return 0;
++
++      /*
++       * this formula is only an approximation, found by the recommended
++       * values in the "I2C Architecture Specification 1.7.1"
++       */
++      dec = input_clock / (priv->i2c_clock * 2);
++      if (dec <= 6)
++              return -ENXIO;
++
++      i2c_w32(0, fdiv_high_cfg);
++      i2c_w32((inc << I2C_FDIV_CFG_INC_OFFSET) |
++              (dec << I2C_FDIV_CFG_DEC_OFFSET),
++              fdiv_cfg);
++
++      dev_info(priv->dev, "setup clocks (in %d kHz, bus %d kHz, dec=%d)\n",
++              input_clock, priv->i2c_clock, dec);
++
++      priv->input_clock = input_clock;
++      return 0;
++}
++
++static int ltq_i2c_hw_init(struct i2c_adapter *adap)
++{
++      int ret = 0;
++      struct ltq_i2c *priv = i2c_get_adapdata(adap);
++
++      /* disable bus */
++      i2c_w32_mask(I2C_RUN_CTRL_RUN_EN, 0, run_ctrl);
++
++#ifndef DEBUG
++      /* set normal operation clock divider */
++      i2c_w32(1 << I2C_CLC_RMC_OFFSET, clc);
++#else
++      /* for debugging a higher divider value! */
++      i2c_w32(0xF0 << I2C_CLC_RMC_OFFSET, clc);
++#endif
++
++      /* setup clock */
++      ret = ltq_i2c_hw_set_clock(adap);
++      if (ret != 0) {
++              dev_warn(priv->dev, "invalid clock settings\n");
++              return ret;
++      }
++
++      /* configure fifo */
++      i2c_w32(I2C_FIFO_CFG_TXFC | /* tx fifo as flow controller */
++              I2C_FIFO_CFG_RXFC | /* rx fifo as flow controller */
++              I2C_FIFO_CFG_TXFA_TXFA2 | /* tx fifo 4-byte aligned */
++              I2C_FIFO_CFG_RXFA_RXFA2 | /* rx fifo 4-byte aligned */
++              I2C_FIFO_CFG_TXBS_TXBS0 | /* tx fifo burst size is 1 word */
++              I2C_FIFO_CFG_RXBS_RXBS0,  /* rx fifo burst size is 1 word */
++              fifo_cfg);
++
++      /* configure address */
++      i2c_w32(I2C_ADDR_CFG_SOPE_EN |  /* generate stop when no more data in
++                                         the fifo */
++              I2C_ADDR_CFG_SONA_EN |  /* generate stop when NA received */
++              I2C_ADDR_CFG_MnS_EN |   /* we are master device */
++              0,                      /* our slave address (not used!) */
++              addr_cfg);
++
++      /* enable bus */
++      i2c_w32_mask(0, I2C_RUN_CTRL_RUN_EN, run_ctrl);
++
++      return 0;
++}
++
++static int ltq_i2c_wait_bus_not_busy(struct ltq_i2c *priv)
++{
++      unsigned long timeout;
++
++      timeout = jiffies + msecs_to_jiffies(LTQ_I2C_BUSY_TIMEOUT);
++
++      do {
++              u32 stat = i2c_r32(bus_stat);
++
++              if ((stat & I2C_BUS_STAT_BS_MASK) == I2C_BUS_STAT_BS_FREE)
++                      return 0;
++
++              cond_resched();
++      } while (!time_after_eq(jiffies, timeout));
++
++      dev_err(priv->dev, "timeout waiting for bus ready\n");
++      return -ETIMEDOUT;
++}
++
++static void ltq_i2c_tx(struct ltq_i2c *priv, int last)
++{
++      if (priv->msg_buf_len && priv->msg_buf) {
++              i2c_w32(*priv->msg_buf, txd);
++
++              if (--priv->msg_buf_len)
++                      priv->msg_buf++;
++              else
++                      priv->msg_buf = NULL;
++      } else {
++              last = 1;
++      }
++
++      if (last)
++              disable_burst_irq(priv);
++}
++
++static void ltq_i2c_rx(struct ltq_i2c *priv, int last)
++{
++      u32 fifo_stat, timeout;
++      if (priv->msg_buf_len && priv->msg_buf) {
++              timeout = 5000000;
++              do {
++                      fifo_stat = i2c_r32(ffs_stat);
++              } while (!fifo_stat && --timeout);
++              if (!timeout) {
++                      last = 1;
++                      pr_debug("\nrx timeout\n");
++                      goto err;
++              }
++              while (fifo_stat) {
++                      *priv->msg_buf = i2c_r32(rxd);
++                      if (--priv->msg_buf_len) {
++                              priv->msg_buf++;
++                      } else {
++                              priv->msg_buf = NULL;
++                              last = 1;
++                              break;
++                      }
++                      /*
++                       * do not read more than burst size, otherwise no "last
++                       * burst" is generated and the transaction is blocked!
++                       */
++                      fifo_stat = 0;
++              }
++      } else {
++              last = 1;
++      }
++err:
++      if (last) {
++              disable_burst_irq(priv);
++
++              if (priv->status == STATUS_READ_END) {
++                      /* 
++                       * do the STATUS_STOP and complete() here, as sometimes
++                       * the tx_end is already seen before this is finished
++                       */
++                      priv->status = STATUS_STOP;
++                      complete(&priv->cmd_complete);
++              } else {
++                      i2c_w32(I2C_ENDD_CTRL_SETEND, endd_ctrl);
++                      priv->status = STATUS_READ_END;
++              }
++      }
++}
++
++static void ltq_i2c_xfer_init(struct ltq_i2c *priv)
++{
++      /* enable interrupts */
++      i2c_w32(LTQ_I2C_IMSC_DEFAULT_MASK, imsc);
++
++      /* trigger transfer of first msg */
++      ltq_i2c_set_tx_len(priv);
++}
++
++static void dump_msgs(struct i2c_msg msgs[], int num, int rx)
++{
++#if defined(DEBUG)
++      int i, j;
++      pr_debug("Messages %d %s\n", num, rx ? "out" : "in");
++      for (i = 0; i < num; i++) {
++              pr_debug("%2d %cX Msg(%d) addr=0x%X: ", i,
++                      (msgs[i].flags & I2C_M_RD) ? 'R' : 'T',
++                      msgs[i].len, msgs[i].addr);
++              if (!(msgs[i].flags & I2C_M_RD) || rx) {
++                      for (j = 0; j < msgs[i].len; j++)
++                              pr_debug("%02X ", msgs[i].buf[j]);
++              }
++              pr_debug("\n");
++      }
++#endif
++}
++
++static void ltq_i2c_release_bus(struct ltq_i2c *priv)
++{
++      if ((i2c_r32(bus_stat) & I2C_BUS_STAT_BS_MASK) == I2C_BUS_STAT_BS_BM)
++              i2c_w32(I2C_ENDD_CTRL_SETEND, endd_ctrl);
++}
++
++static int ltq_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
++                         int num)
++{
++      struct ltq_i2c *priv = i2c_get_adapdata(adap);
++      int ret;
++
++      dev_dbg(priv->dev, "xfer %u messages\n", num);
++      dump_msgs(msgs, num, 0);
++
++      mutex_lock(&priv->mutex);
++
++      INIT_COMPLETION(priv->cmd_complete);
++      priv->current_msg = msgs;
++      priv->msgs_num = num;
++      priv->msg_err = 0;
++      priv->status = STATUS_IDLE;
++
++      /* wait for the bus to become ready */
++      ret = ltq_i2c_wait_bus_not_busy(priv);
++      if (ret)
++              goto done;
++
++      while (priv->msgs_num) {
++              /* start the transfers */
++              ltq_i2c_xfer_init(priv);
++
++              /* wait for transfers to complete */
++              ret = wait_for_completion_interruptible_timeout(
++                      &priv->cmd_complete, LTQ_I2C_XFER_TIMEOUT);
++              if (ret == 0) {
++                      dev_err(priv->dev, "controller timed out\n");
++                      ltq_i2c_hw_init(adap);
++                      ret = -ETIMEDOUT;
++                      goto done;
++              } else if (ret < 0)
++                      goto done;
++
++              if (priv->msg_err) {
++                      if (priv->msg_err & LTQ_I2C_NACK)
++                              ret = -ENXIO;
++                      else
++                              ret = -EREMOTEIO;
++                      goto done;
++              }
++              if (--priv->msgs_num)
++                      priv->current_msg++;
++      }
++      /* no error? */
++      ret = num;
++
++done:
++      ltq_i2c_release_bus(priv);
++
++      mutex_unlock(&priv->mutex);
++
++      if (ret >= 0)
++              dump_msgs(msgs, num, 1);
++
++      pr_debug("XFER ret %d\n", ret);
++      return ret;
++}
++
++static irqreturn_t ltq_i2c_isr_burst(int irq, void *dev_id)
++{
++      struct ltq_i2c *priv = dev_id;
++      struct i2c_msg *msg = priv->current_msg;
++      int last = (irq == priv->irq_lb);
++
++      if (last)
++              pr_debug("LB ");
++      else
++              pr_debug("B ");
++
++      if (msg->flags & I2C_M_RD) {
++              switch (priv->status) {
++              case STATUS_ADDR:
++                      pr_debug("X");
++                      prepare_msg_send_addr(priv);
++                      disable_burst_irq(priv);
++                      break;
++              case STATUS_READ:
++              case STATUS_READ_END:
++                      pr_debug("R");
++                      ltq_i2c_rx(priv, last);
++                      break;
++              default:
++                      disable_burst_irq(priv);
++                      pr_warn("Status R %d\n", priv->status);
++                      break;
++              }
++      } else {
++              switch (priv->status) {
++              case STATUS_ADDR:
++                      pr_debug("x");
++                      prepare_msg_send_addr(priv);
++                      break;
++              case STATUS_WRITE:
++                      pr_debug("w");
++                      ltq_i2c_tx(priv, last);
++                      break;
++              default:
++                      disable_burst_irq(priv);
++                      pr_warn("Status W %d\n", priv->status);
++                      break;
++              }
++      }
++
++      i2c_w32(I2C_ICR_BREQ_INT_CLR | I2C_ICR_LBREQ_INT_CLR, icr);
++      return IRQ_HANDLED;
++}
++
++static void ltq_i2c_isr_prot(struct ltq_i2c *priv)
++{
++      u32 i_pro = i2c_r32(p_irqss);
++
++      pr_debug("i2c-p");
++
++      /* not acknowledge */
++      if (i_pro & I2C_P_IRQSS_NACK) {
++              priv->msg_err |= LTQ_I2C_NACK;
++              pr_debug(" nack");
++      }
++
++      /* arbitration lost */
++      if (i_pro & I2C_P_IRQSS_AL) {
++              priv->msg_err |= LTQ_I2C_ARB_LOST;
++              pr_debug(" arb-lost");
++      }
++      /* tx -> rx switch */
++      if (i_pro & I2C_P_IRQSS_RX)
++              pr_debug(" rx");
++
++      /* tx end */
++      if (i_pro & I2C_P_IRQSS_TX_END)
++              pr_debug(" txend");
++      pr_debug("\n");
++
++      if (!priv->msg_err) {
++              /* tx -> rx switch */
++              if (i_pro & I2C_P_IRQSS_RX) {
++                      priv->status = STATUS_READ;
++                      enable_burst_irq(priv);
++              }
++              if (i_pro & I2C_P_IRQSS_TX_END) {
++                      if (priv->status == STATUS_READ)
++                              priv->status = STATUS_READ_END;
++                      else {
++                              disable_burst_irq(priv);
++                              priv->status = STATUS_STOP;
++                      }
++              }
++      }
++
++      i2c_w32(i_pro, p_irqsc);
++}
++
++static irqreturn_t ltq_i2c_isr(int irq, void *dev_id)
++{
++      u32 i_raw, i_err = 0;
++      struct ltq_i2c *priv = dev_id;
++
++      i_raw = i2c_r32(mis);
++      pr_debug("i_raw 0x%08X\n", i_raw);
++
++      /* error interrupt */
++      if (i_raw & I2C_RIS_I2C_ERR_INT_INTOCC) {
++              i_err = i2c_r32(err_irqss);
++              pr_debug("i_err 0x%08X bus_stat 0x%04X\n",
++                      i_err, i2c_r32(bus_stat));
++
++              /* tx fifo overflow (8) */
++              if (i_err & I2C_ERR_IRQSS_TXF_OFL)
++                      priv->msg_err |= LTQ_I2C_TX_OFL;
++
++              /* tx fifo underflow (4) */
++              if (i_err & I2C_ERR_IRQSS_TXF_UFL)
++                      priv->msg_err |= LTQ_I2C_TX_UFL;
++
++              /* rx fifo overflow (2) */
++              if (i_err & I2C_ERR_IRQSS_RXF_OFL)
++                      priv->msg_err |= LTQ_I2C_RX_OFL;
++
++              /* rx fifo underflow (1) */
++              if (i_err & I2C_ERR_IRQSS_RXF_UFL)
++                      priv->msg_err |= LTQ_I2C_RX_UFL;
++
++              i2c_w32(i_err, err_irqsc);
++      }
++
++      /* protocol interrupt */
++      if (i_raw & I2C_RIS_I2C_P_INT_INTOCC)
++              ltq_i2c_isr_prot(priv);
++
++      if ((priv->msg_err) || (priv->status == STATUS_STOP))
++              complete(&priv->cmd_complete);
++
++      return IRQ_HANDLED;
++}
++
++static u32 ltq_i2c_functionality(struct i2c_adapter *adap)
++{
++      return  I2C_FUNC_I2C |
++              I2C_FUNC_10BIT_ADDR |
++              I2C_FUNC_SMBUS_EMUL;
++}
++
++static struct i2c_algorithm ltq_i2c_algorithm = {
++      .master_xfer    = ltq_i2c_xfer,
++      .functionality  = ltq_i2c_functionality,
++};
++
++static int __devinit ltq_i2c_probe(struct platform_device *pdev)
++{
++      struct device_node *node = pdev->dev.of_node;
++      struct ltq_i2c *priv;
++      struct i2c_adapter *adap;
++      struct resource *mmres, irqres[4];
++      int ret = 0;
++
++      dev_dbg(&pdev->dev, "probing\n");
++
++      mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      ret = of_irq_to_resource_table(node, irqres, 4);
++      if (!mmres || (ret != 4)) {
++              dev_err(&pdev->dev, "no resources\n");
++              return -ENODEV;
++      }
++
++      /* allocate private data */
++      priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv) {
++              dev_err(&pdev->dev, "can't allocate private data\n");
++              return -ENOMEM;
++      }
++
++      adap = &priv->adap;
++      i2c_set_adapdata(adap, priv);
++      adap->owner = THIS_MODULE;
++      adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
++      strlcpy(adap->name, DRV_NAME "-adapter", sizeof(adap->name));
++      adap->algo = &ltq_i2c_algorithm;
++
++      if (of_property_read_u32(node, "clock-frequency", &priv->i2c_clock)) {
++              dev_warn(&pdev->dev, "No I2C speed selected, using 100kHz\n");
++              priv->i2c_clock = 100000;
++      }
++
++      init_completion(&priv->cmd_complete);
++      mutex_init(&priv->mutex);
++
++      priv->membase = devm_request_and_ioremap(&pdev->dev, mmres);
++      if (priv->membase == NULL)
++              return -ENOMEM;
++
++      priv->dev = &pdev->dev;
++      priv->irq_lb = irqres[0].start;
++
++      ret = devm_request_irq(&pdev->dev, irqres[0].start, ltq_i2c_isr_burst,
++              IRQF_DISABLED, "i2c lb", priv);
++      if (ret) {
++              dev_err(&pdev->dev, "can't get last burst IRQ %d\n",
++                      irqres[0].start);
++              return -ENODEV;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irqres[1].start, ltq_i2c_isr_burst,
++              IRQF_DISABLED, "i2c b", priv);
++      if (ret) {
++              dev_err(&pdev->dev, "can't get burst IRQ %d\n",
++                      irqres[1].start);
++              return -ENODEV;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irqres[2].start, ltq_i2c_isr,
++              IRQF_DISABLED, "i2c err", priv);
++      if (ret) {
++              dev_err(&pdev->dev, "can't get error IRQ %d\n",
++                      irqres[2].start);
++              return -ENODEV;
++      }
++
++      ret = devm_request_irq(&pdev->dev, irqres[3].start, ltq_i2c_isr,
++              IRQF_DISABLED, "i2c p", priv);
++      if (ret) {
++              dev_err(&pdev->dev, "can't get protocol IRQ %d\n",
++                      irqres[3].start);
++              return -ENODEV;
++      }
++
++      dev_dbg(&pdev->dev, "mapped io-space to %p\n", priv->membase);
++      dev_dbg(&pdev->dev, "use IRQs %d, %d, %d, %d\n", irqres[0].start,
++              irqres[1].start, irqres[2].start, irqres[3].start);
++
++      priv->clk_gate = devm_clk_get(&pdev->dev, NULL);
++      if (IS_ERR(priv->clk_gate)) {
++              dev_err(&pdev->dev, "failed to get i2c clk\n");
++              return -ENOENT;
++      }
++
++      /* this is a static clock, which has no refcounting */
++      priv->clk_input = clk_get_fpi();
++      if (IS_ERR(priv->clk_input)) {
++              dev_err(&pdev->dev, "failed to get fpi clk\n");
++              return -ENOENT;
++      }
++
++      clk_activate(priv->clk_gate);
++
++      /* add our adapter to the i2c stack */
++      ret = i2c_add_numbered_adapter(adap);
++      if (ret) {
++              dev_err(&pdev->dev, "can't register I2C adapter\n");
++              goto out;
++      }
++
++      platform_set_drvdata(pdev, priv);
++      i2c_set_adapdata(adap, priv);
++
++      /* print module version information */
++      dev_dbg(&pdev->dev, "module id=%u revision=%u\n",
++              (i2c_r32(id) & I2C_ID_ID_MASK) >> I2C_ID_ID_OFFSET,
++              (i2c_r32(id) & I2C_ID_REV_MASK) >> I2C_ID_REV_OFFSET);
++
++      /* initialize HW */
++      ret = ltq_i2c_hw_init(adap);
++      if (ret) {
++              dev_err(&pdev->dev, "can't configure adapter\n");
++              i2c_del_adapter(adap);
++              platform_set_drvdata(pdev, NULL);
++      } else {
++              dev_info(&pdev->dev, "version %s\n", DRV_VERSION);
++      }
++
++      of_i2c_register_devices(adap);
++
++out:
++      /* if init failed, we need to deactivate the clock gate */
++      if (ret)
++              clk_deactivate(priv->clk_gate);
++
++      return ret;
++}
++
++static int __devexit ltq_i2c_remove(struct platform_device *pdev)
++{
++      struct ltq_i2c *priv = platform_get_drvdata(pdev);
++
++      /* disable bus */
++      i2c_w32_mask(I2C_RUN_CTRL_RUN_EN, 0, run_ctrl);
++
++      /* power down the core */
++      clk_deactivate(priv->clk_gate);
++
++      /* remove driver */
++      i2c_del_adapter(&priv->adap);
++      kfree(priv);
++
++      dev_dbg(&pdev->dev, "removed\n");
++      platform_set_drvdata(pdev, NULL);
++
++      return 0;
++}
++static const struct of_device_id ltq_i2c_match[] = {
++      { .compatible = "lantiq,lantiq-i2c" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, ltq_i2c_match);
++
++static struct platform_driver ltq_i2c_driver = {
++      .probe  = ltq_i2c_probe,
++      .remove = __devexit_p(ltq_i2c_remove),
++      .driver = {
++              .name   = DRV_NAME,
++              .owner  = THIS_MODULE,
++              .of_match_table = ltq_i2c_match,
++      },
++};
++
++module_platform_driver(ltq_i2c_driver);
++
++MODULE_DESCRIPTION("Lantiq I2C bus adapter");
++MODULE_AUTHOR("Thomas Langer <thomas.langer@lantiq.com>");
++MODULE_ALIAS("platform:" DRV_NAME);
++MODULE_LICENSE("GPL");
++MODULE_VERSION(DRV_VERSION);
+diff --git a/drivers/i2c/busses/i2c-lantiq.h b/drivers/i2c/busses/i2c-lantiq.h
+new file mode 100644
+index 0000000..7a86b89
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-lantiq.h
+@@ -0,0 +1,234 @@
++#ifndef I2C_LANTIQ_H
++#define I2C_LANTIQ_H
++
++/* I2C register structure */
++struct lantiq_reg_i2c {
++      /* I2C Kernel Clock Control Register */
++      unsigned int clc; /* 0x00000000 */
++      /* Reserved */
++      unsigned int res_0; /* 0x00000004 */
++      /* I2C Identification Register */
++      unsigned int id; /* 0x00000008 */
++      /* Reserved */
++      unsigned int res_1; /* 0x0000000C */
++      /*
++       * I2C RUN Control Register
++       * This register enables and disables the I2C peripheral. Before
++       * enabling, the I2C has to be configured properly. After enabling
++       * no configuration is possible
++       */
++      unsigned int run_ctrl; /* 0x00000010 */
++      /*
++       * I2C End Data Control Register
++       * This register is used to either turn around the data transmission
++       * direction or to address another slave without sending a stop
++       * condition. Also the software can stop the slave-transmitter by
++       * sending a not-accolade when working as master-receiver or even
++       * stop data transmission immediately when operating as
++       * master-transmitter. The writing to the bits of this control
++       * register is only effective when in MASTER RECEIVES BYTES, MASTER
++       * TRANSMITS BYTES, MASTER RESTART or SLAVE RECEIVE BYTES state
++       */
++      unsigned int endd_ctrl; /* 0x00000014 */
++      /*
++       * I2C Fractional Divider Configuration Register
++       * These register is used to program the fractional divider of the I2C
++       * bus. Before the peripheral is switched on by setting the RUN-bit the
++       * two (fixed) values for the two operating frequencies are programmed
++       * into these (configuration) registers. The Register FDIV_HIGH_CFG has
++       * the same layout as I2C_FDIV_CFG.
++       */
++      unsigned int fdiv_cfg; /* 0x00000018 */
++      /*
++       * I2C Fractional Divider (highspeed mode) Configuration Register
++       * These register is used to program the fractional divider of the I2C
++       * bus. Before the peripheral is switched on by setting the RUN-bit the
++       * two (fixed) values for the two operating frequencies are programmed
++       * into these (configuration) registers. The Register FDIV_CFG has the
++       * same layout as I2C_FDIV_CFG.
++       */
++      unsigned int fdiv_high_cfg; /* 0x0000001C */
++      /* I2C Address Configuration Register */
++      unsigned int addr_cfg; /* 0x00000020 */
++      /* I2C Bus Status Register
++       * This register gives a status information of the I2C. This additional
++       * information can be used by the software to start proper actions.
++       */
++      unsigned int bus_stat; /* 0x00000024 */
++      /* I2C FIFO Configuration Register */
++      unsigned int fifo_cfg; /* 0x00000028 */
++      /* I2C Maximum Received Packet Size Register */
++      unsigned int mrps_ctrl; /* 0x0000002C */
++      /* I2C Received Packet Size Status Register */
++      unsigned int rps_stat; /* 0x00000030 */
++      /* I2C Transmit Packet Size Register */
++      unsigned int tps_ctrl; /* 0x00000034 */
++      /* I2C Filled FIFO Stages Status Register */
++      unsigned int ffs_stat; /* 0x00000038 */
++      /* Reserved */
++      unsigned int res_2; /* 0x0000003C */
++      /* I2C Timing Configuration Register */
++      unsigned int tim_cfg; /* 0x00000040 */
++      /* Reserved */
++      unsigned int res_3[7]; /* 0x00000044 */
++      /* I2C Error Interrupt Request Source Mask Register */
++      unsigned int err_irqsm; /* 0x00000060 */
++      /* I2C Error Interrupt Request Source Status Register */
++      unsigned int err_irqss; /* 0x00000064 */
++      /* I2C Error Interrupt Request Source Clear Register */
++      unsigned int err_irqsc; /* 0x00000068 */
++      /* Reserved */
++      unsigned int res_4; /* 0x0000006C */
++      /* I2C Protocol Interrupt Request Source Mask Register */
++      unsigned int p_irqsm; /* 0x00000070 */
++      /* I2C Protocol Interrupt Request Source Status Register */
++      unsigned int p_irqss; /* 0x00000074 */
++      /* I2C Protocol Interrupt Request Source Clear Register */
++      unsigned int p_irqsc; /* 0x00000078 */
++      /* Reserved */
++      unsigned int res_5; /* 0x0000007C */
++      /* I2C Raw Interrupt Status Register */
++      unsigned int ris; /* 0x00000080 */
++      /* I2C Interrupt Mask Control Register */
++      unsigned int imsc; /* 0x00000084 */
++      /* I2C Masked Interrupt Status Register */
++      unsigned int mis; /* 0x00000088 */
++      /* I2C Interrupt Clear Register */
++      unsigned int icr; /* 0x0000008C */
++      /* I2C Interrupt Set Register */
++      unsigned int isr; /* 0x00000090 */
++      /* I2C DMA Enable Register */
++      unsigned int dmae; /* 0x00000094 */
++      /* Reserved */
++      unsigned int res_6[8154]; /* 0x00000098 */
++      /* I2C Transmit Data Register */
++      unsigned int txd; /* 0x00008000 */
++      /* Reserved */
++      unsigned int res_7[4095]; /* 0x00008004 */
++      /* I2C Receive Data Register */
++      unsigned int rxd; /* 0x0000C000 */
++      /* Reserved */
++      unsigned int res_8[4095]; /* 0x0000C004 */
++};
++
++/*
++ * Clock Divider for Normal Run Mode
++ * Max 8-bit divider value. IF RMC is 0 the module is disabled. Note: As long
++ * as the new divider value RMC is not valid, the register returns 0x0000 00xx
++ * on reading.
++ */
++#define I2C_CLC_RMC_MASK 0x0000FF00
++/* field offset */
++#define I2C_CLC_RMC_OFFSET 8
++
++/* Fields of "I2C Identification Register" */
++/* Module ID */
++#define I2C_ID_ID_MASK 0x0000FF00
++/* field offset */
++#define I2C_ID_ID_OFFSET 8
++/* Revision */
++#define I2C_ID_REV_MASK 0x000000FF
++/* field offset */
++#define I2C_ID_REV_OFFSET 0
++
++/* Fields of "I2C Interrupt Mask Control Register" */
++/* Enable */
++#define I2C_IMSC_BREQ_INT_EN 0x00000008
++/* Enable */
++#define I2C_IMSC_LBREQ_INT_EN 0x00000004
++
++/* Fields of "I2C Fractional Divider Configuration Register" */
++/* field offset */
++#define I2C_FDIV_CFG_INC_OFFSET 16
++
++/* Fields of "I2C Interrupt Mask Control Register" */
++/* Enable */
++#define I2C_IMSC_I2C_P_INT_EN 0x00000020
++/* Enable */
++#define I2C_IMSC_I2C_ERR_INT_EN 0x00000010
++
++/* Fields of "I2C Error Interrupt Request Source Status Register" */
++/* TXF_OFL */
++#define I2C_ERR_IRQSS_TXF_OFL 0x00000008
++/* TXF_UFL */
++#define I2C_ERR_IRQSS_TXF_UFL 0x00000004
++/* RXF_OFL */
++#define I2C_ERR_IRQSS_RXF_OFL 0x00000002
++/* RXF_UFL */
++#define I2C_ERR_IRQSS_RXF_UFL 0x00000001
++
++/* Fields of "I2C Raw Interrupt Status Register" */
++/* Read: Interrupt occurred. */
++#define I2C_RIS_I2C_ERR_INT_INTOCC 0x00000010
++/* Read: Interrupt occurred. */
++#define I2C_RIS_I2C_P_INT_INTOCC 0x00000020
++
++/* Fields of "I2C FIFO Configuration Register" */
++/* TX FIFO Flow Control */
++#define I2C_FIFO_CFG_TXFC 0x00020000
++/* RX FIFO Flow Control */
++#define I2C_FIFO_CFG_RXFC 0x00010000
++/* Word aligned (character alignment of four characters) */
++#define I2C_FIFO_CFG_TXFA_TXFA2 0x00002000
++/* Word aligned (character alignment of four characters) */
++#define I2C_FIFO_CFG_RXFA_RXFA2 0x00000200
++/* 1 word */
++#define I2C_FIFO_CFG_TXBS_TXBS0 0x00000000
++
++/* Fields of "I2C FIFO Configuration Register" */
++/* 1 word */
++#define I2C_FIFO_CFG_RXBS_RXBS0 0x00000000
++/* Stop on Packet End Enable */
++#define I2C_ADDR_CFG_SOPE_EN 0x00200000
++/* Stop on Not Acknowledge Enable */
++#define I2C_ADDR_CFG_SONA_EN 0x00100000
++/* Enable */
++#define I2C_ADDR_CFG_MnS_EN 0x00080000
++
++/* Fields of "I2C Interrupt Clear Register" */
++/* Clear */
++#define I2C_ICR_BREQ_INT_CLR 0x00000008
++/* Clear */
++#define I2C_ICR_LBREQ_INT_CLR 0x00000004
++
++/* Fields of "I2C Fractional Divider Configuration Register" */
++/* field offset */
++#define I2C_FDIV_CFG_DEC_OFFSET 0
++
++/* Fields of "I2C Bus Status Register" */
++/* Bus Status */
++#define I2C_BUS_STAT_BS_MASK 0x00000003
++/* Read from I2C Bus. */
++#define I2C_BUS_STAT_RNW_READ 0x00000004
++/* I2C Bus is free. */
++#define I2C_BUS_STAT_BS_FREE 0x00000000
++/*
++ * The device is working as master and has claimed the control on the
++ * I2C-bus (busy master).
++ */
++#define I2C_BUS_STAT_BS_BM 0x00000002
++
++/* Fields of "I2C RUN Control Register" */
++/* Enable */
++#define I2C_RUN_CTRL_RUN_EN 0x00000001
++
++/* Fields of "I2C End Data Control Register" */
++/*
++ * Set End of Transmission
++ * Note:Do not write '1' to this bit when bus is free. This will cause an
++ * abort after the first byte when a new transfer is started.
++ */
++#define I2C_ENDD_CTRL_SETEND 0x00000002
++
++/* Fields of "I2C Protocol Interrupt Request Source Status Register" */
++/* NACK */
++#define I2C_P_IRQSS_NACK 0x00000010
++/* AL */
++#define I2C_P_IRQSS_AL 0x00000008
++/* RX */
++#define I2C_P_IRQSS_RX 0x00000040
++/* TX_END */
++#define I2C_P_IRQSS_TX_END 0x00000020
++
++
++#endif /* I2C_LANTIQ_H */
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0023-USB-fix-roothub-for-IFXHCD.patch b/target/linux/lantiq/patches-3.8/0023-USB-fix-roothub-for-IFXHCD.patch
new file mode 100644 (file)
index 0000000..0a9dbe7
--- /dev/null
@@ -0,0 +1,38 @@
+From e6c3c0d86a581e0738e18e5a3369ded8527a3315 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 6 Dec 2012 19:59:53 +0100
+Subject: [PATCH 23/40] USB: fix roothub for IFXHCD
+
+---
+ arch/mips/lantiq/Kconfig |    1 +
+ drivers/usb/core/hub.c   |    2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig
+index c002191..675310a 100644
+--- a/arch/mips/lantiq/Kconfig
++++ b/arch/mips/lantiq/Kconfig
+@@ -3,6 +3,7 @@ if LANTIQ
+ config SOC_TYPE_XWAY
+       bool
+       select PINCTRL_XWAY
++      select USB_ARCH_HAS_HCD
+       default n
+ choice
+diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
+index cbf7168..5cddead 100644
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -4006,7 +4006,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
+               udev->ttport = hdev->ttport;
+       } else if (udev->speed != USB_SPEED_HIGH
+                       && hdev->speed == USB_SPEED_HIGH) {
+-              if (!hub->tt.hub) {
++              if (hdev->parent && !hub->tt.hub) {
+                       dev_err(&udev->dev, "parent hub has no TT\n");
+                       retval = -EINVAL;
+                       goto fail;
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0024-SPI-MIPS-lantiq-adds-spi-xway.patch b/target/linux/lantiq/patches-3.8/0024-SPI-MIPS-lantiq-adds-spi-xway.patch
new file mode 100644 (file)
index 0000000..a1a70f8
--- /dev/null
@@ -0,0 +1,1032 @@
+From 60092075ded3e51036fd018ba6d9cc49e7079dcd Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 13 Mar 2013 09:29:37 +0100
+Subject: [PATCH 24/40] SPI: MIPS: lantiq: adds spi-xway
+
+This patch adds support for the SPI core found on several Lantiq SoCs.
+The Driver has been runtime tested in combination with m25p80 Flash Devices
+on Amazon_SE and VR9.
+
+Signed-off-by: Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/spi/Kconfig    |    8 +
+ drivers/spi/Makefile   |    1 +
+ drivers/spi/spi-xway.c |  977 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 986 insertions(+)
+ create mode 100644 drivers/spi/spi-xway.c
+
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index 2e188e1..3522f29 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -449,6 +449,14 @@ config SPI_NUC900
+       help
+         SPI driver for Nuvoton NUC900 series ARM SoCs
++config SPI_XWAY
++      tristate "Lantiq XWAY SPI controller"
++      depends on LANTIQ && SOC_TYPE_XWAY
++      select SPI_BITBANG
++      help
++        This driver supports the Lantiq SoC SPI controller in master
++        mode.
++
+ #
+ # Add new SPI master controllers in alphabetical order above this line
+ #
+diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
+index 64e970b..63c24da 100644
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -68,3 +68,4 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH)               += spi-topcliff-pch.o
+ obj-$(CONFIG_SPI_TXX9)                        += spi-txx9.o
+ obj-$(CONFIG_SPI_XCOMM)               += spi-xcomm.o
+ obj-$(CONFIG_SPI_XILINX)              += spi-xilinx.o
++obj-$(CONFIG_SPI_XWAY)                        += spi-xway.o
+diff --git a/drivers/spi/spi-xway.c b/drivers/spi/spi-xway.c
+new file mode 100644
+index 0000000..61532e3
+--- /dev/null
++++ b/drivers/spi/spi-xway.c
+@@ -0,0 +1,977 @@
++/*
++ * Lantiq SoC SPI controller
++ *
++ * Copyright (C) 2011 Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>
++ * Copyright (C) 2012 John Crispin <blogic@openwrt.org>
++ *
++ * This program is free software; you can distribute it and/or modify it
++ * under the terms of the GNU General Public License (Version 2) as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/workqueue.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/completion.h>
++#include <linux/spinlock.h>
++#include <linux/err.h>
++#include <linux/clk.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++#include <linux/of_irq.h>
++
++#include <lantiq_soc.h>
++
++#define LTQ_SPI_CLC           0x00    /* Clock control */
++#define LTQ_SPI_PISEL         0x04    /* Port input select */
++#define LTQ_SPI_ID            0x08    /* Identification */
++#define LTQ_SPI_CON           0x10    /* Control */
++#define LTQ_SPI_STAT          0x14    /* Status */
++#define LTQ_SPI_WHBSTATE      0x18    /* Write HW modified state */
++#define LTQ_SPI_TB            0x20    /* Transmit buffer */
++#define LTQ_SPI_RB            0x24    /* Receive buffer */
++#define LTQ_SPI_RXFCON                0x30    /* Receive FIFO control */
++#define LTQ_SPI_TXFCON                0x34    /* Transmit FIFO control */
++#define LTQ_SPI_FSTAT         0x38    /* FIFO status */
++#define LTQ_SPI_BRT           0x40    /* Baudrate timer */
++#define LTQ_SPI_BRSTAT                0x44    /* Baudrate timer status */
++#define LTQ_SPI_SFCON         0x60    /* Serial frame control */
++#define LTQ_SPI_SFSTAT                0x64    /* Serial frame status */
++#define LTQ_SPI_GPOCON                0x70    /* General purpose output control */
++#define LTQ_SPI_GPOSTAT               0x74    /* General purpose output status */
++#define LTQ_SPI_FGPO          0x78    /* Forced general purpose output */
++#define LTQ_SPI_RXREQ         0x80    /* Receive request */
++#define LTQ_SPI_RXCNT         0x84    /* Receive count */
++#define LTQ_SPI_DMACON                0xEC    /* DMA control */
++#define LTQ_SPI_IRNEN         0xF4    /* Interrupt node enable */
++#define LTQ_SPI_IRNICR                0xF8    /* Interrupt node interrupt capture */
++#define LTQ_SPI_IRNCR         0xFC    /* Interrupt node control */
++
++#define LTQ_SPI_CLC_SMC_SHIFT 16      /* Clock divider for sleep mode */
++#define LTQ_SPI_CLC_SMC_MASK  0xFF
++#define LTQ_SPI_CLC_RMC_SHIFT 8       /* Clock divider for normal run mode */
++#define LTQ_SPI_CLC_RMC_MASK  0xFF
++#define LTQ_SPI_CLC_DISS      BIT(1)  /* Disable status bit */
++#define LTQ_SPI_CLC_DISR      BIT(0)  /* Disable request bit */
++
++#define LTQ_SPI_ID_TXFS_SHIFT 24      /* Implemented TX FIFO size */
++#define LTQ_SPI_ID_TXFS_MASK  0x3F
++#define LTQ_SPI_ID_RXFS_SHIFT 16      /* Implemented RX FIFO size */
++#define LTQ_SPI_ID_RXFS_MASK  0x3F
++#define LTQ_SPI_ID_REV_MASK   0x1F    /* Hardware revision number */
++#define LTQ_SPI_ID_CFG                BIT(5)  /* DMA interface support */
++
++#define LTQ_SPI_CON_BM_SHIFT  16      /* Data width selection */
++#define LTQ_SPI_CON_BM_MASK   0x1F
++#define LTQ_SPI_CON_EM                BIT(24) /* Echo mode */
++#define LTQ_SPI_CON_IDLE      BIT(23) /* Idle bit value */
++#define LTQ_SPI_CON_ENBV      BIT(22) /* Enable byte valid control */
++#define LTQ_SPI_CON_RUEN      BIT(12) /* Receive underflow error enable */
++#define LTQ_SPI_CON_TUEN      BIT(11) /* Transmit underflow error enable */
++#define LTQ_SPI_CON_AEN               BIT(10) /* Abort error enable */
++#define LTQ_SPI_CON_REN               BIT(9)  /* Receive overflow error enable */
++#define LTQ_SPI_CON_TEN               BIT(8)  /* Transmit overflow error enable */
++#define LTQ_SPI_CON_LB                BIT(7)  /* Loopback control */
++#define LTQ_SPI_CON_PO                BIT(6)  /* Clock polarity control */
++#define LTQ_SPI_CON_PH                BIT(5)  /* Clock phase control */
++#define LTQ_SPI_CON_HB                BIT(4)  /* Heading control */
++#define LTQ_SPI_CON_RXOFF     BIT(1)  /* Switch receiver off */
++#define LTQ_SPI_CON_TXOFF     BIT(0)  /* Switch transmitter off */
++
++#define LTQ_SPI_STAT_RXBV_MASK        0x7
++#define LTQ_SPI_STAT_RXBV_SHIFT       28
++#define LTQ_SPI_STAT_BSY      BIT(13) /* Busy flag */
++#define LTQ_SPI_STAT_RUE      BIT(12) /* Receive underflow error flag */
++#define LTQ_SPI_STAT_TUE      BIT(11) /* Transmit underflow error flag */
++#define LTQ_SPI_STAT_AE               BIT(10) /* Abort error flag */
++#define LTQ_SPI_STAT_RE               BIT(9)  /* Receive error flag */
++#define LTQ_SPI_STAT_TE               BIT(8)  /* Transmit error flag */
++#define LTQ_SPI_STAT_MS               BIT(1)  /* Master/slave select bit */
++#define LTQ_SPI_STAT_EN               BIT(0)  /* Enable bit */
++
++#define LTQ_SPI_WHBSTATE_SETTUE       BIT(15) /* Set transmit underflow error flag */
++#define LTQ_SPI_WHBSTATE_SETAE        BIT(14) /* Set abort error flag */
++#define LTQ_SPI_WHBSTATE_SETRE        BIT(13) /* Set receive error flag */
++#define LTQ_SPI_WHBSTATE_SETTE        BIT(12) /* Set transmit error flag */
++#define LTQ_SPI_WHBSTATE_CLRTUE       BIT(11) /* Clear transmit underflow error
++                                              flag */
++#define LTQ_SPI_WHBSTATE_CLRAE        BIT(10) /* Clear abort error flag */
++#define LTQ_SPI_WHBSTATE_CLRRE        BIT(9)  /* Clear receive error flag */
++#define LTQ_SPI_WHBSTATE_CLRTE        BIT(8)  /* Clear transmit error flag */
++#define LTQ_SPI_WHBSTATE_SETME        BIT(7)  /* Set mode error flag */
++#define LTQ_SPI_WHBSTATE_CLRME        BIT(6)  /* Clear mode error flag */
++#define LTQ_SPI_WHBSTATE_SETRUE       BIT(5)  /* Set receive underflow error flag */
++#define LTQ_SPI_WHBSTATE_CLRRUE       BIT(4)  /* Clear receive underflow error flag */
++#define LTQ_SPI_WHBSTATE_SETMS        BIT(3)  /* Set master select bit */
++#define LTQ_SPI_WHBSTATE_CLRMS        BIT(2)  /* Clear master select bit */
++#define LTQ_SPI_WHBSTATE_SETEN        BIT(1)  /* Set enable bit (operational mode) */
++#define LTQ_SPI_WHBSTATE_CLREN        BIT(0)  /* Clear enable bit (config mode */
++#define LTQ_SPI_WHBSTATE_CLR_ERRORS   0x0F50
++
++#define LTQ_SPI_RXFCON_RXFITL_SHIFT   8 /* FIFO interrupt trigger level */
++#define LTQ_SPI_RXFCON_RXFITL_MASK    0x3F
++#define LTQ_SPI_RXFCON_RXFLU          BIT(1)  /* FIFO flush */
++#define LTQ_SPI_RXFCON_RXFEN          BIT(0)  /* FIFO enable */
++
++#define LTQ_SPI_TXFCON_TXFITL_SHIFT   8 /* FIFO interrupt trigger level */
++#define LTQ_SPI_TXFCON_TXFITL_MASK    0x3F
++#define LTQ_SPI_TXFCON_TXFLU          BIT(1)  /* FIFO flush */
++#define LTQ_SPI_TXFCON_TXFEN          BIT(0)  /* FIFO enable */
++
++#define LTQ_SPI_FSTAT_RXFFL_MASK      0x3f
++#define LTQ_SPI_FSTAT_RXFFL_SHIFT     0
++#define LTQ_SPI_FSTAT_TXFFL_MASK      0x3f
++#define LTQ_SPI_FSTAT_TXFFL_SHIFT     8
++
++#define LTQ_SPI_GPOCON_ISCSBN_SHIFT   8
++#define LTQ_SPI_GPOCON_INVOUTN_SHIFT  0
++
++#define LTQ_SPI_FGPO_SETOUTN_SHIFT    8
++#define LTQ_SPI_FGPO_CLROUTN_SHIFT    0
++
++#define LTQ_SPI_RXREQ_RXCNT_MASK      0xFFFF  /* Receive count value */
++#define LTQ_SPI_RXCNT_TODO_MASK               0xFFFF  /* Recevie to-do value */
++
++#define LTQ_SPI_IRNEN_F               BIT(3)  /* Frame end interrupt request */
++#define LTQ_SPI_IRNEN_E               BIT(2)  /* Error end interrupt request */
++#define LTQ_SPI_IRNEN_T               BIT(1)  /* Transmit end interrupt request */
++#define LTQ_SPI_IRNEN_R               BIT(0)  /* Receive end interrupt request */
++#define LTQ_SPI_IRNEN_ALL     0xF
++
++struct ltq_spi {
++      struct spi_bitbang      bitbang;
++      struct completion       done;
++      spinlock_t              lock;
++
++      struct device           *dev;
++      void __iomem            *base;
++      struct clk              *fpiclk;
++      struct clk              *spiclk;
++
++      int                     status;
++      int                     irq[3];
++
++      const u8                *tx;
++      u8                      *rx;
++      u32                     tx_cnt;
++      u32                     rx_cnt;
++      u32                     len;
++      struct spi_transfer     *curr_transfer;
++
++      u32 (*get_tx) (struct ltq_spi *);
++
++      u16                     txfs;
++      u16                     rxfs;
++      unsigned                dma_support:1;
++      unsigned                cfg_mode:1;
++};
++
++static inline struct ltq_spi *ltq_spi_to_hw(struct spi_device *spi)
++{
++      return spi_master_get_devdata(spi->master);
++}
++
++static inline u32 ltq_spi_reg_read(struct ltq_spi *hw, u32 reg)
++{
++      return ioread32be(hw->base + reg);
++}
++
++static inline void ltq_spi_reg_write(struct ltq_spi *hw, u32 val, u32 reg)
++{
++      iowrite32be(val, hw->base + reg);
++}
++
++static inline void ltq_spi_reg_setbit(struct ltq_spi *hw, u32 bits, u32 reg)
++{
++      u32 val;
++
++      val = ltq_spi_reg_read(hw, reg);
++      val |= bits;
++      ltq_spi_reg_write(hw, val, reg);
++}
++
++static inline void ltq_spi_reg_clearbit(struct ltq_spi *hw, u32 bits, u32 reg)
++{
++      u32 val;
++
++      val = ltq_spi_reg_read(hw, reg);
++      val &= ~bits;
++      ltq_spi_reg_write(hw, val, reg);
++}
++
++static void ltq_spi_hw_enable(struct ltq_spi *hw)
++{
++      u32 clc;
++
++      /* Power-up module */
++      clk_enable(hw->spiclk);
++
++      /*
++       * Set clock divider for run mode to 1 to
++       * run at same frequency as FPI bus
++       */
++      clc = (1 << LTQ_SPI_CLC_RMC_SHIFT);
++      ltq_spi_reg_write(hw, clc, LTQ_SPI_CLC);
++}
++
++static void ltq_spi_hw_disable(struct ltq_spi *hw)
++{
++      /* Set clock divider to 0 and set module disable bit */
++      ltq_spi_reg_write(hw, LTQ_SPI_CLC_DISS, LTQ_SPI_CLC);
++
++      /* Power-down module */
++      clk_disable(hw->spiclk);
++}
++
++static void ltq_spi_reset_fifos(struct ltq_spi *hw)
++{
++      u32 val;
++
++      /*
++       * Enable and flush FIFOs. Set interrupt trigger level to
++       * half of FIFO count implemented in hardware.
++       */
++      if (hw->txfs > 1) {
++              val = hw->txfs << (LTQ_SPI_TXFCON_TXFITL_SHIFT - 1);
++              val |= LTQ_SPI_TXFCON_TXFEN | LTQ_SPI_TXFCON_TXFLU;
++              ltq_spi_reg_write(hw, val, LTQ_SPI_TXFCON);
++      }
++
++      if (hw->rxfs > 1) {
++              val = hw->rxfs << (LTQ_SPI_RXFCON_RXFITL_SHIFT - 1);
++              val |= LTQ_SPI_RXFCON_RXFEN | LTQ_SPI_RXFCON_RXFLU;
++              ltq_spi_reg_write(hw, val, LTQ_SPI_RXFCON);
++      }
++}
++
++static inline int ltq_spi_wait_ready(struct ltq_spi *hw)
++{
++      u32 stat;
++      unsigned long timeout;
++
++      timeout = jiffies + msecs_to_jiffies(200);
++
++      do {
++              stat = ltq_spi_reg_read(hw, LTQ_SPI_STAT);
++              if (!(stat & LTQ_SPI_STAT_BSY))
++                      return 0;
++
++              cond_resched();
++      } while (!time_after_eq(jiffies, timeout));
++
++      dev_err(hw->dev, "SPI wait ready timed out stat: %x\n", stat);
++
++      return -ETIMEDOUT;
++}
++
++static void ltq_spi_config_mode_set(struct ltq_spi *hw)
++{
++      if (hw->cfg_mode)
++              return;
++
++      /*
++       * Putting the SPI module in config mode is only safe if no
++       * transfer is in progress as indicated by busy flag STATE.BSY.
++       */
++      if (ltq_spi_wait_ready(hw)) {
++              ltq_spi_reset_fifos(hw);
++              hw->status = -ETIMEDOUT;
++      }
++      ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_CLREN, LTQ_SPI_WHBSTATE);
++
++      hw->cfg_mode = 1;
++}
++
++static void ltq_spi_run_mode_set(struct ltq_spi *hw)
++{
++      if (!hw->cfg_mode)
++              return;
++
++      ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_SETEN, LTQ_SPI_WHBSTATE);
++
++      hw->cfg_mode = 0;
++}
++
++static u32 ltq_spi_tx_word_u8(struct ltq_spi *hw)
++{
++      const u8 *tx = hw->tx;
++      u32 data = *tx++;
++
++      hw->tx_cnt++;
++      hw->tx++;
++
++      return data;
++}
++
++static u32 ltq_spi_tx_word_u16(struct ltq_spi *hw)
++{
++      const u16 *tx = (u16 *) hw->tx;
++      u32 data = *tx++;
++
++      hw->tx_cnt += 2;
++      hw->tx += 2;
++
++      return data;
++}
++
++static u32 ltq_spi_tx_word_u32(struct ltq_spi *hw)
++{
++      const u32 *tx = (u32 *) hw->tx;
++      u32 data = *tx++;
++
++      hw->tx_cnt += 4;
++      hw->tx += 4;
++
++      return data;
++}
++
++static void ltq_spi_bits_per_word_set(struct spi_device *spi)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u32 bm;
++      u8 bits_per_word = spi->bits_per_word;
++
++      /*
++       * Use either default value of SPI device or value
++       * from current transfer.
++       */
++      if (hw->curr_transfer && hw->curr_transfer->bits_per_word)
++              bits_per_word = hw->curr_transfer->bits_per_word;
++
++      if (bits_per_word <= 8)
++              hw->get_tx = ltq_spi_tx_word_u8;
++      else if (bits_per_word <= 16)
++              hw->get_tx = ltq_spi_tx_word_u16;
++      else if (bits_per_word <= 32)
++              hw->get_tx = ltq_spi_tx_word_u32;
++
++      /* CON.BM value = bits_per_word - 1 */
++      bm = (bits_per_word - 1) << LTQ_SPI_CON_BM_SHIFT;
++
++      ltq_spi_reg_clearbit(hw, LTQ_SPI_CON_BM_MASK <<
++                           LTQ_SPI_CON_BM_SHIFT, LTQ_SPI_CON);
++      ltq_spi_reg_setbit(hw, bm, LTQ_SPI_CON);
++}
++
++static void ltq_spi_speed_set(struct spi_device *spi)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u32 br, max_speed_hz, spi_clk;
++      u32 speed_hz = spi->max_speed_hz;
++
++      /*
++       * Use either default value of SPI device or value
++       * from current transfer.
++       */
++      if (hw->curr_transfer && hw->curr_transfer->speed_hz)
++              speed_hz = hw->curr_transfer->speed_hz;
++
++      /*
++       * SPI module clock is derived from FPI bus clock dependent on
++       * divider value in CLC.RMS which is always set to 1.
++       */
++      spi_clk = clk_get_rate(hw->fpiclk);
++
++      /*
++       * Maximum SPI clock frequency in master mode is half of
++       * SPI module clock frequency. Maximum reload value of
++       * baudrate generator BR is 2^16.
++       */
++      max_speed_hz = spi_clk / 2;
++      if (speed_hz >= max_speed_hz)
++              br = 0;
++      else
++              br = (max_speed_hz / speed_hz) - 1;
++
++      if (br > 0xFFFF)
++              br = 0xFFFF;
++
++      ltq_spi_reg_write(hw, br, LTQ_SPI_BRT);
++}
++
++static void ltq_spi_clockmode_set(struct spi_device *spi)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u32 con;
++
++      con = ltq_spi_reg_read(hw, LTQ_SPI_CON);
++
++      /*
++       * SPI mode mapping in CON register:
++       * Mode CPOL CPHA CON.PO CON.PH
++       *  0    0    0      0      1
++       *  1    0    1      0      0
++       *  2    1    0      1      1
++       *  3    1    1      1      0
++       */
++      if (spi->mode & SPI_CPHA)
++              con &= ~LTQ_SPI_CON_PH;
++      else
++              con |= LTQ_SPI_CON_PH;
++
++      if (spi->mode & SPI_CPOL)
++              con |= LTQ_SPI_CON_PO;
++      else
++              con &= ~LTQ_SPI_CON_PO;
++
++      /* Set heading control */
++      if (spi->mode & SPI_LSB_FIRST)
++              con &= ~LTQ_SPI_CON_HB;
++      else
++              con |= LTQ_SPI_CON_HB;
++
++      ltq_spi_reg_write(hw, con, LTQ_SPI_CON);
++}
++
++static void ltq_spi_xmit_set(struct ltq_spi *hw, struct spi_transfer *t)
++{
++      u32 con;
++
++      con = ltq_spi_reg_read(hw, LTQ_SPI_CON);
++
++      if (t) {
++              if (t->tx_buf && t->rx_buf) {
++                      con &= ~(LTQ_SPI_CON_TXOFF | LTQ_SPI_CON_RXOFF);
++              } else if (t->rx_buf) {
++                      con &= ~LTQ_SPI_CON_RXOFF;
++                      con |= LTQ_SPI_CON_TXOFF;
++              } else if (t->tx_buf) {
++                      con &= ~LTQ_SPI_CON_TXOFF;
++                      con |= LTQ_SPI_CON_RXOFF;
++              }
++      } else
++              con |= (LTQ_SPI_CON_TXOFF | LTQ_SPI_CON_RXOFF);
++
++      ltq_spi_reg_write(hw, con, LTQ_SPI_CON);
++}
++
++static void ltq_spi_internal_cs_activate(struct spi_device *spi)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u32 fgpo;
++
++      fgpo = (1 << (spi->chip_select + LTQ_SPI_FGPO_CLROUTN_SHIFT));
++      ltq_spi_reg_setbit(hw, fgpo, LTQ_SPI_FGPO);
++}
++
++static void ltq_spi_internal_cs_deactivate(struct spi_device *spi)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u32 fgpo;
++
++      fgpo = (1 << (spi->chip_select + LTQ_SPI_FGPO_SETOUTN_SHIFT));
++      ltq_spi_reg_setbit(hw, fgpo, LTQ_SPI_FGPO);
++}
++
++static void ltq_spi_chipselect(struct spi_device *spi, int cs)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++
++      switch (cs) {
++      case BITBANG_CS_ACTIVE:
++              ltq_spi_bits_per_word_set(spi);
++              ltq_spi_speed_set(spi);
++              ltq_spi_clockmode_set(spi);
++              ltq_spi_run_mode_set(hw);
++              ltq_spi_internal_cs_activate(spi);
++              break;
++
++      case BITBANG_CS_INACTIVE:
++              ltq_spi_internal_cs_deactivate(spi);
++              ltq_spi_config_mode_set(hw);
++              break;
++      }
++}
++
++static int ltq_spi_setup_transfer(struct spi_device *spi,
++                                struct spi_transfer *t)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u8 bits_per_word = spi->bits_per_word;
++
++      hw->curr_transfer = t;
++
++      if (t && t->bits_per_word)
++              bits_per_word = t->bits_per_word;
++
++      if (bits_per_word > 32)
++              return -EINVAL;
++
++      ltq_spi_config_mode_set(hw);
++
++      return 0;
++}
++
++static int ltq_spi_setup(struct spi_device *spi)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u32 gpocon, fgpo;
++
++      /* Set default word length to 8 if not set */
++      if (!spi->bits_per_word)
++              spi->bits_per_word = 8;
++
++      if (spi->bits_per_word > 32)
++              return -EINVAL;
++
++      /*
++       * Up to six GPIOs can be connected to the SPI module
++       * via GPIO alternate function to control the chip select lines.
++       */
++      gpocon = (1 << (spi->chip_select +
++                      LTQ_SPI_GPOCON_ISCSBN_SHIFT));
++
++      if (spi->mode & SPI_CS_HIGH)
++              gpocon |= (1 << spi->chip_select);
++
++      fgpo = (1 << (spi->chip_select + LTQ_SPI_FGPO_SETOUTN_SHIFT));
++
++      ltq_spi_reg_setbit(hw, gpocon, LTQ_SPI_GPOCON);
++      ltq_spi_reg_setbit(hw, fgpo, LTQ_SPI_FGPO);
++
++      return 0;
++}
++
++static void ltq_spi_cleanup(struct spi_device *spi)
++{
++
++}
++
++static void ltq_spi_txfifo_write(struct ltq_spi *hw)
++{
++      u32 fstat, data;
++      u16 fifo_space;
++
++      /* Determine how much FIFOs are free for TX data */
++      fstat = ltq_spi_reg_read(hw, LTQ_SPI_FSTAT);
++      fifo_space = hw->txfs - ((fstat >> LTQ_SPI_FSTAT_TXFFL_SHIFT) &
++                                      LTQ_SPI_FSTAT_TXFFL_MASK);
++
++      if (!fifo_space)
++              return;
++
++      while (hw->tx_cnt < hw->len && fifo_space) {
++              data = hw->get_tx(hw);
++              ltq_spi_reg_write(hw, data, LTQ_SPI_TB);
++              fifo_space--;
++      }
++}
++
++static void ltq_spi_rxfifo_read(struct ltq_spi *hw)
++{
++      u32 fstat, data, *rx32;
++      u16 fifo_fill;
++      u8 rxbv, shift, *rx8;
++
++      /* Determine how much FIFOs are filled with RX data */
++      fstat = ltq_spi_reg_read(hw, LTQ_SPI_FSTAT);
++      fifo_fill = ((fstat >> LTQ_SPI_FSTAT_RXFFL_SHIFT)
++                      & LTQ_SPI_FSTAT_RXFFL_MASK);
++
++      if (!fifo_fill)
++              return;
++
++      /*
++       * The 32 bit FIFO is always used completely independent from the
++       * bits_per_word value. Thus four bytes have to be read at once
++       * per FIFO.
++       */
++      rx32 = (u32 *) hw->rx;
++      while (hw->len - hw->rx_cnt >= 4 && fifo_fill) {
++              *rx32++ = ltq_spi_reg_read(hw, LTQ_SPI_RB);
++              hw->rx_cnt += 4;
++              hw->rx += 4;
++              fifo_fill--;
++      }
++
++      /*
++       * If there are remaining bytes, read byte count from STAT.RXBV
++       * register and read the data byte-wise.
++       */
++      while (fifo_fill && hw->rx_cnt < hw->len) {
++              rxbv = (ltq_spi_reg_read(hw, LTQ_SPI_STAT) >>
++                      LTQ_SPI_STAT_RXBV_SHIFT) & LTQ_SPI_STAT_RXBV_MASK;
++              data = ltq_spi_reg_read(hw, LTQ_SPI_RB);
++
++              shift = (rxbv - 1) * 8;
++              rx8 = hw->rx;
++
++              while (rxbv) {
++                      *rx8++ = (data >> shift) & 0xFF;
++                      rxbv--;
++                      shift -= 8;
++                      hw->rx_cnt++;
++                      hw->rx++;
++              }
++
++              fifo_fill--;
++      }
++}
++
++static void ltq_spi_rxreq_set(struct ltq_spi *hw)
++{
++      u32 rxreq, rxreq_max, rxtodo;
++
++      rxtodo = ltq_spi_reg_read(hw, LTQ_SPI_RXCNT) & LTQ_SPI_RXCNT_TODO_MASK;
++
++      /*
++       * In RX-only mode the serial clock is activated only after writing
++       * the expected amount of RX bytes into RXREQ register.
++       * To avoid receive overflows at high clocks it is better to request
++       * only the amount of bytes that fits into all FIFOs. This value
++       * depends on the FIFO size implemented in hardware.
++       */
++      rxreq = hw->len - hw->rx_cnt;
++      rxreq_max = hw->rxfs << 2;
++      rxreq = min(rxreq_max, rxreq);
++
++      if (!rxtodo && rxreq)
++              ltq_spi_reg_write(hw, rxreq, LTQ_SPI_RXREQ);
++}
++
++static inline void ltq_spi_complete(struct ltq_spi *hw)
++{
++      complete(&hw->done);
++}
++
++irqreturn_t ltq_spi_tx_irq(int irq, void *data)
++{
++      struct ltq_spi *hw = data;
++      unsigned long flags;
++      int completed = 0;
++
++      spin_lock_irqsave(&hw->lock, flags);
++
++      if (hw->tx_cnt < hw->len)
++              ltq_spi_txfifo_write(hw);
++
++      if (hw->tx_cnt == hw->len)
++              completed = 1;
++
++      spin_unlock_irqrestore(&hw->lock, flags);
++
++      if (completed)
++              ltq_spi_complete(hw);
++
++      return IRQ_HANDLED;
++}
++
++irqreturn_t ltq_spi_rx_irq(int irq, void *data)
++{
++      struct ltq_spi *hw = data;
++      unsigned long flags;
++      int completed = 0;
++
++      spin_lock_irqsave(&hw->lock, flags);
++
++      if (hw->rx_cnt < hw->len) {
++              ltq_spi_rxfifo_read(hw);
++
++              if (hw->tx && hw->tx_cnt < hw->len)
++                      ltq_spi_txfifo_write(hw);
++      }
++
++      if (hw->rx_cnt == hw->len)
++              completed = 1;
++      else if (!hw->tx)
++              ltq_spi_rxreq_set(hw);
++
++      spin_unlock_irqrestore(&hw->lock, flags);
++
++      if (completed)
++              ltq_spi_complete(hw);
++
++      return IRQ_HANDLED;
++}
++
++irqreturn_t ltq_spi_err_irq(int irq, void *data)
++{
++      struct ltq_spi *hw = data;
++      unsigned long flags;
++
++      spin_lock_irqsave(&hw->lock, flags);
++
++      /* Disable all interrupts */
++      ltq_spi_reg_clearbit(hw, LTQ_SPI_IRNEN_ALL, LTQ_SPI_IRNEN);
++
++      /* Clear all error flags */
++      ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_CLR_ERRORS, LTQ_SPI_WHBSTATE);
++
++      /* Flush FIFOs */
++      ltq_spi_reg_setbit(hw, LTQ_SPI_RXFCON_RXFLU, LTQ_SPI_RXFCON);
++      ltq_spi_reg_setbit(hw, LTQ_SPI_TXFCON_TXFLU, LTQ_SPI_TXFCON);
++
++      hw->status = -EIO;
++      spin_unlock_irqrestore(&hw->lock, flags);
++
++      ltq_spi_complete(hw);
++
++      return IRQ_HANDLED;
++}
++
++static int ltq_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
++{
++      struct ltq_spi *hw = ltq_spi_to_hw(spi);
++      u32 irq_flags = 0;
++
++      hw->tx = t->tx_buf;
++      hw->rx = t->rx_buf;
++      hw->len = t->len;
++      hw->tx_cnt = 0;
++      hw->rx_cnt = 0;
++      hw->status = 0;
++      INIT_COMPLETION(hw->done);
++
++      ltq_spi_xmit_set(hw, t);
++
++      /* Enable error interrupts */
++      ltq_spi_reg_setbit(hw, LTQ_SPI_IRNEN_E, LTQ_SPI_IRNEN);
++
++      if (hw->tx) {
++              /* Initially fill TX FIFO with as much data as possible */
++              ltq_spi_txfifo_write(hw);
++              irq_flags |= LTQ_SPI_IRNEN_T;
++
++              /* Always enable RX interrupt in Full Duplex mode */
++              if (hw->rx)
++                      irq_flags |= LTQ_SPI_IRNEN_R;
++      } else if (hw->rx) {
++              /* Start RX clock */
++              ltq_spi_rxreq_set(hw);
++
++              /* Enable RX interrupt to receive data from RX FIFOs */
++              irq_flags |= LTQ_SPI_IRNEN_R;
++      }
++
++      /* Enable TX or RX interrupts */
++      ltq_spi_reg_setbit(hw, irq_flags, LTQ_SPI_IRNEN);
++      wait_for_completion_interruptible(&hw->done);
++
++      /* Disable all interrupts */
++      ltq_spi_reg_clearbit(hw, LTQ_SPI_IRNEN_ALL, LTQ_SPI_IRNEN);
++
++      /*
++       * Return length of current transfer for bitbang utility code if
++       * no errors occured during transmission.
++       */
++      if (!hw->status)
++              hw->status = hw->len;
++
++      return hw->status;
++}
++
++static const struct ltq_spi_irq_map {
++      char *name;
++      irq_handler_t handler;
++} ltq_spi_irqs[] = {
++      { "spi_rx", ltq_spi_rx_irq },
++      { "spi_tx", ltq_spi_tx_irq },
++      { "spi_err", ltq_spi_err_irq },
++};
++
++static int ltq_spi_probe(struct platform_device *pdev)
++{
++      struct resource irqres[3];
++      struct spi_master *master;
++      struct resource *r;
++      struct ltq_spi *hw;
++      int ret, i;
++      u32 data, id;
++
++      if (of_irq_to_resource_table(pdev->dev.of_node, irqres, 3) != 3) {
++              dev_err(&pdev->dev, "IRQ settings missing in device tree\n");
++              return -EINVAL;
++      }
++
++      master = spi_alloc_master(&pdev->dev, sizeof(struct ltq_spi));
++      if (!master) {
++              dev_err(&pdev->dev, "spi_alloc_master\n");
++              ret = -ENOMEM;
++              goto err;
++      }
++
++      hw = spi_master_get_devdata(master);
++
++      r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      if (r == NULL) {
++              dev_err(&pdev->dev, "platform_get_resource\n");
++              ret = -ENOENT;
++              goto err_master;
++      }
++
++      r = devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
++                      pdev->name);
++      if (!r) {
++              dev_err(&pdev->dev, "failed to request memory region\n");
++              ret = -ENXIO;
++              goto err_master;
++      }
++
++      hw->base = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
++      if (!hw->base) {
++              dev_err(&pdev->dev, "failed to remap memory region\n");
++              ret = -ENXIO;
++              goto err_master;
++      }
++
++      memset(hw->irq, 0, sizeof(hw->irq));
++      for (i = 0; i < ARRAY_SIZE(ltq_spi_irqs); i++) {
++              hw->irq[i] = irqres[i].start;
++              ret = request_irq(hw->irq[i], ltq_spi_irqs[i].handler,
++                                0, ltq_spi_irqs[i].name, hw);
++              if (ret) {
++                      dev_err(&pdev->dev, "failed to request %s irq (%d)\n",
++                                      ltq_spi_irqs[i].name, hw->irq[i]);
++                      goto err_irq;
++              }
++      }
++
++      hw->fpiclk = clk_get_fpi();
++      if (IS_ERR(hw->fpiclk)) {
++              dev_err(&pdev->dev, "failed to get fpi clock\n");
++              ret = PTR_ERR(hw->fpiclk);
++              goto err_clk;
++      }
++
++      hw->spiclk = clk_get(&pdev->dev, NULL);
++      if (IS_ERR(hw->spiclk)) {
++              dev_err(&pdev->dev, "failed to get spi clock gate\n");
++              ret = PTR_ERR(hw->spiclk);
++              goto err_clk;
++      }
++
++      hw->bitbang.master = spi_master_get(master);
++      hw->bitbang.chipselect = ltq_spi_chipselect;
++      hw->bitbang.setup_transfer = ltq_spi_setup_transfer;
++      hw->bitbang.txrx_bufs = ltq_spi_txrx_bufs;
++
++      if (of_machine_is_compatible("lantiq,ase"))
++              master->num_chipselect = 3;
++      else
++              master->num_chipselect = 6;
++      master->bus_num = pdev->id;
++      master->setup = ltq_spi_setup;
++      master->cleanup = ltq_spi_cleanup;
++      master->dev.of_node = pdev->dev.of_node;
++
++      hw->dev = &pdev->dev;
++      init_completion(&hw->done);
++      spin_lock_init(&hw->lock);
++
++      ltq_spi_hw_enable(hw);
++
++      /* Read module capabilities */
++      id = ltq_spi_reg_read(hw, LTQ_SPI_ID);
++      hw->txfs = (id >> LTQ_SPI_ID_TXFS_SHIFT) & LTQ_SPI_ID_TXFS_MASK;
++      hw->rxfs = (id >> LTQ_SPI_ID_TXFS_SHIFT) & LTQ_SPI_ID_TXFS_MASK;
++      hw->dma_support = (id & LTQ_SPI_ID_CFG) ? 1 : 0;
++
++      ltq_spi_config_mode_set(hw);
++
++      /* Enable error checking, disable TX/RX, set idle value high */
++      data = LTQ_SPI_CON_RUEN | LTQ_SPI_CON_AEN |
++          LTQ_SPI_CON_TEN | LTQ_SPI_CON_REN |
++          LTQ_SPI_CON_TXOFF | LTQ_SPI_CON_RXOFF | LTQ_SPI_CON_IDLE;
++      ltq_spi_reg_write(hw, data, LTQ_SPI_CON);
++
++      /* Enable master mode and clear error flags */
++      ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_SETMS |
++                        LTQ_SPI_WHBSTATE_CLR_ERRORS, LTQ_SPI_WHBSTATE);
++
++      /* Reset GPIO/CS registers */
++      ltq_spi_reg_write(hw, 0x0, LTQ_SPI_GPOCON);
++      ltq_spi_reg_write(hw, 0xFF00, LTQ_SPI_FGPO);
++
++      /* Enable and flush FIFOs */
++      ltq_spi_reset_fifos(hw);
++
++      ret = spi_bitbang_start(&hw->bitbang);
++      if (ret) {
++              dev_err(&pdev->dev, "spi_bitbang_start failed\n");
++              goto err_bitbang;
++      }
++
++      platform_set_drvdata(pdev, hw);
++
++      pr_info("Lantiq SoC SPI controller rev %u (TXFS %u, RXFS %u, DMA %u)\n",
++              id & LTQ_SPI_ID_REV_MASK, hw->txfs, hw->rxfs, hw->dma_support);
++
++      return 0;
++
++err_bitbang:
++      ltq_spi_hw_disable(hw);
++
++err_clk:
++      if (hw->fpiclk)
++              clk_put(hw->fpiclk);
++      if (hw->spiclk)
++              clk_put(hw->spiclk);
++
++err_irq:
++      clk_put(hw->fpiclk);
++
++      for (; i > 0; i--)
++              free_irq(hw->irq[i], hw);
++
++err_master:
++      spi_master_put(master);
++
++err:
++      return ret;
++}
++
++static int ltq_spi_remove(struct platform_device *pdev)
++{
++      struct ltq_spi *hw = platform_get_drvdata(pdev);
++      int ret, i;
++
++      ret = spi_bitbang_stop(&hw->bitbang);
++      if (ret)
++              return ret;
++
++      platform_set_drvdata(pdev, NULL);
++
++      ltq_spi_config_mode_set(hw);
++      ltq_spi_hw_disable(hw);
++
++      for (i = 0; i < ARRAY_SIZE(hw->irq); i++)
++              if (0 < hw->irq[i])
++                      free_irq(hw->irq[i], hw);
++
++      if (hw->fpiclk)
++              clk_put(hw->fpiclk);
++      if (hw->spiclk)
++              clk_put(hw->spiclk);
++
++      spi_master_put(hw->bitbang.master);
++
++      return 0;
++}
++
++static const struct of_device_id ltq_spi_match[] = {
++      { .compatible = "lantiq,spi-xway" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, ltq_spi_match);
++
++static struct platform_driver ltq_spi_driver = {
++      .probe = ltq_spi_probe,
++      .remove = ltq_spi_remove,
++      .driver = {
++              .name = "spi-xway",
++              .owner = THIS_MODULE,
++              .of_match_table = ltq_spi_match,
++      },
++};
++
++module_platform_driver(ltq_spi_driver);
++
++MODULE_DESCRIPTION("Lantiq SoC SPI controller driver");
++MODULE_AUTHOR("Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:spi-xway");
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0025-NET-MIPS-lantiq-adds-xrx200-net.patch b/target/linux/lantiq/patches-3.8/0025-NET-MIPS-lantiq-adds-xrx200-net.patch
new file mode 100644 (file)
index 0000000..bc2b70d
--- /dev/null
@@ -0,0 +1,1426 @@
+From fbfdf78ba827a8f854ae3ed7b11ea6df4054ffb1 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Mon, 22 Oct 2012 12:22:23 +0200
+Subject: [PATCH 25/40] NET: MIPS: lantiq: adds xrx200-net
+
+---
+ drivers/net/ethernet/Kconfig         |    8 +-
+ drivers/net/ethernet/Makefile        |    1 +
+ drivers/net/ethernet/lantiq_pce.h    |  163 +++++
+ drivers/net/ethernet/lantiq_xrx200.c | 1203 ++++++++++++++++++++++++++++++++++
+ 4 files changed, 1374 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/lantiq_pce.h
+ create mode 100644 drivers/net/ethernet/lantiq_xrx200.c
+
+diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
+index e4ff389..35cb7b0 100644
+--- a/drivers/net/ethernet/Kconfig
++++ b/drivers/net/ethernet/Kconfig
+@@ -83,7 +83,13 @@ config LANTIQ_ETOP
+       tristate "Lantiq SoC ETOP driver"
+       depends on SOC_TYPE_XWAY
+       ---help---
+-        Support for the MII0 inside the Lantiq SoC
++        Support for the MII0 inside the Lantiq ADSL SoC
++
++config LANTIQ_XRX200
++      tristate "Lantiq SoC XRX200 driver"
++      depends on SOC_TYPE_XWAY
++      ---help---
++        Support for the MII0 inside the Lantiq VDSL SoC
+ source "drivers/net/ethernet/marvell/Kconfig"
+ source "drivers/net/ethernet/mellanox/Kconfig"
+diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
+index d447307..4f95100 100644
+--- a/drivers/net/ethernet/Makefile
++++ b/drivers/net/ethernet/Makefile
+@@ -36,6 +36,7 @@ obj-$(CONFIG_IP1000) += icplus/
+ obj-$(CONFIG_JME) += jme.o
+ obj-$(CONFIG_KORINA) += korina.o
+ obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
++obj-$(CONFIG_LANTIQ_XRX200) += lantiq_xrx200.o
+ obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
+ obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
+ obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
+diff --git a/drivers/net/ethernet/lantiq_pce.h b/drivers/net/ethernet/lantiq_pce.h
+new file mode 100644
+index 0000000..0c38efe
+--- /dev/null
++++ b/drivers/net/ethernet/lantiq_pce.h
+@@ -0,0 +1,163 @@
++/*
++ *   This program is free software; you can redistribute it and/or modify it
++ *   under the terms of the GNU General Public License version 2 as published
++ *   by the Free Software Foundation.
++ *
++ *   This program is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   You should have received a copy of the GNU General Public License
++ *   along with this program; if not, write to the Free Software
++ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *   Copyright (C) 2010 Lantiq Deutschland GmbH
++ *   Copyright (C) 2012 John Crispin <blogic@openwrt.org>
++ *
++ *   PCE microcode extracted from UGW5.2 switch api
++ */
++
++/* Switch API Micro Code V0.3 */
++enum {
++      OUT_MAC0 = 0,
++      OUT_MAC1,
++      OUT_MAC2,
++      OUT_MAC3,
++      OUT_MAC4,
++      OUT_MAC5,
++      OUT_ETHTYP,
++      OUT_VTAG0,
++      OUT_VTAG1,
++      OUT_ITAG0,
++      OUT_ITAG1,      /*10 */
++      OUT_ITAG2,
++      OUT_ITAG3,
++      OUT_IP0,
++      OUT_IP1,
++      OUT_IP2,
++      OUT_IP3,
++      OUT_SIP0,
++      OUT_SIP1,
++      OUT_SIP2,
++      OUT_SIP3,       /*20*/
++      OUT_SIP4,
++      OUT_SIP5,
++      OUT_SIP6,
++      OUT_SIP7,
++      OUT_DIP0,
++      OUT_DIP1,
++      OUT_DIP2,
++      OUT_DIP3,
++      OUT_DIP4,
++      OUT_DIP5,       /*30*/
++      OUT_DIP6,
++      OUT_DIP7,
++      OUT_SESID,
++      OUT_PROT,
++      OUT_APP0,
++      OUT_APP1,
++      OUT_IGMP0,
++      OUT_IGMP1,
++      OUT_IPOFF,      /*39*/
++      OUT_NONE =  63
++};
++
++/* parser's microcode length type */
++#define INSTR         0
++#define IPV6          1
++#define LENACCU               2
++
++/* parser's microcode flag type */
++enum {
++      FLAG_ITAG =  0,
++      FLAG_VLAN,
++      FLAG_SNAP,
++      FLAG_PPPOE,
++      FLAG_IPV6,
++      FLAG_IPV6FL,
++      FLAG_IPV4,
++      FLAG_IGMP,
++      FLAG_TU,
++      FLAG_HOP,
++      FLAG_NN1,       /*10 */
++      FLAG_NN2,
++      FLAG_END,
++      FLAG_NO,        /*13*/
++};
++
++/* Micro code version V2_11 (extension for parsing IPv6 in PPPoE) */
++#define MC_ENTRY(val, msk, ns, out, len, type, flags, ipv4_len) \
++      { {val, msk, (ns<<10 | out<<4 | len>>1), (len&1)<<15 | type<<13 | flags<<9 | ipv4_len<<8 }}
++struct pce_microcode {
++      unsigned short val[4];
++/*    unsigned short val_2;
++      unsigned short val_1;
++      unsigned short val_0;*/
++} pce_microcode[] = {
++      /*      value    mask    ns  fields      L  type     flags       ipv4_len */
++      MC_ENTRY(0x88c3, 0xFFFF,  1, OUT_ITAG0,  4, INSTR,   FLAG_ITAG,  0),
++      MC_ENTRY(0x8100, 0xFFFF,  2, OUT_VTAG0,  2, INSTR,   FLAG_VLAN,  0),
++      MC_ENTRY(0x88A8, 0xFFFF,  1, OUT_VTAG0,  2, INSTR,   FLAG_VLAN,  0),
++      MC_ENTRY(0x8100, 0xFFFF,  1, OUT_VTAG0,  2, INSTR,   FLAG_VLAN,  0),
++      MC_ENTRY(0x8864, 0xFFFF, 17, OUT_ETHTYP, 1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0800, 0xFFFF, 21, OUT_ETHTYP, 1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x86DD, 0xFFFF, 22, OUT_ETHTYP, 1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x8863, 0xFFFF, 16, OUT_ETHTYP, 1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0xF800, 10, OUT_NONE,   0, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 38, OUT_ETHTYP, 1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0600, 0x0600, 38, OUT_ETHTYP, 1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 12, OUT_NONE,   1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0xAAAA, 0xFFFF, 14, OUT_NONE,   1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0300, 0xFF00, 39, OUT_NONE,   0, INSTR,   FLAG_SNAP,  0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_DIP7,   3, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 18, OUT_DIP7,   3, INSTR,   FLAG_PPPOE, 0),
++      MC_ENTRY(0x0021, 0xFFFF, 21, OUT_NONE,   1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0057, 0xFFFF, 22, OUT_NONE,   1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x4000, 0xF000, 24, OUT_IP0,    4, INSTR,   FLAG_IPV4,  1),
++      MC_ENTRY(0x6000, 0xF000, 27, OUT_IP0,    3, INSTR,   FLAG_IPV6,  0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 25, OUT_IP3,    2, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 26, OUT_SIP0,   4, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 38, OUT_NONE,   0, LENACCU, FLAG_NO,    0),
++      MC_ENTRY(0x1100, 0xFF00, 37, OUT_PROT,   1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0600, 0xFF00, 37, OUT_PROT,   1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0xFF00, 33, OUT_IP3,   17, INSTR,   FLAG_HOP,   0),
++      MC_ENTRY(0x2B00, 0xFF00, 33, OUT_IP3,   17, INSTR,   FLAG_NN1,   0),
++      MC_ENTRY(0x3C00, 0xFF00, 33, OUT_IP3,   17, INSTR,   FLAG_NN2,   0),
++      MC_ENTRY(0x0000, 0x0000, 37, OUT_PROT,   1, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0xFF00, 33, OUT_NONE,   0, IPV6,    FLAG_HOP,   0),
++      MC_ENTRY(0x2B00, 0xFF00, 33, OUT_NONE,   0, IPV6,    FLAG_NN1,   0),
++      MC_ENTRY(0x3C00, 0xFF00, 33, OUT_NONE,   0, IPV6,    FLAG_NN2,   0),
++      MC_ENTRY(0x0000, 0x0000, 38, OUT_PROT,   1, IPV6,    FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 38, OUT_SIP0,  16, INSTR,   FLAG_NO,    0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_APP0,   4, INSTR,   FLAG_IGMP,  0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++      MC_ENTRY(0x0000, 0x0000, 39, OUT_NONE,   0, INSTR,   FLAG_END,   0),
++};
+diff --git a/drivers/net/ethernet/lantiq_xrx200.c b/drivers/net/ethernet/lantiq_xrx200.c
+new file mode 100644
+index 0000000..f815165
+--- /dev/null
++++ b/drivers/net/ethernet/lantiq_xrx200.c
+@@ -0,0 +1,1203 @@
++/*
++ *   This program is free software; you can redistribute it and/or modify it
++ *   under the terms of the GNU General Public License version 2 as published
++ *   by the Free Software Foundation.
++ *
++ *   This program is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   You should have received a copy of the GNU General Public License
++ *   along with this program; if not, write to the Free Software
++ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *   Copyright (C) 2010 Lantiq Deutschland
++ *   Copyright (C) 2012 John Crispin <blogic@openwrt.org>
++ */
++
++#include <linux/etherdevice.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/clk.h>
++#include <asm/delay.h>
++
++#include <linux/of_net.h>
++#include <linux/of_mdio.h>
++#include <linux/of_gpio.h>
++
++#include <xway_dma.h>
++#include <lantiq_soc.h>
++
++#include "lantiq_pce.h"
++
++#define SW_POLLING
++#define SW_ROUTING
++#define SW_PORTMAP
++
++#ifdef SW_ROUTING
++  #ifdef SW_PORTMAP
++#define XRX200_MAX_DEV                7
++  #else
++#define XRX200_MAX_DEV                2
++  #endif
++#else
++#define XRX200_MAX_DEV                1
++#endif
++
++#define XRX200_MAX_PORT               7
++#define XRX200_MAX_DMA                8
++
++#define XRX200_HEADROOM               4
++
++#define XRX200_TX_TIMEOUT     (10 * HZ)
++
++/* port type */
++#define XRX200_PORT_TYPE_PHY  1
++#define XRX200_PORT_TYPE_MAC  2
++
++/* DMA */
++#define XRX200_DMA_CRC_LEN    0x4
++#define XRX200_DMA_DATA_LEN   0x600
++#define XRX200_DMA_IRQ                INT_NUM_IM2_IRL0
++#define XRX200_DMA_RX         0
++#define XRX200_DMA_TX         1
++
++/* fetch / store dma */
++#define FDMA_PCTRL0           0x2A00
++#define FDMA_PCTRLx(x)                (FDMA_PCTRL0 + (x * 0x18))
++#define SDMA_PCTRL0           0x2F00
++#define SDMA_PCTRLx(x)                (SDMA_PCTRL0 + (x * 0x18))
++
++/* buffer management */
++#define BM_PCFG0              0x200
++#define BM_PCFGx(x)           (BM_PCFG0 + (x * 8))
++
++/* MDIO */
++#define MDIO_GLOB             0x0000
++#define MDIO_CTRL             0x0020
++#define MDIO_READ             0x0024
++#define MDIO_WRITE            0x0028
++#define MDIO_PHY0             0x0054
++#define MDIO_PHY(x)           (0x0054 - (x * sizeof(unsigned)))
++#define MDIO_CLK_CFG0         0x002C
++#define MDIO_CLK_CFG1         0x0030
++
++#define MDIO_GLOB_ENABLE      0x8000
++#define MDIO_BUSY             BIT(12)
++#define MDIO_RD                       BIT(11)
++#define MDIO_WR                       BIT(10)
++#define MDIO_MASK             0x1f
++#define MDIO_ADDRSHIFT                5
++#define MDIO1_25MHZ           9
++
++#define MDIO_PHY_LINK_DOWN    0x4000
++#define MDIO_PHY_LINK_UP      0x2000
++
++#define MDIO_PHY_SPEED_M10    0x0000
++#define MDIO_PHY_SPEED_M100   0x0800
++#define MDIO_PHY_SPEED_G1     0x1000
++
++#define MDIO_PHY_FDUP_EN      0x0600
++#define MDIO_PHY_FDUP_DIS     0x0200
++
++#define MDIO_PHY_LINK_MASK    0x6000
++#define MDIO_PHY_SPEED_MASK   0x1800
++#define MDIO_PHY_FDUP_MASK    0x0600
++#define MDIO_PHY_ADDR_MASK    0x001f
++#define MDIO_UPDATE_MASK      MDIO_PHY_ADDR_MASK | MDIO_PHY_LINK_MASK | \
++                                      MDIO_PHY_SPEED_MASK | MDIO_PHY_FDUP_MASK
++
++/* MII */
++#define MII_CFG(p)            (p * 8)
++
++#define MII_CFG_EN            BIT(14)
++
++#define MII_CFG_MODE_MIIP     0x0
++#define MII_CFG_MODE_MIIM     0x1
++#define MII_CFG_MODE_RMIIP    0x2
++#define MII_CFG_MODE_RMIIM    0x3
++#define MII_CFG_MODE_RGMII    0x4
++#define MII_CFG_MODE_MASK     0xf
++
++#define MII_CFG_RATE_M2P5     0x00
++#define MII_CFG_RATE_M25      0x10
++#define MII_CFG_RATE_M125     0x20
++#define MII_CFG_RATE_M50      0x30
++#define MII_CFG_RATE_AUTO     0x40
++#define MII_CFG_RATE_MASK     0x70
++
++/* cpu port mac */
++#define PMAC_HD_CTL           0x0000
++#define PMAC_RX_IPG           0x0024
++#define PMAC_EWAN             0x002c
++
++#define PMAC_IPG_MASK         0xf
++#define PMAC_HD_CTL_AS                0x0008
++#define PMAC_HD_CTL_AC                0x0004
++#define PMAC_HD_CTL_RXSH      0x0040
++#define PMAC_HD_CTL_AST               0x0080
++#define PMAC_HD_CTL_RST               0x0100
++
++/* PCE */
++#define PCE_TBL_KEY(x)                (0x1100 + ((7 - x) * 4))
++#define PCE_TBL_MASK          0x1120
++#define PCE_TBL_VAL(x)                (0x1124 + ((4 - x) * 4))
++#define PCE_TBL_ADDR          0x1138
++#define PCE_TBL_CTRL          0x113c
++#define PCE_PMAP1             0x114c
++#define PCE_PMAP2             0x1150
++#define PCE_PMAP3             0x1154
++#define PCE_GCTRL_REG(x)      (0x1158 + (x * 4))
++#define PCE_PCTRL_REG(p, x)   (0x1200 + (((p * 0xa) + x) * 4))
++
++#define PCE_TBL_BUSY          BIT(15)
++#define PCE_TBL_CFG_ADDR_MASK 0x1f
++#define PCE_TBL_CFG_ADWR      0x20
++#define PCE_TBL_CFG_ADWR_MASK 0x60
++#define PCE_INGRESS           BIT(11)
++
++/* MAC */
++#define MAC_FLEN_REG          (0x2314)
++#define MAC_CTRL_REG(p, x)    (0x240c + (((p * 0xc) + x) * 4))
++
++/* buffer management */
++#define BM_PCFG(p)            (0x200 + (p * 8))
++
++/* special tag in TX path header */
++#define SPID_SHIFT            24
++#define DPID_SHIFT            16
++#define DPID_ENABLE           1
++#define SPID_CPU_PORT         2
++#define PORT_MAP_SEL          BIT(15)
++#define PORT_MAP_EN           BIT(14)
++#define PORT_MAP_SHIFT                1
++#define PORT_MAP_MASK         0x3f
++
++#define SPPID_MASK            0x7
++#define SPPID_SHIFT           4
++
++/* MII regs not yet in linux */
++#define MDIO_DEVAD_NONE               (-1)
++#define ADVERTIZE_MPD         (1 << 10)
++
++struct xrx200_port {
++      u8 num;
++      u8 phy_addr;
++      u16 flags;
++      phy_interface_t phy_if;
++
++      int link;
++      int gpio;
++      enum of_gpio_flags gpio_flags;
++
++      struct phy_device *phydev;
++      struct device_node *phy_node;
++};
++
++struct xrx200_chan {
++      int idx;
++      int refcount;
++      int tx_free;
++
++      struct net_device dummy_dev;
++      struct net_device *devs[XRX200_MAX_DEV];
++
++      struct napi_struct napi;
++      struct ltq_dma_channel dma;
++      struct sk_buff *skb[LTQ_DESC_NUM];
++};
++
++struct xrx200_hw {
++      struct clk *clk;
++      struct mii_bus *mii_bus;
++
++      struct xrx200_chan chan[XRX200_MAX_DMA];
++
++      struct net_device *devs[XRX200_MAX_DEV];
++      int num_devs;
++
++      int port_map[XRX200_MAX_PORT];
++      unsigned short wan_map;
++
++      spinlock_t lock;
++};
++
++struct xrx200_priv {
++      struct net_device_stats stats;
++      int id;
++
++      struct xrx200_port port[XRX200_MAX_PORT];
++      int num_port;
++      int wan;
++      unsigned short port_map;
++      const void *mac;
++
++      struct xrx200_hw *hw;
++};
++
++static __iomem void *xrx200_switch_membase;
++static __iomem void *xrx200_mii_membase;
++static __iomem void *xrx200_mdio_membase;
++static __iomem void *xrx200_pmac_membase;
++
++#define ltq_switch_r32(x)     ltq_r32(xrx200_switch_membase + (x))
++#define ltq_switch_w32(x, y)  ltq_w32(x, xrx200_switch_membase + (y))
++#define ltq_switch_w32_mask(x, y, z) \
++                      ltq_w32_mask(x, y, xrx200_switch_membase + (z))
++
++#define ltq_mdio_r32(x)               ltq_r32(xrx200_mdio_membase + (x))
++#define ltq_mdio_w32(x, y)    ltq_w32(x, xrx200_mdio_membase + (y))
++#define ltq_mdio_w32_mask(x, y, z) \
++                      ltq_w32_mask(x, y, xrx200_mdio_membase + (z))
++
++#define ltq_mii_r32(x)                ltq_r32(xrx200_mii_membase + (x))
++#define ltq_mii_w32(x, y)     ltq_w32(x, xrx200_mii_membase + (y))
++#define ltq_mii_w32_mask(x, y, z) \
++                      ltq_w32_mask(x, y, xrx200_mii_membase + (z))
++
++#define ltq_pmac_r32(x)               ltq_r32(xrx200_pmac_membase + (x))
++#define ltq_pmac_w32(x, y)    ltq_w32(x, xrx200_pmac_membase + (y))
++#define ltq_pmac_w32_mask(x, y, z) \
++                      ltq_w32_mask(x, y, xrx200_pmac_membase + (z))
++
++static int xrx200_open(struct net_device *dev)
++{
++      struct xrx200_priv *priv = netdev_priv(dev);
++      unsigned long flags;
++      int i;
++
++      for (i = 0; i < XRX200_MAX_DMA; i++) {
++              if (!priv->hw->chan[i].dma.irq)
++                      continue;
++              spin_lock_irqsave(&priv->hw->lock, flags);
++              if (!priv->hw->chan[i].refcount) {
++                      napi_enable(&priv->hw->chan[i].napi);
++                      ltq_dma_open(&priv->hw->chan[i].dma);
++              }
++              priv->hw->chan[i].refcount++;
++              spin_unlock_irqrestore(&priv->hw->lock, flags);
++      }
++      for (i = 0; i < priv->num_port; i++)
++              if (priv->port[i].phydev)
++                      phy_start(priv->port[i].phydev);
++      netif_start_queue(dev);
++
++      return 0;
++}
++
++static int xrx200_close(struct net_device *dev)
++{
++      struct xrx200_priv *priv = netdev_priv(dev);
++      unsigned long flags;
++      int i;
++
++      netif_stop_queue(dev);
++
++      for (i = 0; i < priv->num_port; i++)
++              if (priv->port[i].phydev)
++                      phy_stop(priv->port[i].phydev);
++
++      for (i = 0; i < XRX200_MAX_DMA; i++) {
++              if (!priv->hw->chan[i].dma.irq)
++                      continue;
++              spin_lock_irqsave(&priv->hw->lock, flags);
++              priv->hw->chan[i].refcount--;
++              if (!priv->hw->chan[i].refcount) {
++                      napi_disable(&priv->hw->chan[i].napi);
++                      ltq_dma_close(&priv->hw->chan[XRX200_DMA_RX].dma);
++              }
++              spin_unlock_irqrestore(&priv->hw->lock, flags);
++      }
++
++      return 0;
++}
++
++static int xrx200_alloc_skb(struct xrx200_chan *ch)
++{
++#define DMA_PAD       (NET_IP_ALIGN + NET_SKB_PAD)
++      ch->skb[ch->dma.desc] = dev_alloc_skb(XRX200_DMA_DATA_LEN + DMA_PAD);
++      if (!ch->skb[ch->dma.desc])
++              return -ENOMEM;
++
++      skb_reserve(ch->skb[ch->dma.desc], NET_SKB_PAD);
++      ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(NULL,
++              ch->skb[ch->dma.desc]->data, XRX200_DMA_DATA_LEN,
++                      DMA_FROM_DEVICE);
++      ch->dma.desc_base[ch->dma.desc].addr =
++              CPHYSADDR(ch->skb[ch->dma.desc]->data);
++      ch->dma.desc_base[ch->dma.desc].ctl =
++              LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) |
++              XRX200_DMA_DATA_LEN;
++      skb_reserve(ch->skb[ch->dma.desc], NET_IP_ALIGN);
++
++      return 0;
++}
++
++static void xrx200_hw_receive(struct xrx200_chan *ch, int id)
++{
++      struct net_device *dev = ch->devs[id];
++      struct xrx200_priv *priv = netdev_priv(dev);
++      struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
++      struct sk_buff *skb = ch->skb[ch->dma.desc];
++      int len = (desc->ctl & LTQ_DMA_SIZE_MASK) - XRX200_DMA_CRC_LEN;
++      unsigned long flags;
++
++      spin_lock_irqsave(&priv->hw->lock, flags);
++      if (xrx200_alloc_skb(ch)) {
++              netdev_err(dev,
++                      "failed to allocate new rx buffer, stopping DMA\n");
++              ltq_dma_close(&ch->dma);
++      }
++
++      ch->dma.desc++;
++      ch->dma.desc %= LTQ_DESC_NUM;
++      spin_unlock_irqrestore(&priv->hw->lock, flags);
++
++      skb_put(skb, len);
++#ifdef SW_ROUTING
++      skb_pull(skb, 8);
++#endif
++      skb->dev = dev;
++      skb->protocol = eth_type_trans(skb, dev);
++      netif_receive_skb(skb);
++      priv->stats.rx_packets++;
++      priv->stats.rx_bytes+=len;
++}
++
++static int xrx200_poll_rx(struct napi_struct *napi, int budget)
++{
++      struct xrx200_chan *ch = container_of(napi,
++                              struct xrx200_chan, napi);
++      struct xrx200_priv *priv = netdev_priv(ch->devs[0]);
++      int rx = 0;
++      int complete = 0;
++      unsigned long flags;
++
++      while ((rx < budget) && !complete) {
++              struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
++              if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) {
++#ifdef SW_ROUTING
++                      struct sk_buff *skb = ch->skb[ch->dma.desc];
++                      u32 *special_tag = (u32*)skb->data;
++                      int port = (special_tag[1] >> SPPID_SHIFT) & SPPID_MASK;
++                      xrx200_hw_receive(ch, priv->hw->port_map[port]);
++#else
++                      xrx200_hw_receive(ch, 0);
++#endif
++                      rx++;
++              } else {
++                      complete = 1;
++              }
++      }
++      if (complete || !rx) {
++                      napi_complete(&ch->napi);
++              spin_lock_irqsave(&priv->hw->lock, flags);
++              ltq_dma_ack_irq(&ch->dma);
++              spin_unlock_irqrestore(&priv->hw->lock, flags);
++      }
++      return rx;
++}
++
++static int xrx200_poll_tx(struct napi_struct *napi, int budget)
++{
++      struct xrx200_chan *ch =
++              container_of(napi, struct xrx200_chan, napi);
++      struct xrx200_priv *priv = netdev_priv(ch->devs[0]);
++      unsigned long flags;
++      int i;
++
++      spin_lock_irqsave(&priv->hw->lock, flags);
++      while ((ch->dma.desc_base[ch->tx_free].ctl &
++                      (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) {
++              dev_kfree_skb_any(ch->skb[ch->tx_free]);
++              ch->skb[ch->tx_free] = NULL;
++              memset(&ch->dma.desc_base[ch->tx_free], 0,
++                      sizeof(struct ltq_dma_desc));
++              ch->tx_free++;
++              ch->tx_free %= LTQ_DESC_NUM;
++      }
++      spin_unlock_irqrestore(&priv->hw->lock, flags);
++
++      for (i = 0; i < XRX200_MAX_DEV && ch->devs[i]; i++) {
++              struct netdev_queue *txq =
++                      netdev_get_tx_queue(ch->devs[i], 0);
++              if (netif_tx_queue_stopped(txq))
++                      netif_tx_start_queue(txq);
++      }
++      napi_complete(&ch->napi);
++      spin_lock_irqsave(&priv->hw->lock, flags);
++      ltq_dma_ack_irq(&ch->dma);
++      spin_unlock_irqrestore(&priv->hw->lock, flags);
++
++      return 1;
++}
++
++static struct net_device_stats *xrx200_get_stats (struct net_device *dev)
++{
++      struct xrx200_priv *priv = netdev_priv(dev);
++
++      return &priv->stats;
++}
++
++static void xrx200_tx_timeout(struct net_device *dev)
++{
++      struct xrx200_priv *priv = netdev_priv(dev);
++
++      printk(KERN_ERR "%s: transmit timed out, disable the dma channel irq\n", dev->name);
++
++      priv->stats.tx_errors++;
++      netif_wake_queue(dev);
++}
++
++static int xrx200_start_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++      int queue = skb_get_queue_mapping(skb);
++      struct netdev_queue *txq = netdev_get_tx_queue(dev, queue);
++      struct xrx200_priv *priv = netdev_priv(dev);
++      struct xrx200_chan *ch = &priv->hw->chan[XRX200_DMA_TX];
++      struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
++      unsigned long flags;
++      u32 byte_offset;
++      int len;
++#ifdef SW_ROUTING
++  #ifdef SW_PORTMAP
++      u32 special_tag = (SPID_CPU_PORT << SPID_SHIFT) | PORT_MAP_SEL | PORT_MAP_EN | DPID_ENABLE;
++  #else
++      u32 special_tag = (SPID_CPU_PORT << SPID_SHIFT) | DPID_ENABLE;
++  #endif
++#endif
++
++      len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
++
++      if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) {
++              netdev_err(dev, "tx ring full\n");
++              netif_tx_stop_queue(txq);
++              return NETDEV_TX_BUSY;
++      }
++#ifdef SW_ROUTING
++  #ifdef SW_PORTMAP
++      special_tag |= priv->port_map << PORT_MAP_SHIFT;
++  #else
++      if(priv->id)
++              special_tag |= (1 << DPID_SHIFT);
++  #endif
++      if(skb_headroom(skb) < 4) {
++              struct sk_buff *tmp = skb_realloc_headroom(skb, 4);
++              dev_kfree_skb_any(skb);
++              skb = tmp;
++      }
++      skb_push(skb, 4);
++      memcpy(skb->data, &special_tag, sizeof(u32));
++      len += 4;
++#endif
++
++      /* dma needs to start on a 16 byte aligned address */
++      byte_offset = CPHYSADDR(skb->data) % 16;
++      ch->skb[ch->dma.desc] = skb;
++
++      dev->trans_start = jiffies;
++
++      spin_lock_irqsave(&priv->hw->lock, flags);
++      desc->addr = ((unsigned int) dma_map_single(NULL, skb->data, len,
++                                              DMA_TO_DEVICE)) - byte_offset;
++      wmb();
++      desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP |
++              LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK);
++      ch->dma.desc++;
++      ch->dma.desc %= LTQ_DESC_NUM;
++      spin_unlock_irqrestore(&priv->hw->lock, flags);
++
++      if (ch->dma.desc_base[ch->dma.desc].ctl & LTQ_DMA_OWN)
++              netif_tx_stop_queue(txq);
++
++      priv->stats.tx_packets++;
++      priv->stats.tx_bytes+=len;
++
++      return NETDEV_TX_OK;
++}
++
++static irqreturn_t xrx200_dma_irq(int irq, void *priv)
++{
++      struct xrx200_hw *hw = priv;
++      int ch = irq - XRX200_DMA_IRQ;
++
++      napi_schedule(&hw->chan[ch].napi);
++
++      return IRQ_HANDLED;
++}
++
++static int xrx200_dma_init(struct xrx200_hw *hw)
++{
++      int i, err = 0;
++
++      ltq_dma_init_port(DMA_PORT_ETOP);
++
++      for (i = 0; i < 8 && !err; i++) {
++              int irq = XRX200_DMA_IRQ + i;
++              struct xrx200_chan *ch = &hw->chan[i];
++
++              ch->idx = ch->dma.nr = i;
++
++              if (i == XRX200_DMA_TX) {
++                      ltq_dma_alloc_tx(&ch->dma);
++                      err = request_irq(irq, xrx200_dma_irq, 0, "vrx200_tx", hw);
++              } else if (i == XRX200_DMA_RX) {
++                      ltq_dma_alloc_rx(&ch->dma);
++                      for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM;
++                                      ch->dma.desc++)
++                              if (xrx200_alloc_skb(ch))
++                                      err = -ENOMEM;
++                      ch->dma.desc = 0;
++                      err = request_irq(irq, xrx200_dma_irq, 0, "vrx200_rx", hw);
++              } else
++                      continue;
++
++              if (!err)
++                      ch->dma.irq = irq;
++      }
++
++      return err;
++}
++
++#ifdef SW_POLLING
++static void xrx200_gmac_update(struct xrx200_port *port)
++{
++      u16 phyaddr = port->phydev->addr & MDIO_PHY_ADDR_MASK;
++      u16 miimode = ltq_mii_r32(MII_CFG(port->num)) & MII_CFG_MODE_MASK;
++      u16 miirate = 0;
++
++      switch (port->phydev->speed) {
++      case SPEED_1000:
++              phyaddr |= MDIO_PHY_SPEED_G1;
++              miirate = MII_CFG_RATE_M125;
++              break;
++
++      case SPEED_100:
++              phyaddr |= MDIO_PHY_SPEED_M100;
++              switch (miimode) {
++              case MII_CFG_MODE_RMIIM:
++              case MII_CFG_MODE_RMIIP:
++                      miirate = MII_CFG_RATE_M50;
++                      break;
++              default:
++                      miirate = MII_CFG_RATE_M25;
++                      break;
++              }
++              break;
++
++      default:
++              phyaddr |= MDIO_PHY_SPEED_M10;
++              miirate = MII_CFG_RATE_M2P5;
++              break;
++      }
++
++      if (port->phydev->link)
++              phyaddr |= MDIO_PHY_LINK_UP;
++      else
++              phyaddr |= MDIO_PHY_LINK_DOWN;
++
++      if (port->phydev->duplex == DUPLEX_FULL)
++              phyaddr |= MDIO_PHY_FDUP_EN;
++      else
++              phyaddr |= MDIO_PHY_FDUP_DIS;
++
++      ltq_mdio_w32_mask(MDIO_UPDATE_MASK, phyaddr, MDIO_PHY(port->num));
++      ltq_mii_w32_mask(MII_CFG_RATE_MASK, miirate, MII_CFG(port->num));
++      udelay(1);
++}
++#else
++static void xrx200_gmac_update(struct xrx200_port *port)
++{
++
++}
++#endif
++
++static void xrx200_mdio_link(struct net_device *dev)
++{
++      struct xrx200_priv *priv = netdev_priv(dev);
++      int i;
++
++      for (i = 0; i < priv->num_port; i++) {
++              if (!priv->port[i].phydev)
++                      continue;
++
++              if (priv->port[i].link != priv->port[i].phydev->link) {
++                      xrx200_gmac_update(&priv->port[i]);
++                      priv->port[i].link = priv->port[i].phydev->link;
++                      netdev_info(dev, "port %d %s link\n",
++                              priv->port[i].num,
++                              (priv->port[i].link)?("got"):("lost"));
++              }
++      }
++}
++
++static inline int xrx200_mdio_poll(struct mii_bus *bus)
++{
++      unsigned cnt = 10000;
++
++      while (likely(cnt--)) {
++              unsigned ctrl = ltq_mdio_r32(MDIO_CTRL);
++              if ((ctrl & MDIO_BUSY) == 0)
++                      return 0;
++      }
++
++      return 1;
++}
++
++static int xrx200_mdio_wr(struct mii_bus *bus, int addr, int reg, u16 val)
++{
++      if (xrx200_mdio_poll(bus))
++              return 1;
++
++      ltq_mdio_w32(val, MDIO_WRITE);
++      ltq_mdio_w32(MDIO_BUSY | MDIO_WR |
++              ((addr & MDIO_MASK) << MDIO_ADDRSHIFT) |
++              (reg & MDIO_MASK),
++              MDIO_CTRL);
++
++      return 0;
++}
++
++static int xrx200_mdio_rd(struct mii_bus *bus, int addr, int reg)
++{
++      if (xrx200_mdio_poll(bus))
++              return -1;
++
++      ltq_mdio_w32(MDIO_BUSY | MDIO_RD |
++              ((addr & MDIO_MASK) << MDIO_ADDRSHIFT) |
++              (reg & MDIO_MASK),
++              MDIO_CTRL);
++
++      if (xrx200_mdio_poll(bus))
++              return -1;
++
++      return ltq_mdio_r32(MDIO_READ);
++}
++
++static int xrx200_mdio_probe(struct net_device *dev, struct xrx200_port *port)
++{
++      struct xrx200_priv *priv = netdev_priv(dev);
++      struct phy_device *phydev = NULL;
++      unsigned val;
++
++      phydev = priv->hw->mii_bus->phy_map[port->phy_addr];
++
++      if (!phydev) {
++              netdev_err(dev, "no PHY found\n");
++              return -ENODEV;
++      }
++
++      phydev = phy_connect(dev, dev_name(&phydev->dev), &xrx200_mdio_link,
++                              0, port->phy_if);
++
++      if (IS_ERR(phydev)) {
++              netdev_err(dev, "Could not attach to PHY\n");
++              return PTR_ERR(phydev);
++      }
++
++      phydev->supported &= (SUPPORTED_10baseT_Half
++                      | SUPPORTED_10baseT_Full
++                      | SUPPORTED_100baseT_Half
++                      | SUPPORTED_100baseT_Full
++                      | SUPPORTED_1000baseT_Half
++                      | SUPPORTED_1000baseT_Full
++                      | SUPPORTED_Autoneg
++                      | SUPPORTED_MII
++                      | SUPPORTED_TP);
++      phydev->advertising = phydev->supported;
++      port->phydev = phydev;
++
++      pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n",
++              dev->name, phydev->drv->name,
++              dev_name(&phydev->dev), phydev->irq);
++
++#ifdef SW_POLLING
++      phy_read_status(phydev);
++
++      val = xrx200_mdio_rd(priv->hw->mii_bus, MDIO_DEVAD_NONE, MII_CTRL1000);
++      val |= ADVERTIZE_MPD;
++      xrx200_mdio_wr(priv->hw->mii_bus, MDIO_DEVAD_NONE, MII_CTRL1000, val);
++      xrx200_mdio_wr(priv->hw->mii_bus, 0, 0, 0x1040);
++
++      phy_start_aneg(phydev);
++#endif
++      return 0;
++}
++
++static void xrx200_port_config(struct xrx200_priv *priv,
++              const struct xrx200_port *port)
++{
++      u16 miimode = 0;
++
++      switch (port->num) {
++      case 0: /* xMII0 */
++      case 1: /* xMII1 */
++              switch (port->phy_if) {
++              case PHY_INTERFACE_MODE_MII:
++                      if (port->flags & XRX200_PORT_TYPE_PHY)
++                              /* MII MAC mode, connected to external PHY */
++                              miimode = MII_CFG_MODE_MIIM;
++                      else
++                              /* MII PHY mode, connected to external MAC */
++                              miimode = MII_CFG_MODE_MIIP;
++                      break;
++              case PHY_INTERFACE_MODE_RMII:
++                      if (port->flags & XRX200_PORT_TYPE_PHY)
++                              /* RMII MAC mode, connected to external PHY */
++                              miimode = MII_CFG_MODE_RMIIM;
++                      else
++                              /* RMII PHY mode, connected to external MAC */
++                              miimode = MII_CFG_MODE_RMIIP;
++                      break;
++              case PHY_INTERFACE_MODE_RGMII:
++                      /* RGMII MAC mode, connected to external PHY */
++                      miimode = MII_CFG_MODE_RGMII;
++                      break;
++              default:
++                      break;
++              }
++              break;
++      case 2: /* internal GPHY0 */
++      case 3: /* internal GPHY0 */
++      case 4: /* internal GPHY1 */
++              switch (port->phy_if) {
++                      case PHY_INTERFACE_MODE_MII:
++                      case PHY_INTERFACE_MODE_GMII:
++                              /* MII MAC mode, connected to internal GPHY */
++                              miimode = MII_CFG_MODE_MIIM;
++                              break;
++                      default:
++                              break;
++              }
++              break;
++      case 5: /* internal GPHY1 or xMII2 */
++              switch (port->phy_if) {
++              case PHY_INTERFACE_MODE_MII:
++                      /* MII MAC mode, connected to internal GPHY */
++                      miimode = MII_CFG_MODE_MIIM;
++                      break;
++              case PHY_INTERFACE_MODE_RGMII:
++                      /* RGMII MAC mode, connected to external PHY */
++                      miimode = MII_CFG_MODE_RGMII;
++                      break;
++              default:
++                      break;
++              }
++              break;
++      default:
++              break;
++      }
++
++      ltq_mii_w32_mask(MII_CFG_MODE_MASK, miimode | MII_CFG_EN,
++              MII_CFG(port->num));
++}
++
++static int xrx200_init(struct net_device *dev)
++{
++      struct xrx200_priv *priv = netdev_priv(dev);
++      struct sockaddr mac;
++      int err, i;
++
++#ifndef SW_POLLING
++      unsigned int reg = 0;
++
++      /* enable auto polling */
++      for (i = 0; i < priv->num_port; i++)
++              reg |= BIT(priv->port[i].num);
++      ltq_mdio_w32(reg, MDIO_CLK_CFG0);
++      ltq_mdio_w32(MDIO1_25MHZ, MDIO_CLK_CFG1);
++#endif
++
++      /* setup each port */
++      for (i = 0; i < priv->num_port; i++)
++              xrx200_port_config(priv, &priv->port[i]);
++
++      memcpy(&mac.sa_data, priv->mac, ETH_ALEN);
++      if (!is_valid_ether_addr(mac.sa_data)) {
++              pr_warn("net-xrx200: invalid MAC, using random\n");
++              eth_random_addr(mac.sa_data);
++              dev->addr_assign_type |= NET_ADDR_RANDOM;
++      }
++
++      err = eth_mac_addr(dev, &mac);
++      if (err)
++              goto err_netdev;
++
++      for (i = 0; i < priv->num_port; i++)
++              if (xrx200_mdio_probe(dev, &priv->port[i]))
++                      pr_warn("xrx200-mdio: probing phy of port %d failed\n",
++                                       priv->port[i].num);
++
++      return 0;
++
++err_netdev:
++      unregister_netdev(dev);
++      free_netdev(dev);
++      return err;
++}
++
++static void xrx200_pci_microcode(void)
++{
++      int i;
++
++      ltq_switch_w32_mask(PCE_TBL_CFG_ADDR_MASK | PCE_TBL_CFG_ADWR_MASK,
++              PCE_TBL_CFG_ADWR, PCE_TBL_CTRL);
++      ltq_switch_w32(0, PCE_TBL_MASK);
++
++      for (i = 0; i < ARRAY_SIZE(pce_microcode); i++) {
++              ltq_switch_w32(i, PCE_TBL_ADDR);
++              ltq_switch_w32(pce_microcode[i].val[3], PCE_TBL_VAL(0));
++              ltq_switch_w32(pce_microcode[i].val[2], PCE_TBL_VAL(1));
++              ltq_switch_w32(pce_microcode[i].val[1], PCE_TBL_VAL(2));
++              ltq_switch_w32(pce_microcode[i].val[0], PCE_TBL_VAL(3));
++
++              // start the table access:
++              ltq_switch_w32_mask(0, PCE_TBL_BUSY, PCE_TBL_CTRL);
++              while (ltq_switch_r32(PCE_TBL_CTRL) & PCE_TBL_BUSY);
++      }
++
++      /* tell the switch that the microcode is loaded */
++      ltq_switch_w32_mask(0, BIT(3), PCE_GCTRL_REG(0));
++}
++
++static void xrx200_hw_init(struct xrx200_hw *hw)
++{
++      int i;
++
++      /* enable clock gate */
++      clk_enable(hw->clk);
++
++      ltq_switch_w32(1, 0);
++      mdelay(100);
++      ltq_switch_w32(0, 0);
++      /*
++       * TODO: we should really disbale all phys/miis here and explicitly
++       * enable them in the device secific init function
++       */
++
++      /* disable port fetch/store dma */
++      for (i = 0; i < 7; i++ ) {
++              ltq_switch_w32(0, FDMA_PCTRLx(i));
++              ltq_switch_w32(0, SDMA_PCTRLx(i));
++      }
++
++      /* enable Switch */
++      ltq_mdio_w32_mask(0, MDIO_GLOB_ENABLE, MDIO_GLOB);
++
++      /* load the pce microcode */
++      xrx200_pci_microcode();
++
++      /* Default unknown Broadcat/Multicast/Unicast port maps */
++      ltq_switch_w32(0x7f, PCE_PMAP1);
++      ltq_switch_w32(0x7f, PCE_PMAP2);
++      ltq_switch_w32(0x7f, PCE_PMAP3);
++
++      /* RMON Counter Enable for all physical ports */
++      for (i = 0; i < 7; i++)
++              ltq_switch_w32(0x1, BM_PCFG(i));
++
++      /* disable auto polling */
++      ltq_mdio_w32(0x0, MDIO_CLK_CFG0);
++
++      /* enable port statistic counters */
++      for (i = 0; i < 7; i++)
++              ltq_switch_w32(0x1, BM_PCFGx(i));
++
++      /* set IPG to 12 */
++      ltq_pmac_w32_mask(PMAC_IPG_MASK, 0xb, PMAC_RX_IPG);
++
++#ifdef SW_ROUTING
++      /* enable status header, enable CRC */
++      ltq_pmac_w32_mask(0,
++              PMAC_HD_CTL_RST | PMAC_HD_CTL_AST | PMAC_HD_CTL_RXSH | PMAC_HD_CTL_AS | PMAC_HD_CTL_AC,
++              PMAC_HD_CTL);
++#else
++      /* disable status header, enable CRC */
++      ltq_pmac_w32_mask(PMAC_HD_CTL_AST | PMAC_HD_CTL_RXSH | PMAC_HD_CTL_AS,
++              PMAC_HD_CTL_AC,
++              PMAC_HD_CTL);
++#endif
++
++      /* enable port fetch/store dma */
++      for (i = 0; i < 7; i++ ) {
++              ltq_switch_w32_mask(0, 0x01, FDMA_PCTRLx(i));
++              ltq_switch_w32_mask(0, 0x01, SDMA_PCTRLx(i));
++              ltq_switch_w32_mask(0, PCE_INGRESS, PCE_PCTRL_REG(i, 0));
++      }
++
++      /* enable special tag insertion on cpu port */
++      ltq_switch_w32_mask(0, 0x02, FDMA_PCTRLx(6));
++      ltq_switch_w32_mask(0, PCE_INGRESS, PCE_PCTRL_REG(6, 0));
++      ltq_switch_w32_mask(0, BIT(3), MAC_CTRL_REG(6, 2));
++      ltq_switch_w32(1518 + 8 + 4 * 2, MAC_FLEN_REG);
++}
++
++static void xrx200_hw_cleanup(struct xrx200_hw *hw)
++{
++      int i;
++
++      /* disable the switch */
++      ltq_mdio_w32_mask(MDIO_GLOB_ENABLE, 0, MDIO_GLOB);
++
++      /* free the channels and IRQs */
++      for (i = 0; i < 2; i++) {
++              ltq_dma_free(&hw->chan[i].dma);
++              if (hw->chan[i].dma.irq)
++                      free_irq(hw->chan[i].dma.irq, hw);
++      }
++
++      /* free the allocated RX ring */
++      for (i = 0; i < LTQ_DESC_NUM; i++)
++              dev_kfree_skb_any(hw->chan[XRX200_DMA_RX].skb[i]);
++
++      /* clear the mdio bus */
++      mdiobus_unregister(hw->mii_bus);
++      mdiobus_free(hw->mii_bus);
++
++      /* release the clock */
++      clk_disable(hw->clk);
++      clk_put(hw->clk);
++}
++
++static int xrx200_of_mdio(struct xrx200_hw *hw, struct device_node *np)
++{
++      int i;
++      hw->mii_bus = mdiobus_alloc();
++      if (!hw->mii_bus)
++              return -ENOMEM;
++
++      hw->mii_bus->read = xrx200_mdio_rd;
++      hw->mii_bus->write = xrx200_mdio_wr;
++      hw->mii_bus->name = "lantiq,xrx200-mdio";
++      snprintf(hw->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0);
++
++      if (of_mdiobus_register(hw->mii_bus, np)) {
++              mdiobus_free(hw->mii_bus);
++              return -ENXIO;
++      }
++
++      return 0;
++}
++
++static void xrx200_of_port(struct xrx200_priv *priv, struct device_node *port)
++{
++      const __be32 *addr, *id = of_get_property(port, "reg", NULL);
++      struct xrx200_port *p = &priv->port[priv->num_port];
++
++      if (!id)
++              return;
++
++      memset(p, 0, sizeof(struct xrx200_port));
++      p->phy_node = of_parse_phandle(port, "phy-handle", 0);
++      addr = of_get_property(p->phy_node, "reg", NULL);
++      if (!addr)
++              return;
++
++      p->num = *id;
++      p->phy_addr = *addr;
++      p->phy_if = of_get_phy_mode(port);
++      if (p->phy_addr > 0x10)
++              p->flags = XRX200_PORT_TYPE_MAC;
++      else
++              p->flags = XRX200_PORT_TYPE_PHY;
++      priv->num_port++;
++
++      p->gpio = of_get_gpio_flags(port, 0, &p->gpio_flags);
++      if (gpio_is_valid(p->gpio))
++              if (!gpio_request(p->gpio, "phy-reset")) {
++                      gpio_direction_output(p->gpio,
++                              (p->gpio_flags & OF_GPIO_ACTIVE_LOW) ? (1) : (0));
++                      udelay(100);
++                      gpio_set_value(p->gpio, (p->gpio_flags & OF_GPIO_ACTIVE_LOW) ? (0) : (1));
++              }
++      /* is this port a wan port ? */
++      if (priv->wan)
++              priv->hw->wan_map |= BIT(p->num);
++
++      priv->port_map |= BIT(p->num);
++
++      /* store the port id in the hw struct so we can map ports -> devices */
++      priv->hw->port_map[p->num] = priv->hw->num_devs;
++}
++
++static const struct net_device_ops xrx200_netdev_ops = {
++      .ndo_init               = xrx200_init,
++      .ndo_open               = xrx200_open,
++      .ndo_stop               = xrx200_close,
++      .ndo_start_xmit         = xrx200_start_xmit,
++      .ndo_set_mac_address    = eth_mac_addr,
++      .ndo_validate_addr      = eth_validate_addr,
++      .ndo_change_mtu         = eth_change_mtu,
++      .ndo_get_stats          = xrx200_get_stats,
++      .ndo_tx_timeout         = xrx200_tx_timeout,
++};
++
++static void xrx200_of_iface(struct xrx200_hw *hw, struct device_node *iface)
++{
++      struct xrx200_priv *priv;
++      struct device_node *port;
++      const __be32 *wan;
++
++      /* alloc the network device */
++      hw->devs[hw->num_devs] = alloc_etherdev(sizeof(struct xrx200_priv));
++      if (!hw->devs[hw->num_devs])
++              return;
++
++      /* setup the network device */
++      strcpy(hw->devs[hw->num_devs]->name, "eth%d");
++      hw->devs[hw->num_devs]->netdev_ops = &xrx200_netdev_ops;
++      hw->devs[hw->num_devs]->watchdog_timeo = XRX200_TX_TIMEOUT;
++      hw->devs[hw->num_devs]->needed_headroom = XRX200_HEADROOM;
++
++      /* setup our private data */
++      priv = netdev_priv(hw->devs[hw->num_devs]);
++      priv->hw = hw;
++      priv->mac = of_get_mac_address(iface);
++      priv->id = hw->num_devs;
++
++      /* is this the wan interface ? */
++      wan = of_get_property(iface, "lantiq,wan", NULL);
++      if (wan && (*wan == 1))
++              priv->wan = 1;
++
++      /* load the ports that are part of the interface */
++      for_each_child_of_node(iface, port)
++              if (of_device_is_compatible(port, "lantiq,xrx200-pdi-port"))
++                      xrx200_of_port(priv, port);
++
++      /* register the actual device */
++      if (!register_netdev(hw->devs[hw->num_devs]))
++              hw->num_devs++;
++}
++
++static struct xrx200_hw xrx200_hw;
++
++static int xrx200_probe(struct platform_device *pdev)
++{
++      struct resource *res[4];
++      struct device_node *mdio_np, *iface_np;
++      int i;
++
++      /* load the memory ranges */
++      for (i = 0; i < 4; i++) {
++              res[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
++              if (!res[i]) {
++                      dev_err(&pdev->dev, "failed to get resources\n");
++                      return -ENOENT;
++              }
++      }
++      xrx200_switch_membase = devm_request_and_ioremap(&pdev->dev, res[0]);
++      xrx200_mdio_membase = devm_request_and_ioremap(&pdev->dev, res[1]);
++      xrx200_mii_membase = devm_request_and_ioremap(&pdev->dev, res[2]);
++      xrx200_pmac_membase = devm_request_and_ioremap(&pdev->dev, res[3]);
++      if (!xrx200_switch_membase || !xrx200_mdio_membase ||
++                      !xrx200_mii_membase || !xrx200_pmac_membase) {
++              dev_err(&pdev->dev, "failed to request and remap io ranges \n");
++              return -ENOMEM;
++      }
++
++      /* get the clock */
++      xrx200_hw.clk = clk_get(&pdev->dev, NULL);
++      if (IS_ERR(xrx200_hw.clk)) {
++              dev_err(&pdev->dev, "failed to get clock\n");
++              return PTR_ERR(xrx200_hw.clk);
++      }
++
++      /* bring up the dma engine and IP core */
++      spin_lock_init(&xrx200_hw.lock);
++      xrx200_dma_init(&xrx200_hw);
++      xrx200_hw_init(&xrx200_hw);
++
++      /* bring up the mdio bus */
++      mdio_np = of_find_compatible_node(pdev->dev.of_node, NULL,
++                              "lantiq,xrx200-mdio");
++      if (mdio_np)
++              if (xrx200_of_mdio(&xrx200_hw, mdio_np))
++                      dev_err(&pdev->dev, "mdio probe failed\n");
++
++      /* load the interfaces */
++      for_each_child_of_node(pdev->dev.of_node, iface_np)
++              if (of_device_is_compatible(iface_np, "lantiq,xrx200-pdi")) {
++                      if (xrx200_hw.num_devs < XRX200_MAX_DEV)
++                              xrx200_of_iface(&xrx200_hw, iface_np);
++                      else
++                              dev_err(&pdev->dev,
++                                      "only %d interfaces allowed\n",
++                                      XRX200_MAX_DEV);
++              }
++
++      if (!xrx200_hw.num_devs) {
++              xrx200_hw_cleanup(&xrx200_hw);
++              dev_err(&pdev->dev, "failed to load interfaces\n");
++              return -ENOENT;
++      }
++
++      /* set wan port mask */
++      ltq_pmac_w32(xrx200_hw.wan_map, PMAC_EWAN);
++
++      for (i = 0; i < xrx200_hw.num_devs; i++) {
++              xrx200_hw.chan[XRX200_DMA_RX].devs[i] = xrx200_hw.devs[i];
++              xrx200_hw.chan[XRX200_DMA_TX].devs[i] = xrx200_hw.devs[i];
++      }
++
++      /* setup NAPI */
++      init_dummy_netdev(&xrx200_hw.chan[XRX200_DMA_RX].dummy_dev);
++      init_dummy_netdev(&xrx200_hw.chan[XRX200_DMA_TX].dummy_dev);
++      netif_napi_add(&xrx200_hw.chan[XRX200_DMA_RX].dummy_dev,
++                      &xrx200_hw.chan[XRX200_DMA_RX].napi, xrx200_poll_rx, 32);
++      netif_napi_add(&xrx200_hw.chan[XRX200_DMA_TX].dummy_dev,
++                      &xrx200_hw.chan[XRX200_DMA_TX].napi, xrx200_poll_tx, 8);
++
++      platform_set_drvdata(pdev, &xrx200_hw);
++
++      return 0;
++}
++
++static int xrx200_remove(struct platform_device *pdev)
++{
++      struct net_device *dev = platform_get_drvdata(pdev);
++      struct xrx200_priv *priv;
++
++      if (!dev)
++              return 0;
++
++      priv = netdev_priv(dev);
++
++      /* free stack related instances */
++      netif_stop_queue(dev);
++      netif_napi_del(&xrx200_hw.chan[XRX200_DMA_RX].napi);
++      netif_napi_del(&xrx200_hw.chan[XRX200_DMA_TX].napi);
++
++      /* shut down hardware */
++      xrx200_hw_cleanup(&xrx200_hw);
++
++      /* remove the actual device */
++      unregister_netdev(dev);
++      free_netdev(dev);
++
++      return 0;
++}
++
++static const struct of_device_id xrx200_match[] = {
++      { .compatible = "lantiq,xrx200-net" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, xrx200_match);
++
++static struct platform_driver xrx200_driver = {
++      .probe = xrx200_probe,
++      .remove = xrx200_remove,
++      .driver = {
++              .name = "lantiq,xrx200-net",
++              .of_match_table = xrx200_match,
++              .owner = THIS_MODULE,
++      },
++};
++
++module_platform_driver(xrx200_driver);
++
++MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
++MODULE_DESCRIPTION("Lantiq SoC XRX200 ethernet");
++MODULE_LICENSE("GPL");
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0026-NET-MIPS-lantiq-update-etop-driver-for-devicetree.patch b/target/linux/lantiq/patches-3.8/0026-NET-MIPS-lantiq-update-etop-driver-for-devicetree.patch
new file mode 100644 (file)
index 0000000..ee5420e
--- /dev/null
@@ -0,0 +1,832 @@
+From 32010516999c75d8e8ea95779137438f4f6d06ae Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 13 Mar 2013 09:32:16 +0100
+Subject: [PATCH 26/40] NET: MIPS: lantiq: update etop driver for devicetree
+
+---
+ drivers/net/ethernet/lantiq_etop.c |  496 +++++++++++++++++++++++++-----------
+ 1 file changed, 351 insertions(+), 145 deletions(-)
+
+diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c
+index c124e67..91a37f1 100644
+--- a/drivers/net/ethernet/lantiq_etop.c
++++ b/drivers/net/ethernet/lantiq_etop.c
+@@ -12,7 +12,7 @@
+  *   along with this program; if not, write to the Free Software
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+  *
+- *   Copyright (C) 2011 John Crispin <blogic@openwrt.org>
++ *   Copyright (C) 2011-12 John Crispin <blogic@openwrt.org>
+  */
+ #include <linux/kernel.h>
+@@ -36,6 +36,10 @@
+ #include <linux/io.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/module.h>
++#include <linux/clk.h>
++#include <linux/of_net.h>
++#include <linux/of_irq.h>
++#include <linux/of_platform.h>
+ #include <asm/checksum.h>
+@@ -71,25 +75,61 @@
+ #define ETOP_MII_REVERSE      0xe
+ #define ETOP_PLEN_UNDER               0x40
+ #define ETOP_CGEN             0x800
+-
+-/* use 2 static channels for TX/RX */
+-#define LTQ_ETOP_TX_CHANNEL   1
+-#define LTQ_ETOP_RX_CHANNEL   6
+-#define IS_TX(x)              (x == LTQ_ETOP_TX_CHANNEL)
+-#define IS_RX(x)              (x == LTQ_ETOP_RX_CHANNEL)
+-
++#define ETOP_CFG_MII0         0x01
++
++#define LTQ_GBIT_MDIO_CTL     0xCC
++#define LTQ_GBIT_MDIO_DATA    0xd0
++#define LTQ_GBIT_GCTL0                0x68
++#define LTQ_GBIT_PMAC_HD_CTL  0x8c
++#define LTQ_GBIT_P0_CTL               0x4
++#define LTQ_GBIT_PMAC_RX_IPG  0xa8
++#define LTQ_GBIT_RGMII_CTL    0x78
++
++#define PMAC_HD_CTL_AS                (1 << 19)
++#define PMAC_HD_CTL_RXSH      (1 << 22)
++
++/* Switch Enable (0=disable, 1=enable) */
++#define GCTL0_SE              0x80000000
++/* Disable MDIO auto polling (0=disable, 1=enable) */
++#define PX_CTL_DMDIO          0x00400000
++
++/* MDC clock divider, clock = 25MHz/((MDC_CLOCK + 1) * 2) */
++#define MDC_CLOCK_MASK                0xff000000
++#define MDC_CLOCK_OFFSET      24
++
++/* register information for the gbit's MDIO bus */
++#define MDIO_XR9_REQUEST      0x00008000
++#define MDIO_XR9_READ         0x00000800
++#define MDIO_XR9_WRITE                0x00000400
++#define MDIO_XR9_REG_MASK     0x1f
++#define MDIO_XR9_ADDR_MASK    0x1f
++#define MDIO_XR9_RD_MASK      0xffff
++#define MDIO_XR9_REG_OFFSET   0
++#define MDIO_XR9_ADDR_OFFSET  5
++#define MDIO_XR9_WR_OFFSET    16
++
++#define LTQ_DMA_ETOP  ((of_machine_is_compatible("lantiq,ase")) ? \
++                      (INT_NUM_IM3_IRL0) : (INT_NUM_IM2_IRL0))
++
++/* the newer xway socks have a embedded 3/7 port gbit multiplexer */
+ #define ltq_etop_r32(x)               ltq_r32(ltq_etop_membase + (x))
+ #define ltq_etop_w32(x, y)    ltq_w32(x, ltq_etop_membase + (y))
+ #define ltq_etop_w32_mask(x, y, z)    \
+               ltq_w32_mask(x, y, ltq_etop_membase + (z))
+-#define DRV_VERSION   "1.0"
++#define ltq_gbit_r32(x)               ltq_r32(ltq_gbit_membase + (x))
++#define ltq_gbit_w32(x, y)    ltq_w32(x, ltq_gbit_membase + (y))
++#define ltq_gbit_w32_mask(x, y, z)    \
++              ltq_w32_mask(x, y, ltq_gbit_membase + (z))
++
++#define DRV_VERSION   "1.2"
+ static void __iomem *ltq_etop_membase;
++static void __iomem *ltq_gbit_membase;
+ struct ltq_etop_chan {
+-      int idx;
+       int tx_free;
++      int irq;
+       struct net_device *netdev;
+       struct napi_struct napi;
+       struct ltq_dma_channel dma;
+@@ -99,22 +139,35 @@ struct ltq_etop_chan {
+ struct ltq_etop_priv {
+       struct net_device *netdev;
+       struct platform_device *pdev;
+-      struct ltq_eth_data *pldata;
+       struct resource *res;
+       struct mii_bus *mii_bus;
+       struct phy_device *phydev;
+-      struct ltq_etop_chan ch[MAX_DMA_CHAN];
+-      int tx_free[MAX_DMA_CHAN >> 1];
++      struct ltq_etop_chan txch;
++      struct ltq_etop_chan rxch;
++
++      int tx_irq;
++      int rx_irq;
++
++      const void *mac;
++      int mii_mode;
+       spinlock_t lock;
++
++      struct clk *clk_ppe;
++      struct clk *clk_switch;
++      struct clk *clk_ephy;
++      struct clk *clk_ephycgu;
+ };
++static int ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr,
++                              int phy_reg, u16 phy_data);
++
+ static int
+ ltq_etop_alloc_skb(struct ltq_etop_chan *ch)
+ {
+-      ch->skb[ch->dma.desc] = netdev_alloc_skb(ch->netdev, MAX_DMA_DATA_LEN);
++      ch->skb[ch->dma.desc] = dev_alloc_skb(MAX_DMA_DATA_LEN);
+       if (!ch->skb[ch->dma.desc])
+               return -ENOMEM;
+       ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(NULL,
+@@ -149,8 +202,11 @@ ltq_etop_hw_receive(struct ltq_etop_chan *ch)
+       spin_unlock_irqrestore(&priv->lock, flags);
+       skb_put(skb, len);
++      skb->dev = ch->netdev;
+       skb->protocol = eth_type_trans(skb, ch->netdev);
+       netif_receive_skb(skb);
++      ch->netdev->stats.rx_packets++;
++      ch->netdev->stats.rx_bytes += len;
+ }
+ static int
+@@ -158,8 +214,10 @@ ltq_etop_poll_rx(struct napi_struct *napi, int budget)
+ {
+       struct ltq_etop_chan *ch = container_of(napi,
+                               struct ltq_etop_chan, napi);
++      struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
+       int rx = 0;
+       int complete = 0;
++      unsigned long flags;
+       while ((rx < budget) && !complete) {
+               struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
+@@ -173,7 +231,9 @@ ltq_etop_poll_rx(struct napi_struct *napi, int budget)
+       }
+       if (complete || !rx) {
+               napi_complete(&ch->napi);
++              spin_lock_irqsave(&priv->lock, flags);
+               ltq_dma_ack_irq(&ch->dma);
++              spin_unlock_irqrestore(&priv->lock, flags);
+       }
+       return rx;
+ }
+@@ -185,12 +245,14 @@ ltq_etop_poll_tx(struct napi_struct *napi, int budget)
+               container_of(napi, struct ltq_etop_chan, napi);
+       struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
+       struct netdev_queue *txq =
+-              netdev_get_tx_queue(ch->netdev, ch->idx >> 1);
++              netdev_get_tx_queue(ch->netdev, ch->dma.nr >> 1);
+       unsigned long flags;
+       spin_lock_irqsave(&priv->lock, flags);
+       while ((ch->dma.desc_base[ch->tx_free].ctl &
+                       (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) {
++              ch->netdev->stats.tx_packets++;
++              ch->netdev->stats.tx_bytes += ch->skb[ch->tx_free]->len;
+               dev_kfree_skb_any(ch->skb[ch->tx_free]);
+               ch->skb[ch->tx_free] = NULL;
+               memset(&ch->dma.desc_base[ch->tx_free], 0,
+@@ -203,7 +265,9 @@ ltq_etop_poll_tx(struct napi_struct *napi, int budget)
+       if (netif_tx_queue_stopped(txq))
+               netif_tx_start_queue(txq);
+       napi_complete(&ch->napi);
++      spin_lock_irqsave(&priv->lock, flags);
+       ltq_dma_ack_irq(&ch->dma);
++      spin_unlock_irqrestore(&priv->lock, flags);
+       return 1;
+ }
+@@ -211,9 +275,10 @@ static irqreturn_t
+ ltq_etop_dma_irq(int irq, void *_priv)
+ {
+       struct ltq_etop_priv *priv = _priv;
+-      int ch = irq - LTQ_DMA_CH0_INT;
+-
+-      napi_schedule(&priv->ch[ch].napi);
++      if (irq == priv->txch.dma.irq)
++              napi_schedule(&priv->txch.napi);
++      else
++              napi_schedule(&priv->rxch.napi);
+       return IRQ_HANDLED;
+ }
+@@ -225,7 +290,7 @@ ltq_etop_free_channel(struct net_device *dev, struct ltq_etop_chan *ch)
+       ltq_dma_free(&ch->dma);
+       if (ch->dma.irq)
+               free_irq(ch->dma.irq, priv);
+-      if (IS_RX(ch->idx)) {
++      if (ch == &priv->txch) {
+               int desc;
+               for (desc = 0; desc < LTQ_DESC_NUM; desc++)
+                       dev_kfree_skb_any(ch->skb[ch->dma.desc]);
+@@ -236,23 +301,59 @@ static void
+ ltq_etop_hw_exit(struct net_device *dev)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      int i;
+-      ltq_pmu_disable(PMU_PPE);
+-      for (i = 0; i < MAX_DMA_CHAN; i++)
+-              if (IS_TX(i) || IS_RX(i))
+-                      ltq_etop_free_channel(dev, &priv->ch[i]);
++      clk_disable(priv->clk_ppe);
++
++      if (of_machine_is_compatible("lantiq,ar9"))
++              clk_disable(priv->clk_switch);
++
++      if (of_machine_is_compatible("lantiq,ase")) {
++              clk_disable(priv->clk_ephy);
++              clk_disable(priv->clk_ephycgu);
++      }
++
++      ltq_etop_free_channel(dev, &priv->txch);
++      ltq_etop_free_channel(dev, &priv->rxch);
++}
++
++static void
++ltq_etop_gbit_init(struct net_device *dev)
++{
++      struct ltq_etop_priv *priv = netdev_priv(dev);
++
++      clk_enable(priv->clk_switch);
++
++      ltq_gbit_w32_mask(0, GCTL0_SE, LTQ_GBIT_GCTL0);
++      /** Disable MDIO auto polling mode */
++      ltq_gbit_w32_mask(0, PX_CTL_DMDIO, LTQ_GBIT_P0_CTL);
++      /* set 1522 packet size */
++      ltq_gbit_w32_mask(0x300, 0, LTQ_GBIT_GCTL0);
++      /* disable pmac & dmac headers */
++      ltq_gbit_w32_mask(PMAC_HD_CTL_AS | PMAC_HD_CTL_RXSH, 0,
++              LTQ_GBIT_PMAC_HD_CTL);
++      /* Due to traffic halt when burst length 8,
++              replace default IPG value with 0x3B */
++      ltq_gbit_w32(0x3B, LTQ_GBIT_PMAC_RX_IPG);
++      /* set mdc clock to 2.5 MHz */
++      ltq_gbit_w32_mask(MDC_CLOCK_MASK, 4 << MDC_CLOCK_OFFSET,
++              LTQ_GBIT_RGMII_CTL);
+ }
+ static int
+ ltq_etop_hw_init(struct net_device *dev)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      int i;
++      int mii_mode = priv->mii_mode;
++
++      clk_enable(priv->clk_ppe);
+-      ltq_pmu_enable(PMU_PPE);
++      if (of_machine_is_compatible("lantiq,ar9")) {
++              ltq_etop_gbit_init(dev);
++              /* force the etops link to the gbit to MII */
++              mii_mode = PHY_INTERFACE_MODE_MII;
++      }
+-      switch (priv->pldata->mii_mode) {
++      switch (mii_mode) {
+       case PHY_INTERFACE_MODE_RMII:
+               ltq_etop_w32_mask(ETOP_MII_MASK,
+                       ETOP_MII_REVERSE, LTQ_ETOP_CFG);
+@@ -264,39 +365,68 @@ ltq_etop_hw_init(struct net_device *dev)
+               break;
+       default:
++              if (of_machine_is_compatible("lantiq,ase")) {
++                      clk_enable(priv->clk_ephy);
++                      /* disable external MII */
++                      ltq_etop_w32_mask(0, ETOP_CFG_MII0, LTQ_ETOP_CFG);
++                      /* enable clock for internal PHY */
++                      clk_enable(priv->clk_ephycgu);
++                      /* we need to write this magic to the internal phy to
++                         make it work */
++                      ltq_etop_mdio_wr(NULL, 0x8, 0x12, 0xC020);
++                      pr_info("Selected EPHY mode\n");
++                      break;
++              }
+               netdev_err(dev, "unknown mii mode %d\n",
+-                      priv->pldata->mii_mode);
++                      mii_mode);
+               return -ENOTSUPP;
+       }
+       /* enable crc generation */
+       ltq_etop_w32(PPE32_CGEN, LQ_PPE32_ENET_MAC_CFG);
++      return 0;
++}
++
++static int
++ltq_etop_dma_init(struct net_device *dev)
++{
++      struct ltq_etop_priv *priv = netdev_priv(dev);
++      int tx = priv->tx_irq - LTQ_DMA_ETOP;
++      int rx = priv->rx_irq - LTQ_DMA_ETOP;
++      int err;
++
+       ltq_dma_init_port(DMA_PORT_ETOP);
+-      for (i = 0; i < MAX_DMA_CHAN; i++) {
+-              int irq = LTQ_DMA_CH0_INT + i;
+-              struct ltq_etop_chan *ch = &priv->ch[i];
+-
+-              ch->idx = ch->dma.nr = i;
+-
+-              if (IS_TX(i)) {
+-                      ltq_dma_alloc_tx(&ch->dma);
+-                      request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED,
+-                              "etop_tx", priv);
+-              } else if (IS_RX(i)) {
+-                      ltq_dma_alloc_rx(&ch->dma);
+-                      for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM;
+-                                      ch->dma.desc++)
+-                              if (ltq_etop_alloc_skb(ch))
+-                                      return -ENOMEM;
+-                      ch->dma.desc = 0;
+-                      request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED,
+-                              "etop_rx", priv);
++      priv->txch.dma.nr = tx;
++      ltq_dma_alloc_tx(&priv->txch.dma);
++      err = request_irq(priv->tx_irq, ltq_etop_dma_irq, IRQF_DISABLED,
++              "eth_tx", priv);
++      if (err) {
++              netdev_err(dev, "failed to allocate tx irq\n");
++              goto err_out;
++      }
++      priv->txch.dma.irq = priv->tx_irq;
++
++      priv->rxch.dma.nr = rx;
++      ltq_dma_alloc_rx(&priv->rxch.dma);
++      for (priv->rxch.dma.desc = 0; priv->rxch.dma.desc < LTQ_DESC_NUM;
++                      priv->rxch.dma.desc++) {
++              if (ltq_etop_alloc_skb(&priv->rxch)) {
++                      netdev_err(dev, "failed to allocate skbs\n");
++                      err = -ENOMEM;
++                      goto err_out;
+               }
+-              ch->dma.irq = irq;
+       }
+-      return 0;
++      priv->rxch.dma.desc = 0;
++      err = request_irq(priv->rx_irq, ltq_etop_dma_irq, IRQF_DISABLED,
++              "eth_rx", priv);
++      if (err)
++              netdev_err(dev, "failed to allocate rx irq\n");
++      else
++              priv->rxch.dma.irq = priv->rx_irq;
++err_out:
++      return err;
+ }
+ static void
+@@ -312,7 +442,10 @@ ltq_etop_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      return phy_ethtool_gset(priv->phydev, cmd);
++      if (priv->phydev)
++              return phy_ethtool_gset(priv->phydev, cmd);
++      else
++              return 0;
+ }
+ static int
+@@ -320,7 +453,10 @@ ltq_etop_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      return phy_ethtool_sset(priv->phydev, cmd);
++      if (priv->phydev)
++              return phy_ethtool_sset(priv->phydev, cmd);
++      else
++              return 0;
+ }
+ static int
+@@ -328,7 +464,10 @@ ltq_etop_nway_reset(struct net_device *dev)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      return phy_start_aneg(priv->phydev);
++      if (priv->phydev)
++              return phy_start_aneg(priv->phydev);
++      else
++              return 0;
+ }
+ static const struct ethtool_ops ltq_etop_ethtool_ops = {
+@@ -339,6 +478,39 @@ static const struct ethtool_ops ltq_etop_ethtool_ops = {
+ };
+ static int
++ltq_etop_mdio_wr_xr9(struct mii_bus *bus, int phy_addr,
++              int phy_reg, u16 phy_data)
++{
++      u32 val = MDIO_XR9_REQUEST | MDIO_XR9_WRITE |
++              (phy_data << MDIO_XR9_WR_OFFSET) |
++              ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) |
++              ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET);
++
++      while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST)
++              ;
++      ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL);
++      while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST)
++              ;
++      return 0;
++}
++
++static int
++ltq_etop_mdio_rd_xr9(struct mii_bus *bus, int phy_addr, int phy_reg)
++{
++      u32 val = MDIO_XR9_REQUEST | MDIO_XR9_READ |
++              ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) |
++              ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET);
++
++      while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST)
++              ;
++      ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL);
++      while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST)
++              ;
++      val = ltq_gbit_r32(LTQ_GBIT_MDIO_DATA) & MDIO_XR9_RD_MASK;
++      return val;
++}
++
++static int
+ ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, int phy_reg, u16 phy_data)
+ {
+       u32 val = MDIO_REQUEST |
+@@ -379,14 +551,18 @@ ltq_etop_mdio_probe(struct net_device *dev)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+       struct phy_device *phydev = NULL;
+-      int phy_addr;
+-
+-      for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+-              if (priv->mii_bus->phy_map[phy_addr]) {
+-                      phydev = priv->mii_bus->phy_map[phy_addr];
+-                      break;
+-              }
+-      }
++      u32 phy_supported =  (SUPPORTED_10baseT_Half
++                      | SUPPORTED_10baseT_Full
++                      | SUPPORTED_100baseT_Half
++                      | SUPPORTED_100baseT_Full
++                      | SUPPORTED_Autoneg
++                      | SUPPORTED_MII
++                      | SUPPORTED_TP);
++
++      if (of_machine_is_compatible("lantiq,ase"))
++              phydev = priv->mii_bus->phy_map[8];
++      else
++              phydev = priv->mii_bus->phy_map[0];
+       if (!phydev) {
+               netdev_err(dev, "no PHY found\n");
+@@ -394,21 +570,18 @@ ltq_etop_mdio_probe(struct net_device *dev)
+       }
+       phydev = phy_connect(dev, dev_name(&phydev->dev), &ltq_etop_mdio_link,
+-                      0, priv->pldata->mii_mode);
++                      0, priv->mii_mode);
+       if (IS_ERR(phydev)) {
+               netdev_err(dev, "Could not attach to PHY\n");
+               return PTR_ERR(phydev);
+       }
+-      phydev->supported &= (SUPPORTED_10baseT_Half
+-                            | SUPPORTED_10baseT_Full
+-                            | SUPPORTED_100baseT_Half
+-                            | SUPPORTED_100baseT_Full
+-                            | SUPPORTED_Autoneg
+-                            | SUPPORTED_MII
+-                            | SUPPORTED_TP);
++      if (of_machine_is_compatible("lantiq,ar9"))
++              phy_supported |= SUPPORTED_1000baseT_Half
++                      | SUPPORTED_1000baseT_Full;
++      phydev->supported &= phy_supported;
+       phydev->advertising = phydev->supported;
+       priv->phydev = phydev;
+       pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n",
+@@ -433,8 +606,13 @@ ltq_etop_mdio_init(struct net_device *dev)
+       }
+       priv->mii_bus->priv = dev;
+-      priv->mii_bus->read = ltq_etop_mdio_rd;
+-      priv->mii_bus->write = ltq_etop_mdio_wr;
++      if (of_machine_is_compatible("lantiq,ar9")) {
++              priv->mii_bus->read = ltq_etop_mdio_rd_xr9;
++              priv->mii_bus->write = ltq_etop_mdio_wr_xr9;
++      } else {
++              priv->mii_bus->read = ltq_etop_mdio_rd;
++              priv->mii_bus->write = ltq_etop_mdio_wr;
++      }
+       priv->mii_bus->name = "ltq_mii";
+       snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+               priv->pdev->name, priv->pdev->id);
+@@ -483,17 +661,19 @@ static int
+ ltq_etop_open(struct net_device *dev)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      int i;
++      unsigned long flags;
+-      for (i = 0; i < MAX_DMA_CHAN; i++) {
+-              struct ltq_etop_chan *ch = &priv->ch[i];
++      napi_enable(&priv->txch.napi);
++      napi_enable(&priv->rxch.napi);
++
++      spin_lock_irqsave(&priv->lock, flags);
++      ltq_dma_open(&priv->txch.dma);
++      ltq_dma_open(&priv->rxch.dma);
++      spin_unlock_irqrestore(&priv->lock, flags);
++
++      if (priv->phydev)
++              phy_start(priv->phydev);
+-              if (!IS_TX(i) && (!IS_RX(i)))
+-                      continue;
+-              ltq_dma_open(&ch->dma);
+-              napi_enable(&ch->napi);
+-      }
+-      phy_start(priv->phydev);
+       netif_tx_start_all_queues(dev);
+       return 0;
+ }
+@@ -502,18 +682,19 @@ static int
+ ltq_etop_stop(struct net_device *dev)
+ {
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      int i;
++      unsigned long flags;
+       netif_tx_stop_all_queues(dev);
+-      phy_stop(priv->phydev);
+-      for (i = 0; i < MAX_DMA_CHAN; i++) {
+-              struct ltq_etop_chan *ch = &priv->ch[i];
++      if (priv->phydev)
++              phy_stop(priv->phydev);
++      napi_disable(&priv->txch.napi);
++      napi_disable(&priv->rxch.napi);
++
++      spin_lock_irqsave(&priv->lock, flags);
++      ltq_dma_close(&priv->txch.dma);
++      ltq_dma_close(&priv->rxch.dma);
++      spin_unlock_irqrestore(&priv->lock, flags);
+-              if (!IS_RX(i) && !IS_TX(i))
+-                      continue;
+-              napi_disable(&ch->napi);
+-              ltq_dma_close(&ch->dma);
+-      }
+       return 0;
+ }
+@@ -523,16 +704,16 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev)
+       int queue = skb_get_queue_mapping(skb);
+       struct netdev_queue *txq = netdev_get_tx_queue(dev, queue);
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+-      struct ltq_etop_chan *ch = &priv->ch[(queue << 1) | 1];
+-      struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
+-      int len;
++      struct ltq_dma_desc *desc =
++              &priv->txch.dma.desc_base[priv->txch.dma.desc];
+       unsigned long flags;
+       u32 byte_offset;
++      int len;
+       len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
+-      if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) {
+-              dev_kfree_skb_any(skb);
++      if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) ||
++                      priv->txch.skb[priv->txch.dma.desc]) {
+               netdev_err(dev, "tx ring full\n");
+               netif_tx_stop_queue(txq);
+               return NETDEV_TX_BUSY;
+@@ -540,7 +721,7 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev)
+       /* dma needs to start on a 16 byte aligned address */
+       byte_offset = CPHYSADDR(skb->data) % 16;
+-      ch->skb[ch->dma.desc] = skb;
++      priv->txch.skb[priv->txch.dma.desc] = skb;
+       dev->trans_start = jiffies;
+@@ -550,11 +731,11 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev)
+       wmb();
+       desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP |
+               LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK);
+-      ch->dma.desc++;
+-      ch->dma.desc %= LTQ_DESC_NUM;
++      priv->txch.dma.desc++;
++      priv->txch.dma.desc %= LTQ_DESC_NUM;
+       spin_unlock_irqrestore(&priv->lock, flags);
+-      if (ch->dma.desc_base[ch->dma.desc].ctl & LTQ_DMA_OWN)
++      if (priv->txch.dma.desc_base[priv->txch.dma.desc].ctl & LTQ_DMA_OWN)
+               netif_tx_stop_queue(txq);
+       return NETDEV_TX_OK;
+@@ -633,34 +814,32 @@ ltq_etop_init(struct net_device *dev)
+       struct ltq_etop_priv *priv = netdev_priv(dev);
+       struct sockaddr mac;
+       int err;
+-      bool random_mac = false;
+       ether_setup(dev);
+       dev->watchdog_timeo = 10 * HZ;
+       err = ltq_etop_hw_init(dev);
+       if (err)
+               goto err_hw;
++      err = ltq_etop_dma_init(dev);
++      if (err)
++              goto err_hw;
++
+       ltq_etop_change_mtu(dev, 1500);
+-      memcpy(&mac, &priv->pldata->mac, sizeof(struct sockaddr));
++      memcpy(&mac.sa_data, priv->mac, ETH_ALEN);
+       if (!is_valid_ether_addr(mac.sa_data)) {
+               pr_warn("etop: invalid MAC, using random\n");
+-              eth_random_addr(mac.sa_data);
+-              random_mac = true;
++              random_ether_addr(mac.sa_data);
+       }
+       err = ltq_etop_set_mac_address(dev, &mac);
+       if (err)
+               goto err_netdev;
+-
+-      /* Set addr_assign_type here, ltq_etop_set_mac_address would reset it. */
+-      if (random_mac)
+-              dev->addr_assign_type |= NET_ADDR_RANDOM;
+-
+       ltq_etop_set_multicast_list(dev);
+-      err = ltq_etop_mdio_init(dev);
+-      if (err)
+-              goto err_netdev;
++      if (!ltq_etop_mdio_init(dev))
++              dev->ethtool_ops = &ltq_etop_ethtool_ops;
++      else
++              pr_warn("etop: mdio probe failed\n");;
+       return 0;
+ err_netdev:
+@@ -680,6 +859,9 @@ ltq_etop_tx_timeout(struct net_device *dev)
+       err = ltq_etop_hw_init(dev);
+       if (err)
+               goto err_hw;
++      err = ltq_etop_dma_init(dev);
++      if (err)
++              goto err_hw;
+       dev->trans_start = jiffies;
+       netif_wake_queue(dev);
+       return;
+@@ -703,14 +885,19 @@ static const struct net_device_ops ltq_eth_netdev_ops = {
+       .ndo_tx_timeout = ltq_etop_tx_timeout,
+ };
+-static int __init
++static int __devinit
+ ltq_etop_probe(struct platform_device *pdev)
+ {
+       struct net_device *dev;
+       struct ltq_etop_priv *priv;
+-      struct resource *res;
++      struct resource *res, *gbit_res, irqres[2];
+       int err;
+-      int i;
++
++      err = of_irq_to_resource_table(pdev->dev.of_node, irqres, 2);
++      if (err != 2) {
++              dev_err(&pdev->dev, "failed to get etop irqs\n");
++              return -EINVAL;
++      }
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+@@ -736,30 +923,58 @@ ltq_etop_probe(struct platform_device *pdev)
+               goto err_out;
+       }
+-      dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4);
+-      if (!dev) {
+-              err = -ENOMEM;
+-              goto err_out;
++      if (of_machine_is_compatible("lantiq,ar9")) {
++              gbit_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++              if (!gbit_res) {
++                      dev_err(&pdev->dev, "failed to get gbit resource\n");
++                      err = -ENOENT;
++                      goto err_out;
++              }
++              ltq_gbit_membase = devm_ioremap_nocache(&pdev->dev,
++                      gbit_res->start, resource_size(gbit_res));
++              if (!ltq_gbit_membase) {
++                      dev_err(&pdev->dev, "failed to remap gigabit switch %d\n",
++                              pdev->id);
++                      err = -ENOMEM;
++                      goto err_out;
++              }
+       }
++
++      dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4);
+       strcpy(dev->name, "eth%d");
+       dev->netdev_ops = &ltq_eth_netdev_ops;
+-      dev->ethtool_ops = &ltq_etop_ethtool_ops;
+       priv = netdev_priv(dev);
+       priv->res = res;
+       priv->pdev = pdev;
+-      priv->pldata = dev_get_platdata(&pdev->dev);
+       priv->netdev = dev;
++      priv->tx_irq = irqres[0].start;
++      priv->rx_irq = irqres[1].start;
++      priv->mii_mode = of_get_phy_mode(pdev->dev.of_node);
++      priv->mac = of_get_mac_address(pdev->dev.of_node);
++
++      priv->clk_ppe = clk_get(&pdev->dev, NULL);
++      if (IS_ERR(priv->clk_ppe))
++              return PTR_ERR(priv->clk_ppe);
++      if (of_machine_is_compatible("lantiq,ar9")) {
++              priv->clk_switch = clk_get(&pdev->dev, "switch");
++              if (IS_ERR(priv->clk_switch))
++                      return PTR_ERR(priv->clk_switch);
++      }
++      if (of_machine_is_compatible("lantiq,ase")) {
++              priv->clk_ephy = clk_get(&pdev->dev, "ephy");
++              if (IS_ERR(priv->clk_ephy))
++                      return PTR_ERR(priv->clk_ephy);
++              priv->clk_ephycgu = clk_get(&pdev->dev, "ephycgu");
++              if (IS_ERR(priv->clk_ephycgu))
++                      return PTR_ERR(priv->clk_ephycgu);
++      }
++
+       spin_lock_init(&priv->lock);
+-      for (i = 0; i < MAX_DMA_CHAN; i++) {
+-              if (IS_TX(i))
+-                      netif_napi_add(dev, &priv->ch[i].napi,
+-                              ltq_etop_poll_tx, 8);
+-              else if (IS_RX(i))
+-                      netif_napi_add(dev, &priv->ch[i].napi,
+-                              ltq_etop_poll_rx, 32);
+-              priv->ch[i].netdev = dev;
+-      }
++      netif_napi_add(dev, &priv->txch.napi, ltq_etop_poll_tx, 8);
++      netif_napi_add(dev, &priv->rxch.napi, ltq_etop_poll_rx, 32);
++      priv->txch.netdev = dev;
++      priv->rxch.netdev = dev;
+       err = register_netdev(dev);
+       if (err)
+@@ -788,32 +1003,23 @@ ltq_etop_remove(struct platform_device *pdev)
+       return 0;
+ }
++static const struct of_device_id ltq_etop_match[] = {
++      { .compatible = "lantiq,etop-xway" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, ltq_etop_match);
++
+ static struct platform_driver ltq_mii_driver = {
++      .probe = ltq_etop_probe,
+       .remove = ltq_etop_remove,
+       .driver = {
+               .name = "ltq_etop",
+               .owner = THIS_MODULE,
++              .of_match_table = ltq_etop_match,
+       },
+ };
+-int __init
+-init_ltq_etop(void)
+-{
+-      int ret = platform_driver_probe(&ltq_mii_driver, ltq_etop_probe);
+-
+-      if (ret)
+-              pr_err("ltq_etop: Error registering platform driver!");
+-      return ret;
+-}
+-
+-static void __exit
+-exit_ltq_etop(void)
+-{
+-      platform_driver_unregister(&ltq_mii_driver);
+-}
+-
+-module_init(init_ltq_etop);
+-module_exit(exit_ltq_etop);
++module_platform_driver(ltq_mii_driver);
+ MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+ MODULE_DESCRIPTION("Lantiq SoC ETOP");
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0027-NET-PHY-adds-driver-for-lantiq-PHY11G.patch b/target/linux/lantiq/patches-3.8/0027-NET-PHY-adds-driver-for-lantiq-PHY11G.patch
new file mode 100644 (file)
index 0000000..5250377
--- /dev/null
@@ -0,0 +1,270 @@
+From 0721e9f0502e633390044e651970692213283686 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 13 Mar 2013 09:30:22 +0100
+Subject: [PATCH 27/40] NET: PHY: adds driver for lantiq PHY11G
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/phy/Kconfig  |    5 ++
+ drivers/net/phy/Makefile |    1 +
+ drivers/net/phy/lantiq.c |  220 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 226 insertions(+)
+ create mode 100644 drivers/net/phy/lantiq.c
+
+diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
+index 961f0b2..41a2992 100644
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -107,6 +107,11 @@ config MICREL_PHY
+       ---help---
+         Supports the KSZ9021, VSC8201, KS8001 PHYs.
++config LANTIQ_PHY
++      tristate "Driver for Lantiq PHYs"
++      ---help---
++        Supports the 11G and 22E PHYs.
++
+ config FIXED_PHY
+       bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
+       depends on PHYLIB=y
+diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
+index 9645e38..e2eeee3 100644
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -23,6 +23,7 @@ obj-$(CONFIG_NATIONAL_PHY)   += national.o
+ obj-$(CONFIG_DP83640_PHY)     += dp83640.o
+ obj-$(CONFIG_STE10XP)         += ste10Xp.o
+ obj-$(CONFIG_MICREL_PHY)      += micrel.o
++obj-$(CONFIG_LANTIQ_PHY)        += lantiq.o
+ obj-$(CONFIG_MDIO_OCTEON)     += mdio-octeon.o
+ obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
+diff --git a/drivers/net/phy/lantiq.c b/drivers/net/phy/lantiq.c
+new file mode 100644
+index 0000000..418dff0
+--- /dev/null
++++ b/drivers/net/phy/lantiq.c
+@@ -0,0 +1,220 @@
++/*
++ *   This program is free software; you can redistribute it and/or modify
++ *   it under the terms of the GNU General Public License as published by
++ *   the Free Software Foundation; either version 2 of the License, or
++ *   (at your option) any later version.
++ *
++ *   This program is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   You should have received a copy of the GNU General Public License
++ *   along with this program; if not, write to the Free Software
++ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
++ *
++ *   Copyright (C) 2012 Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>
++ */
++
++#include <linux/module.h>
++#include <linux/phy.h>
++
++#define MII_MMDCTRL           0x0d
++#define MII_MMDDATA           0x0e
++
++#define MII_VR9_11G_IMASK     0x19    /* interrupt mask */
++#define MII_VR9_11G_ISTAT     0x1a    /* interrupt status */
++
++#define INT_VR9_11G_WOL               BIT(15) /* Wake-On-LAN */
++#define INT_VR9_11G_ANE               BIT(11) /* Auto-Neg error */
++#define INT_VR9_11G_ANC               BIT(10) /* Auto-Neg complete */
++#define INT_VR9_11G_ADSC      BIT(5)  /* Link auto-downspeed detect */
++#define INT_VR9_11G_DXMC      BIT(2)  /* Duplex mode change */
++#define INT_VR9_11G_LSPC      BIT(1)  /* Link speed change */
++#define INT_VR9_11G_LSTC      BIT(0)  /* Link state change */
++#define INT_VR9_11G_MASK      (INT_VR9_11G_LSTC | INT_VR9_11G_ADSC)
++
++#define ADVERTISED_MPD                BIT(10) /* Multi-port device */
++
++#define MMD_DEVAD             0x1f
++#define MMD_ACTYPE_SHIFT      14
++#define MMD_ACTYPE_ADDRESS    (0 << MMD_ACTYPE_SHIFT)
++#define MMD_ACTYPE_DATA               (1 << MMD_ACTYPE_SHIFT)
++#define MMD_ACTYPE_DATA_PI    (2 << MMD_ACTYPE_SHIFT)
++#define MMD_ACTYPE_DATA_PIWR  (3 << MMD_ACTYPE_SHIFT)
++
++static __maybe_unused int vr9_gphy_mmd_read(struct phy_device *phydev,
++                                              u16 regnum)
++{
++      phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_ADDRESS | MMD_DEVAD);
++      phy_write(phydev, MII_MMDDATA, regnum);
++      phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_DATA | MMD_DEVAD);
++
++      return phy_read(phydev, MII_MMDDATA);
++}
++
++static __maybe_unused int vr9_gphy_mmd_write(struct phy_device *phydev,
++                                              u16 regnum, u16 val)
++{
++      phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_ADDRESS | MMD_DEVAD);
++      phy_write(phydev, MII_MMDDATA, regnum);
++      phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_DATA | MMD_DEVAD);
++      phy_write(phydev, MII_MMDDATA, val);
++
++      return 0;
++}
++
++static int vr9_gphy_config_init(struct phy_device *phydev)
++{
++      int err;
++
++      dev_dbg(&phydev->dev, "%s\n", __func__);
++
++      /* Mask all interrupts */
++      err = phy_write(phydev, MII_VR9_11G_IMASK, 0);
++      if (err)
++              return err;
++
++      /* Clear all pending interrupts */
++      phy_read(phydev, MII_VR9_11G_ISTAT);
++
++      return 0;
++}
++
++static int vr9_gphy_config_aneg(struct phy_device *phydev)
++{
++      int reg, err;
++
++      /* Advertise as multi-port device */
++      reg = phy_read(phydev, MII_CTRL1000);
++      reg |= ADVERTISED_MPD;
++      err = phy_write(phydev, MII_CTRL1000, reg);
++      if (err)
++              return err;
++
++      return genphy_config_aneg(phydev);
++}
++
++static int vr9_gphy_ack_interrupt(struct phy_device *phydev)
++{
++      int reg;
++
++      /*
++       * Possible IRQ numbers:
++       * - IM3_IRL18 for GPHY0
++       * - IM3_IRL17 for GPHY1
++       *
++       * Due to a silicon bug IRQ lines are not really independent from
++       * each other. Sometimes the two lines are driven at the same time
++       * if only one GPHY core raises the interrupt.
++       */
++
++      reg = phy_read(phydev, MII_VR9_11G_ISTAT);
++
++      return (reg < 0) ? reg : 0;
++}
++
++static int vr9_gphy_did_interrupt(struct phy_device *phydev)
++{
++      int reg;
++
++      reg = phy_read(phydev, MII_VR9_11G_ISTAT);
++
++      return reg > 0;
++}
++
++static int vr9_gphy_config_intr(struct phy_device *phydev)
++{
++      int err;
++
++      if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
++              err = phy_write(phydev, MII_VR9_11G_IMASK, INT_VR9_11G_MASK);
++      else
++              err = phy_write(phydev, MII_VR9_11G_IMASK, 0);
++
++      return err;
++}
++
++static struct phy_driver lantiq_phy[] = {
++      {
++              .phy_id         = 0xd565a400,
++              .phy_id_mask    = 0xffffffff,
++              .name           = "Lantiq XWAY PEF7071",
++              .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
++              .flags          = 0, /*PHY_HAS_INTERRUPT,*/
++              .config_init    = vr9_gphy_config_init,
++              .config_aneg    = vr9_gphy_config_aneg,
++              .read_status    = genphy_read_status,
++              .ack_interrupt  = vr9_gphy_ack_interrupt,
++              .did_interrupt  = vr9_gphy_did_interrupt,
++              .config_intr    = vr9_gphy_config_intr,
++              .driver         = { .owner = THIS_MODULE },
++      }, {
++              .phy_id         = 0x030260D0,
++              .phy_id_mask    = 0xfffffff0,
++              .name           = "Lantiq XWAY VR9 GPHY 11G v1.3",
++              .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
++              .flags          = 0, /*PHY_HAS_INTERRUPT,*/
++              .config_init    = vr9_gphy_config_init,
++              .config_aneg    = vr9_gphy_config_aneg,
++              .read_status    = genphy_read_status,
++              .ack_interrupt  = vr9_gphy_ack_interrupt,
++              .did_interrupt  = vr9_gphy_did_interrupt,
++              .config_intr    = vr9_gphy_config_intr,
++              .driver         = { .owner = THIS_MODULE },
++      }, {
++              .phy_id         = 0xd565a408,
++              .phy_id_mask    = 0xfffffff8,
++              .name           = "Lantiq XWAY VR9 GPHY 11G v1.4",
++              .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
++              .flags          = 0, /*PHY_HAS_INTERRUPT,*/
++              .config_init    = vr9_gphy_config_init,
++              .config_aneg    = vr9_gphy_config_aneg,
++              .read_status    = genphy_read_status,
++              .ack_interrupt  = vr9_gphy_ack_interrupt,
++              .did_interrupt  = vr9_gphy_did_interrupt,
++              .config_intr    = vr9_gphy_config_intr,
++              .driver         = { .owner = THIS_MODULE },
++      }, {
++              .phy_id         = 0xd565a418,
++              .phy_id_mask    = 0xfffffff8,
++              .name           = "Lantiq XWAY XRX PHY22F v1.4",
++              .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
++              .flags          = 0, /*PHY_HAS_INTERRUPT,*/
++              .config_init    = vr9_gphy_config_init,
++              .config_aneg    = vr9_gphy_config_aneg,
++              .read_status    = genphy_read_status,
++              .ack_interrupt  = vr9_gphy_ack_interrupt,
++              .did_interrupt  = vr9_gphy_did_interrupt,
++              .config_intr    = vr9_gphy_config_intr,
++              .driver         = { .owner = THIS_MODULE },
++      },
++};
++
++static int __init ltq_phy_init(void)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(lantiq_phy); i++) {
++              int err = phy_driver_register(&lantiq_phy[i]);
++              if (err)
++                      pr_err("lantiq_phy: failed to load %s\n", lantiq_phy[i].name);
++      }
++
++      return 0;
++}
++
++static void __exit ltq_phy_exit(void)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(lantiq_phy); i++)
++              phy_driver_unregister(&lantiq_phy[i]);
++}
++
++module_init(ltq_phy_init);
++module_exit(ltq_phy_exit);
++
++MODULE_DESCRIPTION("Lantiq PHY drivers");
++MODULE_AUTHOR("Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>");
++MODULE_LICENSE("GPL");
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0028-NET-lantiq-adds-PHY11G-firmware-blobs.patch b/target/linux/lantiq/patches-3.8/0028-NET-lantiq-adds-PHY11G-firmware-blobs.patch
new file mode 100644 (file)
index 0000000..1169201
--- /dev/null
@@ -0,0 +1,373 @@
+From 9664031d0f35be450330bf30ded1c359b9074251 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Mon, 22 Oct 2012 09:26:24 +0200
+Subject: [PATCH 28/40] NET: lantiq: adds PHY11G firmware blobs
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ firmware/Makefile       |    2 +
+ firmware/lantiq/COPYING |  286 +++++++++++++++++++++++++++++++++++++++++++++++
+ firmware/lantiq/README  |   45 ++++++++
+ 3 files changed, 333 insertions(+)
+ create mode 100644 firmware/lantiq/COPYING
+ create mode 100644 firmware/lantiq/README
+
+diff --git a/firmware/Makefile b/firmware/Makefile
+index cbb09ce..cdc0aef 100644
+--- a/firmware/Makefile
++++ b/firmware/Makefile
+@@ -134,6 +134,8 @@ fw-shipped-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda/keyspan_pda.fw
+ fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw
+ fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw
+ fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin
++fw-shipped-$(CONFIG_SOC_TYPE_XWAY) += lantiq/vr9_phy11g_a1x.bin
++fw-shipped-$(CONFIG_SOC_TYPE_XWAY) += lantiq/vr9_phy11g_a2x.bin
+ fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
+ fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
+diff --git a/firmware/lantiq/COPYING b/firmware/lantiq/COPYING
+new file mode 100644
+index 0000000..5ec70b2
+--- /dev/null
++++ b/firmware/lantiq/COPYING
+@@ -0,0 +1,286 @@
++All firmware files are copyrighted by Lantiq Deutschland GmbH.
++The files have been extracted from header files found in Lantiq BSPs.
++If not stated otherwise all files are licensed under GPL.
++
++=======================================================================
++
++                  GNU GENERAL PUBLIC LICENSE
++                     Version 2, June 1991
++
++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
++     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++                          Preamble
++
++  The licenses for most software are designed to take away your
++freedom to share and change it.  By contrast, the GNU General Public
++License is intended to guarantee your freedom to share and change free
++software--to make sure the software is free for all its users.  This
++General Public License applies to most of the Free Software
++Foundation's software and to any other program whose authors commit to
++using it.  (Some other Free Software Foundation software is covered by
++the GNU Library General Public License instead.)  You can apply it to
++your programs, too.
++
++  When we speak of free software, we are referring to freedom, not
++price.  Our General Public Licenses are designed to make sure that you
++have the freedom to distribute copies of free software (and charge for
++this service if you wish), that you receive source code or can get it
++if you want it, that you can change the software or use pieces of it
++in new free programs; and that you know you can do these things.
++
++  To protect your rights, we need to make restrictions that forbid
++anyone to deny you these rights or to ask you to surrender the rights.
++These restrictions translate to certain responsibilities for you if you
++distribute copies of the software, or if you modify it.
++
++  For example, if you distribute copies of such a program, whether
++gratis or for a fee, you must give the recipients all the rights that
++you have.  You must make sure that they, too, receive or can get the
++source code.  And you must show them these terms so they know their
++rights.
++
++  We protect your rights with two steps: (1) copyright the software, and
++(2) offer you this license which gives you legal permission to copy,
++distribute and/or modify the software.
++
++  Also, for each author's protection and ours, we want to make certain
++that everyone understands that there is no warranty for this free
++software.  If the software is modified by someone else and passed on, we
++want its recipients to know that what they have is not the original, so
++that any problems introduced by others will not reflect on the original
++authors' reputations.
++
++  Finally, any free program is threatened constantly by software
++patents.  We wish to avoid the danger that redistributors of a free
++program will individually obtain patent licenses, in effect making the
++program proprietary.  To prevent this, we have made it clear that any
++patent must be licensed for everyone's free use or not licensed at all.
++
++  The precise terms and conditions for copying, distribution and
++modification follow.
++\f
++                  GNU GENERAL PUBLIC LICENSE
++   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
++
++  0. This License applies to any program or other work which contains
++a notice placed by the copyright holder saying it may be distributed
++under the terms of this General Public License.  The "Program", below,
++refers to any such program or work, and a "work based on the Program"
++means either the Program or any derivative work under copyright law:
++that is to say, a work containing the Program or a portion of it,
++either verbatim or with modifications and/or translated into another
++language.  (Hereinafter, translation is included without limitation in
++the term "modification".)  Each licensee is addressed as "you".
++
++Activities other than copying, distribution and modification are not
++covered by this License; they are outside its scope.  The act of
++running the Program is not restricted, and the output from the Program
++is covered only if its contents constitute a work based on the
++Program (independent of having been made by running the Program).
++Whether that is true depends on what the Program does.
++
++  1. You may copy and distribute verbatim copies of the Program's
++source code as you receive it, in any medium, provided that you
++conspicuously and appropriately publish on each copy an appropriate
++copyright notice and disclaimer of warranty; keep intact all the
++notices that refer to this License and to the absence of any warranty;
++and give any other recipients of the Program a copy of this License
++along with the Program.
++
++You may charge a fee for the physical act of transferring a copy, and
++you may at your option offer warranty protection in exchange for a fee.
++
++  2. You may modify your copy or copies of the Program or any portion
++of it, thus forming a work based on the Program, and copy and
++distribute such modifications or work under the terms of Section 1
++above, provided that you also meet all of these conditions:
++
++    a) You must cause the modified files to carry prominent notices
++    stating that you changed the files and the date of any change.
++
++    b) You must cause any work that you distribute or publish, that in
++    whole or in part contains or is derived from the Program or any
++    part thereof, to be licensed as a whole at no charge to all third
++    parties under the terms of this License.
++
++    c) If the modified program normally reads commands interactively
++    when run, you must cause it, when started running for such
++    interactive use in the most ordinary way, to print or display an
++    announcement including an appropriate copyright notice and a
++    notice that there is no warranty (or else, saying that you provide
++    a warranty) and that users may redistribute the program under
++    these conditions, and telling the user how to view a copy of this
++    License.  (Exception: if the Program itself is interactive but
++    does not normally print such an announcement, your work based on
++    the Program is not required to print an announcement.)
++\f
++These requirements apply to the modified work as a whole.  If
++identifiable sections of that work are not derived from the Program,
++and can be reasonably considered independent and separate works in
++themselves, then this License, and its terms, do not apply to those
++sections when you distribute them as separate works.  But when you
++distribute the same sections as part of a whole which is a work based
++on the Program, the distribution of the whole must be on the terms of
++this License, whose permissions for other licensees extend to the
++entire whole, and thus to each and every part regardless of who wrote it.
++
++Thus, it is not the intent of this section to claim rights or contest
++your rights to work written entirely by you; rather, the intent is to
++exercise the right to control the distribution of derivative or
++collective works based on the Program.
++
++In addition, mere aggregation of another work not based on the Program
++with the Program (or with a work based on the Program) on a volume of
++a storage or distribution medium does not bring the other work under
++the scope of this License.
++
++  3. You may copy and distribute the Program (or a work based on it,
++under Section 2) in object code or executable form under the terms of
++Sections 1 and 2 above provided that you also do one of the following:
++
++    a) Accompany it with the complete corresponding machine-readable
++    source code, which must be distributed under the terms of Sections
++    1 and 2 above on a medium customarily used for software interchange; or,
++
++    b) Accompany it with a written offer, valid for at least three
++    years, to give any third party, for a charge no more than your
++    cost of physically performing source distribution, a complete
++    machine-readable copy of the corresponding source code, to be
++    distributed under the terms of Sections 1 and 2 above on a medium
++    customarily used for software interchange; or,
++
++    c) Accompany it with the information you received as to the offer
++    to distribute corresponding source code.  (This alternative is
++    allowed only for noncommercial distribution and only if you
++    received the program in object code or executable form with such
++    an offer, in accord with Subsection b above.)
++
++The source code for a work means the preferred form of the work for
++making modifications to it.  For an executable work, complete source
++code means all the source code for all modules it contains, plus any
++associated interface definition files, plus the scripts used to
++control compilation and installation of the executable.  However, as a
++special exception, the source code distributed need not include
++anything that is normally distributed (in either source or binary
++form) with the major components (compiler, kernel, and so on) of the
++operating system on which the executable runs, unless that component
++itself accompanies the executable.
++
++If distribution of executable or object code is made by offering
++access to copy from a designated place, then offering equivalent
++access to copy the source code from the same place counts as
++distribution of the source code, even though third parties are not
++compelled to copy the source along with the object code.
++\f
++  4. You may not copy, modify, sublicense, or distribute the Program
++except as expressly provided under this License.  Any attempt
++otherwise to copy, modify, sublicense or distribute the Program is
++void, and will automatically terminate your rights under this License.
++However, parties who have received copies, or rights, from you under
++this License will not have their licenses terminated so long as such
++parties remain in full compliance.
++
++  5. You are not required to accept this License, since you have not
++signed it.  However, nothing else grants you permission to modify or
++distribute the Program or its derivative works.  These actions are
++prohibited by law if you do not accept this License.  Therefore, by
++modifying or distributing the Program (or any work based on the
++Program), you indicate your acceptance of this License to do so, and
++all its terms and conditions for copying, distributing or modifying
++the Program or works based on it.
++
++  6. Each time you redistribute the Program (or any work based on the
++Program), the recipient automatically receives a license from the
++original licensor to copy, distribute or modify the Program subject to
++these terms and conditions.  You may not impose any further
++restrictions on the recipients' exercise of the rights granted herein.
++You are not responsible for enforcing compliance by third parties to
++this License.
++
++  7. If, as a consequence of a court judgment or allegation of patent
++infringement or for any other reason (not limited to patent issues),
++conditions are imposed on you (whether by court order, agreement or
++otherwise) that contradict the conditions of this License, they do not
++excuse you from the conditions of this License.  If you cannot
++distribute so as to satisfy simultaneously your obligations under this
++License and any other pertinent obligations, then as a consequence you
++may not distribute the Program at all.  For example, if a patent
++license would not permit royalty-free redistribution of the Program by
++all those who receive copies directly or indirectly through you, then
++the only way you could satisfy both it and this License would be to
++refrain entirely from distribution of the Program.
++
++If any portion of this section is held invalid or unenforceable under
++any particular circumstance, the balance of the section is intended to
++apply and the section as a whole is intended to apply in other
++circumstances.
++
++It is not the purpose of this section to induce you to infringe any
++patents or other property right claims or to contest validity of any
++such claims; this section has the sole purpose of protecting the
++integrity of the free software distribution system, which is
++implemented by public license practices.  Many people have made
++generous contributions to the wide range of software distributed
++through that system in reliance on consistent application of that
++system; it is up to the author/donor to decide if he or she is willing
++to distribute software through any other system and a licensee cannot
++impose that choice.
++
++This section is intended to make thoroughly clear what is believed to
++be a consequence of the rest of this License.
++\f
++  8. If the distribution and/or use of the Program is restricted in
++certain countries either by patents or by copyrighted interfaces, the
++original copyright holder who places the Program under this License
++may add an explicit geographical distribution limitation excluding
++those countries, so that distribution is permitted only in or among
++countries not thus excluded.  In such case, this License incorporates
++the limitation as if written in the body of this License.
++
++  9. The Free Software Foundation may publish revised and/or new versions
++of the General Public License from time to time.  Such new versions will
++be similar in spirit to the present version, but may differ in detail to
++address new problems or concerns.
++
++Each version is given a distinguishing version number.  If the Program
++specifies a version number of this License which applies to it and "any
++later version", you have the option of following the terms and conditions
++either of that version or of any later version published by the Free
++Software Foundation.  If the Program does not specify a version number of
++this License, you may choose any version ever published by the Free Software
++Foundation.
++
++  10. If you wish to incorporate parts of the Program into other free
++programs whose distribution conditions are different, write to the author
++to ask for permission.  For software which is copyrighted by the Free
++Software Foundation, write to the Free Software Foundation; we sometimes
++make exceptions for this.  Our decision will be guided by the two goals
++of preserving the free status of all derivatives of our free software and
++of promoting the sharing and reuse of software generally.
++
++                          NO WARRANTY
++
++  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
++REPAIR OR CORRECTION.
++
++  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
++POSSIBILITY OF SUCH DAMAGES.
++
++                   END OF TERMS AND CONDITIONS
+diff --git a/firmware/lantiq/README b/firmware/lantiq/README
+new file mode 100644
+index 0000000..cb1a10a
+--- /dev/null
++++ b/firmware/lantiq/README
+@@ -0,0 +1,45 @@
++#
++# This program is free software; you can redistribute it and/or
++# modify it under the terms of the GNU General Public License as
++# published by the Free Software Foundation; either version 2 of
++# the License, or (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++# MA 02111-1307 USA
++#
++# (C) Copyright 2007 - 2012
++# Lantiq Deutschland GmbH
++#
++# (C) Copyright 2012
++# Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>
++#
++
++#
++# How to use
++#
++Configure kernel with:
++CONFIG_FW_LOADER=y
++CONFIG_EXTRA_FIRMWARE_DIR="FIRMWARE_DIR"
++CONFIG_EXTRA_FIRMWARE="FIRMWARE_FILES"
++
++where FIRMWARE_DIR should point to this git tree and FIRMWARE_FILES is a list
++of space separated files from list below.
++
++#
++# Firmware files
++#
++
++# GPHY core on Lantiq XWAY VR9 v1.1
++lantiq/vr9_phy11g_a1x.bin
++lantiq/vr9_phy22f_a1x.bin
++
++# GPHY core on Lantiq XWAY VR9 v1.1
++lantiq/vr9_phy11g_a2x.bin
++lantiq/vr9_phy22f_a2x.bin
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0029-NET-lantiq-adds-gphy-clock.patch b/target/linux/lantiq/patches-3.8/0029-NET-lantiq-adds-gphy-clock.patch
new file mode 100644 (file)
index 0000000..3b50b17
--- /dev/null
@@ -0,0 +1,24 @@
+From 9dee771f3a7e51411a408cbb1070e73a50cbd285 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 13 Mar 2013 09:47:44 +0100
+Subject: [PATCH 29/40] NET: lantiq: adds gphy clock
+
+---
+ arch/mips/lantiq/xway/sysctrl.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
+index 3390fcd..c24924f 100644
+--- a/arch/mips/lantiq/xway/sysctrl.c
++++ b/arch/mips/lantiq/xway/sysctrl.c
+@@ -376,6 +376,7 @@ void __init ltq_soc_init(void)
+                               PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM |
+                               PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 |
+                               PMU_PPE_QSB | PMU_PPE_TOP);
++              clkdev_add_pmu("1f203000.rcu", "gphy", 0, PMU_GPHY);
+       } else if (of_machine_is_compatible("lantiq,ar9")) {
+               clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(),
+                               ltq_ar9_fpi_hz(), CLOCK_250M);
+-- 
+1.7.10.4
+
diff --git a/target/linux/lantiq/patches-3.8/0030-MIPS-lantiq-add-pcie-driver.patch b/target/linux/lantiq/patches-3.8/0030-MIPS-lantiq-add-pcie-driver.patch
new file mode 100644 (file)
index 0000000..dc5c545
--- /dev/null
@@ -0,0 +1,4786 @@
+From 86b0b37729298b067157263b7bf5dbf735527e7c Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 13 Mar 2013 09:39:02 +0100
+Subject: [PATCH 30/40] MIPS: lantiq: add pcie driver
+
+---
+ arch/mips/lantiq/Kconfig           |   10 +
+ arch/mips/lantiq/xway/sysctrl.c    |    2 +
+ arch/mips/pci/Makefile             |    2 +
+ arch/mips/pci/fixup-lantiq-pcie.c  |   82 ++
+ arch/mips/pci/fixup-lantiq.c       |    3 +
+ arch/mips/pci/ifxmips_pci_common.h |   57 ++
+ arch/mips/pci/ifxmips_pcie.c       | 1607 ++++++++++++++++++++++++++++++++++++
+ arch/mips/pci/ifxmips_pcie.h       |  135 +++
+ arch/mips/pci/ifxmips_pcie_ar10.h  |  290 +++++++
+ arch/mips/pci/ifxmips_pcie_msi.c   |  392 +++++++++
+ arch/mips/pci/ifxmips_pcie_phy.c   |  478 +++++++++++
+ arch/mips/pci/ifxmips_pcie_pm.c    |  176 ++++
+ arch/mips/pci/ifxmips_pcie_pm.h    |   36 +
+ arch/mips/pci/ifxmips_pcie_reg.h   | 1001 ++++++++++++++++++++++
+ arch/mips/pci/ifxmips_pcie_vr9.h   |  271 ++++++
+ arch/mips/pci/pci.c                |   25 +
+ drivers/pci/pcie/aer/Kconfig       |    2 +-
+ include/linux/pci.h                |    2 +
+ include/linux/pci_ids.h            |    6 +
+ 19 files changed, 4576 insertions(+), 1 deletion(-)
+ create mode 100644 arch/mips/pci/fixup-lantiq-pcie.c
+ create mode 100644 arch/mips/pci/ifxmips_pci_common.h
+ create mode 100644 arch/mips/pci/ifxmips_pcie.c
+ create mode 100644 arch/mips/pci/ifxmips_pcie.h
+ create mode 100644 arch/mips/pci/ifxmips_pcie_ar10.h
+ create mode 100644 arch/mips/pci/ifxmips_pcie_msi.c
+ create mode 100644 arch/mips/pci/ifxmips_pcie_phy.c
+ create mode 100644 arch/mips/pci/ifxmips_pcie_pm.c
+ create mode 100644 arch/mips/pci/ifxmips_pcie_pm.h
+ create mode 100644 arch/mips/pci/ifxmips_pcie_reg.h
+ create mode 100644 arch/mips/pci/ifxmips_pcie_vr9.h
+
+diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig
+index 675310a..4c9a241 100644
+--- a/arch/mips/lantiq/Kconfig
++++ b/arch/mips/lantiq/Kconfig
+@@ -18,6 +18,7 @@ config SOC_XWAY
+       bool "XWAY"
+       select SOC_TYPE_XWAY
+       select HW_HAS_PCI
++      select ARCH_SUPPORTS_MSI
+ config SOC_FALCON
+       bool "FALCON"
+@@ -37,6 +38,15 @@ config PCI_LANTIQ
+       bool "PCI Support"
+       depends on SOC_XWAY && PCI
++config PCIE_LANTIQ
++      bool "PCIE Support"
++      depends on SOC_XWAY && PCI
++
++config PCIE_LANTIQ_MSI
++      bool
++      depends on PCIE_LANTIQ && PCI_MSI
++      default y
++
+ config XRX200_PHY_FW
+       bool "XRX200 PHY firmware loader"
+       depends on SOC_XWAY
+diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
+index c24924f..e30dde8 100644
+--- a/arch/mips/lantiq/xway/sysctrl.c
++++ b/arch/mips/lantiq/xway/sysctrl.c
+@@ -377,6 +377,8 @@ void __init ltq_soc_init(void)
+                               PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 |
+                               PMU_PPE_QSB | PMU_PPE_TOP);
+               clkdev_add_pmu("1f203000.rcu", "gphy", 0, PMU_GPHY);
++              pmu_w32(~0, PMU_PWDSR1);
++              pmu_w32(pmu_r32(PMU_PWDSR) & ~PMU_PCIE_CLK, PMU_PWDSR);
+       } else if (of_machine_is_compatible("lantiq,ar9")) {
+               clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(),
+                               ltq_ar9_fpi_hz(), CLOCK_250M);
+diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
+index ce995d3..bd32fe1 100644
+--- a/arch/mips/pci/Makefile
++++ b/arch/mips/pci/Makefile
+@@ -42,6 +42,8 @@ obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o
+ obj-$(CONFIG_SNI_RM)          += fixup-sni.o ops-sni.o
+ obj-$(CONFIG_LANTIQ)          += fixup-lantiq.o
+ obj-$(CONFIG_PCI_LANTIQ)      += pci-lantiq.o ops-lantiq.o
++obj-$(CONFIG_PCIE_LANTIQ)     += ifxmips_pcie_phy.o ifxmips_pcie.o fixup-lantiq-pcie.o
++obj-$(CONFIG_PCIE_LANTIQ_MSI) += pcie-lantiq-msi.o
+ obj-$(CONFIG_TANBAC_TB0219)   += fixup-tb0219.o
+ obj-$(CONFIG_TANBAC_TB0226)   += fixup-tb0226.o
+ obj-$(CONFIG_TANBAC_TB0287)   += fixup-tb0287.o
+diff --git a/arch/mips/pci/fixup-lantiq-pcie.c b/arch/mips/pci/fixup-lantiq-pcie.c
+new file mode 100644
+index 0000000..50a1c3b
+--- /dev/null
++++ b/arch/mips/pci/fixup-lantiq-pcie.c
+@@ -0,0 +1,82 @@
++/******************************************************************************
++**
++** FILE NAME    : ifxmips_fixup_pcie.c
++** PROJECT      : IFX UEIP for VRX200
++** MODULES      : PCIe 
++**
++** DATE         : 02 Mar 2009
++** AUTHOR       : Lei Chuanhua
++** DESCRIPTION  : PCIe Root Complex Driver
++** COPYRIGHT    :       Copyright (c) 2009
++**                      Infineon Technologies AG
++**                      Am Campeon 1-12, 85579 Neubiberg, Germany
++**
++**    This program is free software; you can redistribute it and/or modify
++**    it under the terms of the GNU General Public License as published by
++**    the Free Software Foundation; either version 2 of the License, or
++**    (at your option) any later version.
++** HISTORY
++** $Version $Date        $Author         $Comment
++** 0.0.1    17 Mar,2009  Lei Chuanhua    Initial version
++*******************************************************************************/
++/*!
++ \file ifxmips_fixup_pcie.c
++ \ingroup IFX_PCIE  
++ \brief PCIe Fixup functions source file
++*/
++#include <linux/pci.h>
++#include <linux/pci_regs.h>
++#include <linux/pci_ids.h>
++
++#include <lantiq_soc.h>
++
++#include "pcie-lantiq.h"
++
++#define PCI_VENDOR_ID_INFINEON         0x15D1
++#define PCI_DEVICE_ID_INFINEON_DANUBE  0x000F
++#define PCI_DEVICE_ID_INFINEON_PCIE    0x0011
++#define PCI_VENDOR_ID_LANTIQ        0x1BEF
++#define PCI_DEVICE_ID_LANTIQ_PCIE       0x0011
++
++
++
++static void __devinit
++ifx_pcie_fixup_resource(struct pci_dev *dev)
++{
++    u32 reg;
++
++    IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev));
++
++    printk("%s: fixup host controller %s (%04x:%04x)\n", 
++        __func__, pci_name(dev), dev->vendor, dev->device); 
++
++   /* Setup COMMAND register */
++    reg = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER /* | 
++          PCI_COMMAND_INTX_DISABLE */| PCI_COMMAND_SERR;
++    pci_write_config_word(dev, PCI_COMMAND, reg);
++    IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev));
++}
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE, ifx_pcie_fixup_resource);
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_VENDOR_ID_LANTIQ, ifx_pcie_fixup_resource);
++
++static void __devinit
++ifx_pcie_rc_class_early_fixup(struct pci_dev *dev)
++{
++    IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev));
++
++    if (dev->devfn == PCI_DEVFN(0, 0) &&
++        (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) {
++
++        dev->class = (PCI_CLASS_BRIDGE_PCI << 8) | (dev->class & 0xff);
++
++        printk(KERN_INFO "%s: fixed pcie host bridge to pci-pci bridge\n", __func__);
++    }
++    IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev));
++    mdelay(10);
++}
++
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE,
++     ifx_pcie_rc_class_early_fixup);
++
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_DEVICE_ID_LANTIQ_PCIE,
++     ifx_pcie_rc_class_early_fixup);
+diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c
+index 6c829df..cf5c4e0 100644
+--- a/arch/mips/pci/fixup-lantiq.c
++++ b/arch/mips/pci/fixup-lantiq.c
+@@ -11,6 +11,7 @@
+ int (*ltq_pci_plat_arch_init)(struct pci_dev *dev) = NULL;
+ int (*ltq_pci_plat_dev_init)(struct pci_dev *dev) = NULL;
++int (*ltq_pci_map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
+ int pcibios_plat_dev_init(struct pci_dev *dev)
+ {
+@@ -28,6 +29,8 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+       struct of_irq dev_irq;
+       int irq;
++      if (ltq_pci_map_irq)
++              return ltq_pci_map_irq(dev, slot, pin);
+       if (of_irq_map_pci(dev, &dev_irq)) {
+               dev_err(&dev->dev, "trying to map irq for unknown slot:%d pin:%d\n",
+                       slot, pin);
+diff --git a/arch/mips/pci/ifxmips_pci_common.h b/arch/mips/pci/ifxmips_pci_common.h
+new file mode 100644
+index 0000000..46f4cb2
+--- /dev/null
++++ b/arch/mips/pci/ifxmips_pci_common.h
+@@ -0,0 +1,57 @@
++/******************************************************************************
++**
++** FILE NAME    : ifxmips_pci_common.h
++** PROJECT      : IFX UEIP
++** MODULES      : PCI subsystem
++**
++** DATE         : 30 June 2009
++** AUTHOR       : Lei Chuanhua
++** DESCRIPTION  : PCIe Root Complex Driver
++** COPYRIGHT    :       Copyright (c) 2009
++**                      Infineon Technologies AG
++**                      Am Campeon 1-12, 85579 Neubiberg, Germany
++**
++**    This program is free software; you can redistribute it and/or modify
++**    it under the terms of the GNU General Public License as published by
++**    the Free Software Foundation; either version 2 of the License, or
++**    (at your option) any later version.
++** HISTORY
++** $Version $Date        $Author         $Comment
++** 0.0.1    30 June,2009  Lei Chuanhua    Initial version
++*******************************************************************************/
++
++#ifndef IFXMIPS_PCI_COMMON_H
++#define IFXMIPS_PCI_COMMON_H
++#include <linux/version.h>
++/*!
++ \defgroup IFX_PCI_COM  IFX PCI/PCIe common parts for OS integration  
++ \brief  PCI/PCIe common parts
++*/
++
++/*!
++ \defgroup IFX_PCI_COM_OS OS APIs
++ \ingroup IFX_PCI_COM
++ \brief PCI/PCIe bus driver OS interface functions
++*/
++/*!
++  \file ifxmips_pci_common.h
++  \ingroup IFX_PCI_COM
++  \brief PCI/PCIe bus driver common OS header file
++*/
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
++#define IFX_PCI_CONST
++#else
++#define IFX_PCI_CONST const
++#endif
++#ifdef CONFIG_IFX_PCI
++extern int ifx_pci_bios_map_irq(IFX_PCI_CONST struct pci_dev *dev, u8 slot, u8 pin);
++extern int ifx_pci_bios_plat_dev_init(struct pci_dev *dev);
++#endif /* COFNIG_IFX_PCI */
++
++#ifdef CONFIG_IFX_PCIE
++extern int ifx_pcie_bios_map_irq(IFX_PCI_CONST struct pci_dev *dev, u8 slot, u8 pin);
++extern int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev);
++#endif
++
++#endif /* IFXMIPS_PCI_COMMON_H */
++
+diff --git a/arch/mips/pci/ifxmips_pcie.c b/arch/mips/pci/ifxmips_pcie.c
+new file mode 100644
+index 0000000..5cebfe6
+--- /dev/null
++++ b/arch/mips/pci/ifxmips_pcie.c
+@@ -0,0 +1,1607 @@
++/******************************************************************************
++**
++** FILE NAME    : ifxmips_pcie.c
++** PROJECT      : IFX UEIP for VRX200
++** MODULES      : PCI MSI sub module
++**
++** DATE         : 02 Mar 2009
++** AUTHOR       : Lei Chuanhua
++** DESCRIPTION  : PCIe Root Complex Driver
++** COPYRIGHT    :       Copyright (c) 2009
++**                      Infineon Technologies AG
++**                      Am Campeon 1-12, 85579 Neubiberg, Germany
++**
++**    This program is free software; you can redistribute it and/or modify
++**    it under the terms of the GNU General Public License as published by
++**    the Free Software Foundation; either version 2 of the License, or
++**    (at your option) any later version.
++** HISTORY
++** $Version $Date        $Author         $Comment
++** 0.0.1    02 Mar,2009  Lei Chuanhua    Initial version
++*******************************************************************************/
++ /*!
++  \file ifxmips_pcie.c
++  \ingroup IFX_PCIE
++  \brief PCI express bus driver source file
++*/
++#include <linux/types.h>
++#include <linux/pci.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/mm.h>
++#include <asm/paccess.h>
++#include <linux/pci.h>
++#include <linux/pci_regs.h>
++#include <linux/module.h>
++
++#include "ifxmips_pcie.h"
++#include "ifxmips_pcie_reg.h"
++
++#define IFX_PCIE_VER_MAJOR          1
++#define IFX_PCIE_VER_MID            5
++#define IFX_PCIE_VER_MINOR          3
++
++/* Enable 32bit io due to its mem mapped io nature */
++#define IFX_PCIE_ERROR_INT
++#define CONFIG_IFX_PCIE_1ST_CORE
++#define IFX_PCIE_IO_32BIT
++
++#define IFX_PCIE_IR                     (INT_NUM_IM4_IRL0 + 25)
++#define IFX_PCIE_INTA                   (INT_NUM_IM4_IRL0 + 8)
++#define IFX_PCIE_INTB                   (INT_NUM_IM4_IRL0 + 9)
++#define IFX_PCIE_INTC                   (INT_NUM_IM4_IRL0 + 10)
++#define IFX_PCIE_INTD                   (INT_NUM_IM4_IRL0 + 11)
++#define MS(_v, _f)  (((_v) & (_f)) >> _f##_S)
++#define SM(_v, _f)  (((_v) << _f##_S) & (_f))
++#define IFX_REG_SET_BIT(_f, _r) \
++      IFX_REG_W32((IFX_REG_R32((_r)) &~ (_f)) | (_f), (_r))
++
++static DEFINE_SPINLOCK(ifx_pcie_lock);
++
++u32 g_pcie_debug_flag = PCIE_MSG_ANY & (~PCIE_MSG_CFG);
++
++static ifx_pcie_irq_t pcie_irqs[IFX_PCIE_CORE_NR] = {
++    {
++        .ir_irq = {
++            .irq  = IFX_PCIE_IR,
++            .name = "ifx_pcie_rc0",
++        },
++
++        .legacy_irq = {
++            {
++                .irq_bit = PCIE_IRN_INTA,
++                .irq     = IFX_PCIE_INTA,
++            },
++            {
++                .irq_bit = PCIE_IRN_INTB,
++                .irq     = IFX_PCIE_INTB,
++            },
++            {
++                .irq_bit = PCIE_IRN_INTC,
++                .irq     = IFX_PCIE_INTC,
++            },
++            {
++                .irq_bit = PCIE_IRN_INTD,
++                .irq     = IFX_PCIE_INTD,
++            },
++        },
++    },
++
++#ifdef CONFIG_IFX_PCIE_2ND_CORE
++    {
++        .ir_irq = {
++            .irq  = IFX_PCIE1_IR,
++            .name = "ifx_pcie_rc1",
++        },
++
++        .legacy_irq = {
++            {
++                .irq_bit = PCIE_IRN_INTA,
++                .irq     = IFX_PCIE1_INTA,
++            },
++            {
++                .irq_bit = PCIE_IRN_INTB,
++                .irq     = IFX_PCIE1_INTB,
++            },
++            {
++                .irq_bit = PCIE_IRN_INTC,
++                .irq     = IFX_PCIE1_INTC,
++            },
++            {
++                .irq_bit = PCIE_IRN_INTD,
++                .irq     = IFX_PCIE1_INTD,
++            },
++        },
++
++    },
++#endif /* CONFIG_IFX_PCIE_2ND_CORE */
++};
++
++void 
++ifx_pcie_debug(const char *fmt, ...)
++{
++    static char buf[256] = {0};      /* XXX */
++    va_list ap;
++
++    va_start(ap, fmt);
++    vsnprintf(buf, sizeof(buf), fmt, ap);
++    va_end(ap); 
++
++    printk("%s", buf);
++}
++
++#ifdef IFX_PCI_PHY_DBG
++/* Generate hot reset, XXX must catpure to verify */
++static INLINE void 
++pcie_secondary_bus_reset(int pcie_port)
++{
++    int i;
++    u32 reg;
++#define IFX_PCIE_RESET_TIME   20
++
++    /* Assert Secondary Bus Reset */
++    reg = IFX_REG_R32(PCIE_INTRBCTRL(pcie_port));
++    reg |= PCIE_INTRBCTRL_RST_SECONDARY_BUS;
++    IFX_REG_W32(reg, PCIE_INTRBCTRL(pcie_port));
++
++    /* De-assert Secondary Bus Reset */
++    reg &= ~PCIE_INTRBCTRL_RST_SECONDARY_BUS;
++    IFX_REG_W32(reg, PCIE_INTRBCTRL(pcie_port));
++
++    /* XXX, wait at least 100 ms, then restore again */
++    for (i = 0; i < IFX_PCIE_RESET_TIME; i++) {
++        mdelay(10);
++    }
++#undef IFX_PCIE_RESET_TIME
++}
++
++/* Error or L0s to L0 */
++static INLINE int 
++pcie_retrain_link(int pcie_port)
++{
++    int i;
++    u32 reg;
++#define IFX_PCIE_RETRAIN_TIME  1000
++
++    reg = IFX_REG_R32(PCIE_LCTLSTS(pcie_port));
++    reg |= PCIE_LCTLSTS_RETRIAN_LINK;
++    IFX_REG_W32(reg, PCIE_LCTLSTS(pcie_port));
++
++    /* Wait for the link to come up */
++    for (i = 0; i < IFX_PCIE_RETRAIN_TIME; i++) {
++        if (!(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_RETRAIN_PENDING)) {
++            break;
++        }
++        udelay(100);
++    }
++    if (i >= IFX_PCIE_RETRAIN_TIME) {
++        IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s retrain timeout\n", __func__);
++        return -1;
++    }
++    return 0;
++#undef IFX_PCIE_RETRAIN_TIME
++}
++
++static INLINE void 
++pcie_disable_scrambling(int pcie_port)
++{
++    u32 reg;
++
++    reg = IFX_REG_R32(PCIE_PLCR(pcie_port));
++    reg |= PCIE_PLCR_SCRAMBLE_DISABLE;
++    IFX_REG_W32(reg, PCIE_PLCR(pcie_port));
++}
++#endif /* IFX_PCI_PHY_DBG */
++
++static INLINE int 
++pcie_ltssm_enable(int pcie_port) 
++{
++    int i;
++#define IFX_PCIE_LTSSM_ENABLE_TIMEOUT 10
++
++    IFX_REG_W32(PCIE_RC_CCR_LTSSM_ENABLE, PCIE_RC_CCR(pcie_port)); /* Enable LTSSM */
++
++    /* Wait for the link to come up */
++    for (i = 0; i < IFX_PCIE_LTSSM_ENABLE_TIMEOUT; i++) {
++        if (!(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_RETRAIN_PENDING)) {
++            break;
++        }
++        udelay(10);
++    }
++    if (i >= IFX_PCIE_LTSSM_ENABLE_TIMEOUT) {
++        IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s link timeout!!!!!\n", __func__);
++        return -1;
++    }
++    return 0;
++#undef IFX_PCIE_LTSSM_ENABLE_TIMEOUT
++}
++
++static INLINE void 
++pcie_ltssm_disable(int pcie_port) 
++{
++    IFX_REG_W32(0, PCIE_RC_CCR(pcie_port)); /* Disable LTSSM */
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_RC_CCR 0x%08x\n", 
++        __func__, IFX_REG_R32(PCIE_RC_CCR(pcie_port)));
++}
++
++static INLINE void
++pcie_ahb_bus_error_suppress(int pcie_port)
++{
++    IFX_REG_W32(PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS, PCIE_AHB_CTRL(pcie_port));
++}
++
++static INLINE void 
++pcie_status_register_clear(int pcie_port)
++{
++   /* Clear the status register, XXX, seperate function */
++    IFX_REG_W32(0, PCIE_RC_DR(pcie_port));
++    IFX_REG_W32(0, PCIE_PCICMDSTS(pcie_port));
++    IFX_REG_W32(0, PCIE_DCTLSTS(pcie_port));
++    IFX_REG_W32(0, PCIE_LCTLSTS(pcie_port));
++    IFX_REG_W32(0, PCIE_SLCTLSTS(pcie_port));
++    IFX_REG_W32(0, PCIE_RSTS(pcie_port));
++    IFX_REG_W32(0, PCIE_UES_R(pcie_port));
++    IFX_REG_W32(0, PCIE_UEMR(pcie_port));
++    IFX_REG_W32(0, PCIE_UESR(pcie_port));
++    IFX_REG_W32(0, PCIE_CESR(pcie_port));
++    IFX_REG_W32(0, PCIE_CEMR(pcie_port));
++    IFX_REG_W32(0, PCIE_RESR(pcie_port));
++    IFX_REG_W32(0, PCIE_PVCCRSR(pcie_port));
++    IFX_REG_W32(0, PCIE_VC0_RSR0(pcie_port));
++    IFX_REG_W32(0, PCIE_TPFCS(pcie_port));
++    IFX_REG_W32(0, PCIE_TNPFCS(pcie_port));
++    IFX_REG_W32(0, PCIE_TCFCS(pcie_port));
++    IFX_REG_W32(0, PCIE_QSR(pcie_port));
++    IFX_REG_W32(0, PCIE_IOBLSECS(pcie_port));
++}
++
++static inline int 
++ifx_pcie_link_up(int pcie_port)
++{
++    return (IFX_REG_R32(PCIE_PHY_SR(pcie_port)) & PCIE_PHY_SR_PHY_LINK_UP) ? 1 : 0;
++}
++
++#ifdef IFX_PCIE_DBG
++static void 
++pcie_status_registers_dump(int pcie_port)
++{
++    printk(KERN_INFO "PCIe_PCICMDSTS: 0x%08x\n", IFX_REG_R32(PCIE_PCICMDSTS(pcie_port)));
++    printk(KERN_INFO "PCIe_RC_DR:     0x%08x\n", IFX_REG_R32(PCIE_RC_DR(pcie_port)));
++    printk(KERN_INFO "PCIe_DCTLSTS:   0x%08x\n", IFX_REG_R32(PCIE_DCTLSTS(pcie_port)));
++    printk(KERN_INFO "PCIe_LCTLSTS:   0x%08x\n", IFX_REG_R32(PCIE_LCTLSTS(pcie_port)));
++    printk(KERN_INFO "PCIe_SLCTLSTS:  0x%08x\n", IFX_REG_R32(PCIE_SLCTLSTS(pcie_port)));
++    printk(KERN_INFO "PCIe_RSTS:      0x%08x\n", IFX_REG_R32(PCIE_RSTS(pcie_port)));
++    printk(KERN_INFO "PCIe_UES_R:     0x%08x\n", IFX_REG_R32(PCIE_UES_R(pcie_port)));
++    printk(KERN_INFO "PCIe_UEMR:      0x%08x\n", IFX_REG_R32(PCIE_UEMR(pcie_port)));
++    printk(KERN_INFO "PCIe_UESR:      0x%08x\n", IFX_REG_R32(PCIE_UESR(pcie_port)));
++    printk(KERN_INFO "PCIe_CESR:      0x%08x\n", IFX_REG_R32(PCIE_CESR(pcie_port)));
++    printk(KERN_INFO "PCIe_CEMR:      0x%08x\n", IFX_REG_R32(PCIE_CEMR(pcie_port)));
++    printk(KERN_INFO "PCIe_RESR:      0x%08x\n", IFX_REG_R32(PCIE_RESR(pcie_port)));
++    printk(KERN_INFO "PCIe_ESIR:      0x%08x\n", IFX_REG_R32(PCIE_ESIR(pcie_port)));
++    printk(KERN_INFO "PCIe_PVCCRSR:   0x%08x\n", IFX_REG_R32(PCIE_PVCCRSR(pcie_port)));
++    printk(KERN_INFO "PCIe_VC0_RSR0:  0x%08x\n", IFX_REG_R32(PCIE_VC0_RSR0(pcie_port)));
++    printk(KERN_INFO "PCIe_TPFCS:     0x%08x\n", IFX_REG_R32(PCIE_TPFCS(pcie_port)));
++    printk(KERN_INFO "PCIe_TNPFCS:    0x%08x\n", IFX_REG_R32(PCIE_TNPFCS(pcie_port)));
++    printk(KERN_INFO "PCIe_TCFCS:     0x%08x\n", IFX_REG_R32(PCIE_TCFCS(pcie_port)));
++    printk(KERN_INFO "PCIe_QSR:       0x%08x\n", IFX_REG_R32(PCIE_QSR(pcie_port)));
++    printk(KERN_INFO "PCIe_VCTAR1:    0x%08x\n", IFX_REG_R32(PCIE_VCTAR1(pcie_port)));
++    printk(KERN_INFO "PCIe_VCTAR2:    0x%08x\n", IFX_REG_R32(PCIE_VCTAR2(pcie_port)));
++    printk(KERN_INFO "PCIe_IOBLSECS:  0x%08x\n", IFX_REG_R32(PCIE_IOBLSECS(pcie_port)));
++    printk(KERN_INFO "PCIe_ALTRT:     0x%08x\n", IFX_REG_R32(PCIE_ALTRT(pcie_port)));
++    printk(KERN_INFO "PCIe_SNR:       0x%08x\n", IFX_REG_R32(PCIE_SNR(pcie_port)));
++    printk(KERN_INFO "PCIe_DBR0:      0x%08x\n", IFX_REG_R32(PCIE_DBR0(pcie_port)));
++    printk(KERN_INFO "PCIe_DBR1:      0x%08x\n", IFX_REG_R32(PCIE_DBR1(pcie_port)));
++}
++
++static void 
++pcie_post_dump(int pcie_port)
++{
++    printk(KERN_INFO "PCIe_PCICMDSTS:  0x%08x\n", IFX_REG_R32(PCIE_PCICMDSTS(pcie_port)));
++    printk(KERN_INFO "PCIe_MBML:       0x%08x\n", IFX_REG_R32(PCIE_MBML(pcie_port)));
++    printk(KERN_INFO "PCIe_PBML:       0x%08x\n", IFX_REG_R32(PCIE_PMBL(pcie_port)));
++    printk(KERN_INFO "PCIe_IOBLSECS:   0x%08x\n", IFX_REG_R32(PCIE_IOBLSECS(pcie_port)));
++    printk(KERN_INFO "PCIe_IO_BANDL:   0x%08x\n", IFX_REG_R32(PCIE_IO_BANDL(pcie_port)));
++    printk(KERN_INFO "PCIe_INTRBCTRL:  0x%08x\n", IFX_REG_R32(PCIE_INTRBCTRL(pcie_port)));
++    printk(KERN_INFO "Power State:     D%1d\n", IFX_REG_R32(PCIE_PM_CSR(pcie_port)) & PCIE_PM_CSR_POWER_STATE);
++    printk(KERN_INFO "Negotiated Link Width: %d\n", MS(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)), PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH));
++    printk(KERN_INFO "Number of VCs:    %d\n", IFX_REG_R32(PCIE_PVC1(pcie_port)) & PCIE_PVC1_EXT_VC_CNT);
++    printk(KERN_INFO "Low-priority VCs: %d\n", MS(IFX_REG_R32(PCIE_PVC1(pcie_port)), PCIE_PVC1_LOW_PRI_EXT_VC_CNT));
++    printk(KERN_INFO "VC Arbitration:   0x%08x\n", IFX_REG_R32(PCIE_PVC2(pcie_port)) & PCIE_PVC2_VC_ARB_WRR);
++    printk(KERN_INFO "Port Arbitration: 0x%08x\n", IFX_REG_R32(PCIE_VC0_RC(pcie_port)) & PCIE_VC0_RC_PORT_ARB);
++
++    if (ifx_pcie_link_up(pcie_port)) {
++        printk(KERN_INFO "PCIe PHY Link is UP\n");
++    }
++    else {
++        printk(KERN_INFO "PCIe PHY Link is DOWN!\n");
++    }
++    if ((IFX_REG_R32(PCIE_RC_DR(pcie_port)) & PCIE_RC_DR_DLL_UP)) {
++        printk(KERN_INFO "PCIe DLL is UP\n");
++    }
++    else {
++        printk(KERN_INFO "PCIe DLL is DOWN!\n");
++    }
++
++    if ((IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_DLL_ACTIVE)) {
++        printk(KERN_INFO "PCIE_LCTLSTS in DL_Active state!\n");
++    }
++    else {
++        printk(KERN_INFO "PCIE_LCTLSTS NOT in DL_Active state!\n");
++    }
++ }
++#endif /* IFX_PCIE_DBG */
++
++/* XXX, this function is not needed in fact */
++static INLINE void
++pcie_mem_io_setup(int pcie_port)
++{
++    u32 reg;
++    /*
++     * BAR[0:1] readonly register 
++     * RC contains only minimal BARs for packets mapped to this device 
++     * Mem/IO filters defines a range of memory occupied by memory mapped IO devices that
++     * reside on the downstream side fo the bridge.
++     */
++    reg = SM((PCIE_MEM_PHY_PORT_TO_END(pcie_port) >> 20), PCIE_MBML_MEM_LIMIT_ADDR)
++        | SM((PCIE_MEM_PHY_PORT_TO_BASE(pcie_port) >> 20), PCIE_MBML_MEM_BASE_ADDR);
++
++    IFX_REG_W32(reg, PCIE_MBML(pcie_port));
++
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_MBML: 0x%08x\n", 
++        __func__, IFX_REG_R32(PCIE_MBML(pcie_port)));
++
++#ifdef IFX_PCIE_PREFETCH_MEM_64BIT
++    reg = SM((PCIE_MEM_PHY_PORT_TO_END(pcie_port) >> 20), PCIE_PMBL_END_ADDR)
++        | SM((PCIE_MEM_PHY_PORT_TO_BASE(pcie_port) >> 20), PCIE_PMBL_UPPER_12BIT)
++        | PCIE_PMBL_64BIT_ADDR;
++    IFX_REG_W32(reg, PCIE_PMBL(pcie_port));
++
++    /* Must configure upper 32bit */
++    IFX_REG_W32(0, PCIE_PMBU32(pcie_port));
++    IFX_REG_W32(0, PCIE_PMLU32(pcie_port));
++#else
++    /* PCIe_PBML, same as MBML */
++    IFX_REG_W32(IFX_REG_R32(PCIE_MBML(pcie_port)), PCIE_PMBL(pcie_port));
++#endif 
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_PMBL: 0x%08x\n",
++    __func__, IFX_REG_R32(PCIE_PMBL(pcie_port)));
++
++    /* IO Address Range */
++    reg = SM((PCIE_IO_PHY_PORT_TO_END(pcie_port) >> 12), PCIE_IOBLSECS_IO_LIMIT_ADDR)
++        | SM((PCIE_IO_PHY_PORT_TO_BASE(pcie_port) >> 12), PCIE_IOBLSECS_IO_BASE_ADDR);
++#ifdef IFX_PCIE_IO_32BIT    
++    reg |= PCIE_IOBLSECS_32BIT_IO_ADDR;
++#endif /* IFX_PCIE_IO_32BIT */
++    IFX_REG_W32(reg, PCIE_IOBLSECS(pcie_port));
++
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_IOBLSECS: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_IOBLSECS(pcie_port)));
++#ifdef IFX_PCIE_IO_32BIT
++    reg = SM((PCIE_IO_PHY_PORT_TO_END(pcie_port) >> 16), PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT)
++        | SM((PCIE_IO_PHY_PORT_TO_BASE(pcie_port) >> 16), PCIE_IO_BANDL_UPPER_16BIT_IO_BASE);
++    IFX_REG_W32(reg, PCIE_IO_BANDL(pcie_port));
++
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_IO_BANDL: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_IO_BANDL(pcie_port)));
++#endif /* IFX_PCIE_IO_32BIT */
++}
++
++static INLINE void
++pcie_msi_setup(int pcie_port)
++{
++    u32 reg;
++
++    /* XXX, MSI stuff should only apply to EP */
++    /* MSI Capability: Only enable 32-bit addresses */
++    reg = IFX_REG_R32(PCIE_MCAPR(pcie_port));
++    reg &= ~PCIE_MCAPR_ADDR64_CAP;
++
++    reg |= PCIE_MCAPR_MSI_ENABLE;
++
++    /* Disable multiple message */
++    reg &= ~(PCIE_MCAPR_MULTI_MSG_CAP | PCIE_MCAPR_MULTI_MSG_ENABLE);
++    IFX_REG_W32(reg, PCIE_MCAPR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_MCAPR: 0x%08x\n", 
++        __func__, IFX_REG_R32(PCIE_MCAPR(pcie_port)));
++}
++
++static INLINE void
++pcie_pm_setup(int pcie_port)
++{
++    u32 reg;
++
++    /* Enable PME, Soft reset enabled */
++    reg = IFX_REG_R32(PCIE_PM_CSR(pcie_port));
++    reg |= PCIE_PM_CSR_PME_ENABLE | PCIE_PM_CSR_SW_RST;
++    IFX_REG_W32(reg, PCIE_PM_CSR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_PM_CSR: 0x%08x\n", 
++        __func__, IFX_REG_R32(PCIE_PM_CSR(pcie_port)));
++}
++
++static INLINE void
++pcie_bus_setup(int pcie_port) 
++{
++    u32 reg;
++
++    reg = SM(0, PCIE_BNR_PRIMARY_BUS_NUM) | SM(1, PCIE_PNR_SECONDARY_BUS_NUM) | SM(0xFF, PCIE_PNR_SUB_BUS_NUM);
++    IFX_REG_W32(reg, PCIE_BNR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_BNR: 0x%08x\n", 
++        __func__, IFX_REG_R32(PCIE_BNR(pcie_port)));
++}
++
++static INLINE void
++pcie_device_setup(int pcie_port)
++{
++    u32 reg;
++
++    /* Device capability register, set up Maximum payload size */
++    reg = IFX_REG_R32(PCIE_DCAP(pcie_port));
++    reg |= PCIE_DCAP_ROLE_BASE_ERR_REPORT;
++    reg |= SM(PCIE_MAX_PAYLOAD_128, PCIE_DCAP_MAX_PAYLOAD_SIZE);
++
++    /* Only available for EP */
++    reg &= ~(PCIE_DCAP_EP_L0S_LATENCY | PCIE_DCAP_EP_L1_LATENCY);
++    IFX_REG_W32(reg, PCIE_DCAP(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_DCAP: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_DCAP(pcie_port)));
++
++    /* Device control and status register */
++    /* Set Maximum Read Request size for the device as a Requestor */
++    reg = IFX_REG_R32(PCIE_DCTLSTS(pcie_port));
++
++    /* 
++     * Request size can be larger than the MPS used, but the completions returned 
++     * for the read will be bounded by the MPS size.
++     * In our system, Max request size depends on AHB burst size. It is 64 bytes.
++     * but we set it as 128 as minimum one.
++     */
++    reg |= SM(PCIE_MAX_PAYLOAD_128, PCIE_DCTLSTS_MAX_READ_SIZE)
++            | SM(PCIE_MAX_PAYLOAD_128, PCIE_DCTLSTS_MAX_PAYLOAD_SIZE);
++
++    /* Enable relaxed ordering, no snoop, and all kinds of errors */
++    reg |= PCIE_DCTLSTS_RELAXED_ORDERING_EN | PCIE_DCTLSTS_ERR_EN | PCIE_DCTLSTS_NO_SNOOP_EN;
++
++    IFX_REG_W32(reg, PCIE_DCTLSTS(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_DCTLSTS: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_DCTLSTS(pcie_port)));
++}
++
++static INLINE void
++pcie_link_setup(int pcie_port)
++{
++    u32 reg;
++
++    /*
++     * XXX, Link capability register, bit 18 for EP CLKREQ# dynamic clock management for L1, L2/3 CPM 
++     * L0s is reported during link training via TS1 order set by N_FTS
++     */
++    reg = IFX_REG_R32(PCIE_LCAP(pcie_port));
++    reg &= ~PCIE_LCAP_L0S_EIXT_LATENCY;
++    reg |= SM(3, PCIE_LCAP_L0S_EIXT_LATENCY);
++    IFX_REG_W32(reg, PCIE_LCAP(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_LCAP: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_LCAP(pcie_port)));
++
++    /* Link control and status register */
++    reg = IFX_REG_R32(PCIE_LCTLSTS(pcie_port));
++
++    /* Link Enable, ASPM enabled  */
++    reg &= ~PCIE_LCTLSTS_LINK_DISABLE;
++
++#ifdef CONFIG_PCIEASPM
++    /*  
++     * We use the same physical reference clock that the platform provides on the connector 
++     * It paved the way for ASPM to calculate the new exit Latency
++     */
++    reg |= PCIE_LCTLSTS_SLOT_CLK_CFG;
++    reg |= PCIE_LCTLSTS_COM_CLK_CFG;
++    /*
++     * We should disable ASPM by default except that we have dedicated power management support
++     * Enable ASPM will cause the system hangup/instability, performance degration
++     */
++    reg |= PCIE_LCTLSTS_ASPM_ENABLE;
++#else
++    reg &= ~PCIE_LCTLSTS_ASPM_ENABLE;
++#endif /* CONFIG_PCIEASPM */
++
++    /* 
++     * The maximum size of any completion with data packet is bounded by the MPS setting 
++     * in  device control register 
++     */
++
++    /* RCB may cause multiple split transactions, two options available, we use 64 byte RCB */
++    reg &= ~ PCIE_LCTLSTS_RCB128;
++
++    IFX_REG_W32(reg, PCIE_LCTLSTS(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_LCTLSTS: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_LCTLSTS(pcie_port)));
++}
++
++static INLINE void
++pcie_error_setup(int pcie_port)
++{
++    u32 reg;
++
++    /* 
++     * Forward ERR_COR, ERR_NONFATAL, ERR_FATAL to the backbone 
++     * Poisoned write TLPs and completions indicating poisoned TLPs will set the PCIe_PCICMDSTS.MDPE 
++     */
++    reg = IFX_REG_R32(PCIE_INTRBCTRL(pcie_port));
++    reg |= PCIE_INTRBCTRL_SERR_ENABLE | PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE;
++
++    IFX_REG_W32(reg, PCIE_INTRBCTRL(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_INTRBCTRL: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_INTRBCTRL(pcie_port)));
++
++    /* Uncorrectable Error Mask Register, Unmask <enable> all bits in PCIE_UESR */
++    reg = IFX_REG_R32(PCIE_UEMR(pcie_port));
++    reg &= ~PCIE_ALL_UNCORRECTABLE_ERR;
++    IFX_REG_W32(reg, PCIE_UEMR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_UEMR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_UEMR(pcie_port)));
++
++    /* Uncorrectable Error Severity Register, ALL errors are FATAL */
++    IFX_REG_W32(PCIE_ALL_UNCORRECTABLE_ERR, PCIE_UESR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_UESR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_UESR(pcie_port)));
++
++    /* Correctable Error Mask Register, unmask <enable> all bits */
++    reg = IFX_REG_R32(PCIE_CEMR(pcie_port));
++    reg &= ~PCIE_CORRECTABLE_ERR;
++    IFX_REG_W32(reg, PCIE_CEMR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_CEMR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_CEMR(pcie_port)));
++
++    /* Advanced Error Capabilities and Control Registr */
++    reg = IFX_REG_R32(PCIE_AECCR(pcie_port));
++    reg |= PCIE_AECCR_ECRC_CHECK_EN | PCIE_AECCR_ECRC_GEN_EN;
++    IFX_REG_W32(reg, PCIE_AECCR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_AECCR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_AECCR(pcie_port)));
++
++    /* Root Error Command Register, Report all types of errors */
++    reg = IFX_REG_R32(PCIE_RECR(pcie_port));
++    reg |= PCIE_RECR_ERR_REPORT_EN;
++    IFX_REG_W32(reg, PCIE_RECR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_RECR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_RECR(pcie_port)));
++
++    /* Clear the Root status register */ 
++    reg = IFX_REG_R32(PCIE_RESR(pcie_port));
++    IFX_REG_W32(reg, PCIE_RESR(pcie_port));
++}
++
++static INLINE void
++pcie_root_setup(int pcie_port)
++{
++    u32 reg;
++
++    /* Root control and capabilities register */
++    reg = IFX_REG_R32(PCIE_RCTLCAP(pcie_port));
++    reg |= PCIE_RCTLCAP_SERR_ENABLE | PCIE_RCTLCAP_PME_INT_EN;
++    IFX_REG_W32(reg, PCIE_RCTLCAP(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_RCTLCAP: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_RCTLCAP(pcie_port)));
++}
++
++static INLINE void
++pcie_vc_setup(int pcie_port)
++{
++    u32 reg;
++
++    /* Port VC Capability Register 2 */
++    reg = IFX_REG_R32(PCIE_PVC2(pcie_port));
++    reg &= ~PCIE_PVC2_VC_ARB_WRR;
++    reg |= PCIE_PVC2_VC_ARB_16P_FIXED_WRR;
++    IFX_REG_W32(reg, PCIE_PVC2(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_PVC2: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_PVC2(pcie_port)));
++
++    /* VC0 Resource Capability Register */
++    reg = IFX_REG_R32(PCIE_VC0_RC(pcie_port));
++    reg &= ~PCIE_VC0_RC_REJECT_SNOOP;
++    IFX_REG_W32(reg, PCIE_VC0_RC(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_VC0_RC: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_VC0_RC(pcie_port)));
++}
++
++static INLINE void
++pcie_port_logic_setup(int pcie_port)
++{
++    u32 reg;
++
++    /* FTS number, default 12, increase to 63, may increase time from/to L0s to L0  */
++    reg = IFX_REG_R32(PCIE_AFR(pcie_port));
++    reg &= ~(PCIE_AFR_FTS_NUM | PCIE_AFR_COM_FTS_NUM);
++    reg |= SM(PCIE_AFR_FTS_NUM_DEFAULT, PCIE_AFR_FTS_NUM)
++        | SM(PCIE_AFR_FTS_NUM_DEFAULT, PCIE_AFR_COM_FTS_NUM);
++    /* L0s and L1 entry latency */
++    reg &= ~(PCIE_AFR_L0S_ENTRY_LATENCY | PCIE_AFR_L1_ENTRY_LATENCY);
++    reg |= SM(PCIE_AFR_L0S_ENTRY_LATENCY_DEFAULT, PCIE_AFR_L0S_ENTRY_LATENCY)
++        | SM(PCIE_AFR_L1_ENTRY_LATENCY_DEFAULT, PCIE_AFR_L1_ENTRY_LATENCY);
++    IFX_REG_W32(reg, PCIE_AFR(pcie_port));
++
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_AFR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_AFR(pcie_port)));
++
++    /* Port Link Control Register */
++    reg = IFX_REG_R32(PCIE_PLCR(pcie_port));
++    reg |= PCIE_PLCR_DLL_LINK_EN;  /* Enable the DLL link */
++    IFX_REG_W32(reg, PCIE_PLCR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_PLCR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_PLCR(pcie_port)));
++
++    /* Lane Skew Register */
++    reg = IFX_REG_R32(PCIE_LSR(pcie_port));
++    /* Enable ACK/NACK and FC */
++    reg &= ~(PCIE_LSR_ACKNAK_DISABLE | PCIE_LSR_FC_DISABLE); 
++    IFX_REG_W32(reg, PCIE_LSR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_LSR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_LSR(pcie_port)));
++
++    /* Symbol Timer Register and Filter Mask Register 1 */
++    reg = IFX_REG_R32(PCIE_STRFMR(pcie_port));
++
++    /* Default SKP interval is very accurate already, 5us */
++    /* Enable IO/CFG transaction */
++    reg |= PCIE_STRFMR_RX_CFG_TRANS_ENABLE | PCIE_STRFMR_RX_IO_TRANS_ENABLE;
++    /* Disable FC WDT */
++    reg &= ~PCIE_STRFMR_FC_WDT_DISABLE;
++    IFX_REG_W32(reg, PCIE_STRFMR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_STRFMR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_STRFMR(pcie_port)));
++
++    /* Filter Masker Register 2 */
++    reg = IFX_REG_R32(PCIE_FMR2(pcie_port));
++    reg |= PCIE_FMR2_VENDOR_MSG1_PASSED_TO_TRGT1 | PCIE_FMR2_VENDOR_MSG0_PASSED_TO_TRGT1;
++    IFX_REG_W32(reg, PCIE_FMR2(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_FMR2: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_FMR2(pcie_port)));
++
++    /* VC0 Completion Receive Queue Control Register */
++    reg = IFX_REG_R32(PCIE_VC0_CRQCR(pcie_port));
++    reg &= ~PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE;
++    reg |= SM(PCIE_VC0_TLP_QUEUE_MODE_BYPASS, PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE);
++    IFX_REG_W32(reg, PCIE_VC0_CRQCR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_VC0_CRQCR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_VC0_CRQCR(pcie_port)));
++}
++
++static INLINE void 
++pcie_rc_cfg_reg_setup(int pcie_port)
++{
++    pcie_ltssm_disable(pcie_port);
++    pcie_mem_io_setup(pcie_port);
++    pcie_msi_setup(pcie_port);
++    pcie_pm_setup(pcie_port);
++    pcie_bus_setup(pcie_port);
++    pcie_device_setup(pcie_port);
++    pcie_link_setup(pcie_port);
++    pcie_error_setup(pcie_port);
++    pcie_root_setup(pcie_port);
++    pcie_vc_setup(pcie_port);
++    pcie_port_logic_setup(pcie_port);
++}
++
++static int 
++ifx_pcie_wait_phy_link_up(int pcie_port)
++{
++#define IFX_PCIE_PHY_LINK_UP_TIMEOUT  1000 /* XXX, tunable */
++    int i;
++
++    /* Wait for PHY link is up */
++    for (i = 0; i < IFX_PCIE_PHY_LINK_UP_TIMEOUT; i++) {
++        if (ifx_pcie_link_up(pcie_port)) {
++            break;
++        }
++        udelay(100);
++    }
++    if (i >= IFX_PCIE_PHY_LINK_UP_TIMEOUT) {
++        printk(KERN_ERR "%s timeout\n", __func__);
++        return -1;
++    }
++
++    /* Check data link up or not */
++    if (!(IFX_REG_R32(PCIE_RC_DR(pcie_port)) & PCIE_RC_DR_DLL_UP)) {
++        printk(KERN_ERR "%s DLL link is still down\n", __func__);
++        return -1;
++    }
++
++    /* Check Data link active or not */
++    if (!(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_DLL_ACTIVE)) {
++        printk(KERN_ERR "%s DLL is not active\n", __func__);
++        return -1;
++    }
++    return 0;
++#undef IFX_PCIE_PHY_LINK_UP_TIMEOUT
++}
++
++static INLINE int
++pcie_app_loigc_setup(int pcie_port)
++{
++#ifdef IFX_PCIE_PHY_DBG
++    pcie_disable_scrambling(pcie_port);
++#endif /* IFX_PCIE_PHY_DBG */
++    pcie_ahb_bus_error_suppress(pcie_port);
++
++    /* Pull PCIe EP out of reset */
++    pcie_device_rst_deassert(pcie_port);
++
++    /* Start LTSSM training between RC and EP */
++    pcie_ltssm_enable(pcie_port);
++
++    /* Check PHY status after enabling LTSSM */
++    if (ifx_pcie_wait_phy_link_up(pcie_port) != 0) {
++        return -1;
++    }
++    return 0;
++}
++
++/* 
++ * Must be done after ltssm due to based on negotiated link 
++ * width and payload size
++ * Update the Replay Time Limit. Empirically, some PCIe 
++ * devices take a little longer to respond than expected under 
++ * load. As a workaround for this we configure the Replay Time 
++ * Limit to the value expected for a 512 byte MPS instead of 
++ * our actual 128 byte MPS. The numbers below are directly 
++ * from the PCIe spec table 3-4/5. 
++ */ 
++static INLINE void
++pcie_replay_time_update(int pcie_port)
++{
++    u32 reg;
++    int nlw;
++    int rtl;
++
++    reg = IFX_REG_R32(PCIE_LCTLSTS(pcie_port));
++
++    nlw = MS(reg, PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH);
++    switch (nlw) {
++        case PCIE_MAX_LENGTH_WIDTH_X1:
++            rtl = 1677;
++            break;
++        case PCIE_MAX_LENGTH_WIDTH_X2:
++            rtl = 867;
++            break;
++        case PCIE_MAX_LENGTH_WIDTH_X4:
++            rtl = 462;
++            break;
++        case PCIE_MAX_LENGTH_WIDTH_X8:
++            rtl = 258;
++            break;
++        default:
++            rtl = 1677;
++            break;
++    }
++    reg = IFX_REG_R32(PCIE_ALTRT(pcie_port));
++    reg &= ~PCIE_ALTRT_REPLAY_TIME_LIMIT;
++    reg |= SM(rtl, PCIE_ALTRT_REPLAY_TIME_LIMIT);
++    IFX_REG_W32(reg, PCIE_ALTRT(pcie_port));
++
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_ALTRT 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_ALTRT(pcie_port)));
++}
++
++/*
++ * Table 359 Enhanced Configuration Address Mapping1)
++ * 1) This table is defined in Table 7-1, page 341, PCI Express Base Specification v1.1
++ * Memory Address PCI Express Configuration Space
++ * A[(20+n-1):20] Bus Number 1 < n < 8
++ * A[19:15] Device Number
++ * A[14:12] Function Number
++ * A[11:8] Extended Register Number
++ * A[7:2] Register Number
++ * A[1:0] Along with size of the access, used to generate Byte Enables
++ * For VR9, only the address bits [22:0] are mapped to the configuration space:
++ * . Address bits [22:20] select the target bus (1-of-8)1)
++ * . Address bits [19:15] select the target device (1-of-32) on the bus
++ * . Address bits [14:12] select the target function (1-of-8) within the device.
++ * . Address bits [11:2] selects the target dword (1-of-1024) within the selected function.s configuration space
++ * . Address bits [1:0] define the start byte location within the selected dword.
++ */
++static inline u32
++pcie_bus_addr(u8 bus_num, u16 devfn, int where)
++{
++    u32 addr;
++    u8  bus;
++
++    if (!bus_num) {
++        /* type 0 */
++        addr = ((PCI_SLOT(devfn) & 0x1F) << 15) | ((PCI_FUNC(devfn) & 0x7) << 12) | ((where & 0xFFF)& ~3);
++    } 
++    else {
++        bus = bus_num;
++        /* type 1, only support 8 buses  */
++        addr = ((bus & 0x7) << 20) | ((PCI_SLOT(devfn) & 0x1F) << 15) |
++                ((PCI_FUNC(devfn) & 0x7) << 12) | ((where & 0xFFF) & ~3);
++    }
++    IFX_PCIE_PRINT(PCIE_MSG_CFG, "%s: bus addr : %02x:%02x.%01x/%02x, addr=%08x\n",
++        __func__, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn), where, addr);
++    return addr;
++}
++
++static int 
++pcie_valid_config(int pcie_port, int bus, int dev)
++{
++    /* RC itself */
++    if ((bus == 0) && (dev == 0)) {
++         return 1;
++    }
++
++    /* No physical link */
++    if (!ifx_pcie_link_up(pcie_port)) {
++        return 0;
++    }
++
++    /* Bus zero only has RC itself
++     * XXX, check if EP will be integrated 
++     */
++    if ((bus == 0) && (dev != 0)) {
++        return 0;
++    }
++
++    /* Maximum 8 buses supported for VRX */
++    if (bus > 9) {
++        return 0;
++    }
++
++    /* 
++     * PCIe is PtP link, one bus only supports only one device 
++     * except bus zero and PCIe switch which is virtual bus device
++     * The following two conditions really depends on the system design
++     * and attached the device.
++     * XXX, how about more new switch
++     */
++    if ((bus == 1) && (dev != 0)) {
++        return 0;
++    }
++
++    if ((bus >= 3) && (dev != 0)) {
++        return 0;
++    }
++    return 1;
++}
++
++static INLINE u32 
++ifx_pcie_cfg_rd(int pcie_port, u32 reg)
++{
++    return IFX_REG_R32((volatile u32 *)(PCIE_CFG_PORT_TO_BASE(pcie_port) + reg));
++}
++
++static INLINE void 
++ifx_pcie_cfg_wr(int pcie_port, unsigned int reg, u32 val)
++{
++    IFX_REG_W32( val, (volatile u32 *)(PCIE_CFG_PORT_TO_BASE(pcie_port) + reg));
++}
++
++static INLINE u32 
++ifx_pcie_rc_cfg_rd(int pcie_port, u32 reg)
++{
++    return IFX_REG_R32((volatile u32 *)(PCIE_RC_PORT_TO_BASE(pcie_port) + reg));
++}
++
++static INLINE void 
++ifx_pcie_rc_cfg_wr(int pcie_port, unsigned int reg, u32 val)
++{
++    IFX_REG_W32(val, (volatile u32 *)(PCIE_RC_PORT_TO_BASE(pcie_port) + reg));
++}
++
++u32 
++ifx_pcie_bus_enum_read_hack(int where, u32 value)
++{
++    u32 tvalue = value;
++
++    if (where == PCI_PRIMARY_BUS) {
++        u8 primary, secondary, subordinate;
++
++        primary = tvalue & 0xFF;
++        secondary = (tvalue >> 8) & 0xFF;
++        subordinate = (tvalue >> 16) & 0xFF;
++        primary += pcibios_1st_host_bus_nr();
++        secondary += pcibios_1st_host_bus_nr();
++        subordinate += pcibios_1st_host_bus_nr();
++        tvalue = (tvalue & 0xFF000000) | (u32)primary | (u32)(secondary << 8) | (u32)(subordinate << 16);
++    }
++    return tvalue;
++}
++
++u32 
++ifx_pcie_bus_enum_write_hack(int where, u32 value)
++{
++    u32 tvalue = value;
++
++    if (where == PCI_PRIMARY_BUS) {
++        u8 primary, secondary, subordinate;
++
++        primary = tvalue & 0xFF;
++        secondary = (tvalue >> 8) & 0xFF;
++        subordinate = (tvalue >> 16) & 0xFF;
++        if (primary > 0 && primary != 0xFF) {
++            primary -= pcibios_1st_host_bus_nr();
++        }
++
++        if (secondary > 0 && secondary != 0xFF) {
++            secondary -= pcibios_1st_host_bus_nr();
++        }
++        if (subordinate > 0 && subordinate != 0xFF) {
++            subordinate -= pcibios_1st_host_bus_nr();
++        }
++        tvalue = (tvalue & 0xFF000000) | (u32)primary | (u32)(secondary << 8) | (u32)(subordinate << 16);
++    }
++    else if (where == PCI_SUBORDINATE_BUS) {
++        u8 subordinate = tvalue & 0xFF;
++
++        subordinate = subordinate > 0 ? subordinate - pcibios_1st_host_bus_nr() : 0;
++        tvalue = subordinate;
++    }
++    return tvalue;
++}
++
++/** 
++ * \fn static int ifx_pcie_read_config(struct pci_bus *bus, u32 devfn, 
++ *                   int where, int size, u32 *value)
++ * \brief Read a value from configuration space 
++ * 
++ * \param[in] bus    Pointer to pci bus
++ * \param[in] devfn  PCI device function number
++ * \param[in] where  PCI register number 
++ * \param[in] size   Register read size
++ * \param[out] value    Pointer to return value
++ * \return  PCIBIOS_BAD_REGISTER_NUMBER Invalid register number
++ * \return  PCIBIOS_FUNC_NOT_SUPPORTED  PCI function not supported
++ * \return  PCIBIOS_DEVICE_NOT_FOUND    PCI device not found
++ * \return  PCIBIOS_SUCCESSFUL          OK
++ * \ingroup IFX_PCIE_OS
++ */ 
++static int 
++ifx_pcie_read_config(struct pci_bus *bus, u32 devfn, 
++                    int where, int size, u32 *value)
++{
++    u32 data = 0;
++    int bus_number = bus->number;
++    static const u32 mask[8] = {0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0};    
++    int ret = PCIBIOS_SUCCESSFUL;
++    struct ifx_pci_controller *ctrl = bus->sysdata;
++    int pcie_port = ctrl->port;
++
++    if (unlikely(size != 1 && size != 2 && size != 4)){
++        ret = PCIBIOS_BAD_REGISTER_NUMBER;
++        goto out;
++    }
++
++    /* Make sure the address is aligned to natural boundary */
++    if (unlikely(((size - 1) & where))) {
++        ret = PCIBIOS_BAD_REGISTER_NUMBER;
++        goto out;
++    }
++
++    /* 
++     * If we are second controller, we have to cheat OS so that it assume 
++     * its bus number starts from 0 in host controller
++     */
++    bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port);
++
++    /* 
++     * We need to force the bus number to be zero on the root 
++     * bus. Linux numbers the 2nd root bus to start after all 
++     * busses on root 0. 
++     */ 
++    if (bus->parent == NULL) {
++        bus_number = 0; 
++    }
++
++    /* 
++     * PCIe only has a single device connected to it. It is 
++     * always device ID 0. Don't bother doing reads for other 
++     * device IDs on the first segment. 
++     */ 
++    if ((bus_number == 0) && (PCI_SLOT(devfn) != 0)) {
++        ret = PCIBIOS_FUNC_NOT_SUPPORTED;
++        goto out; 
++    }
++
++    if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) {
++        *value = 0xffffffff;
++        ret = PCIBIOS_DEVICE_NOT_FOUND;
++        goto out;
++    }
++
++    IFX_PCIE_PRINT(PCIE_MSG_READ_CFG, "%s: %02x:%02x.%01x/%02x:%01d\n", __func__, bus_number,
++        PCI_SLOT(devfn), PCI_FUNC(devfn), where, size);
++
++    PCIE_IRQ_LOCK(ifx_pcie_lock);
++    if (bus_number == 0) { /* RC itself */
++        u32 t;
++
++        t = (where & ~3);
++        data = ifx_pcie_rc_cfg_rd(pcie_port, t);
++        IFX_PCIE_PRINT(PCIE_MSG_READ_CFG, "%s: rd local cfg, offset:%08x, data:%08x\n",
++            __func__, t, data);
++    }
++    else {
++        u32 addr = pcie_bus_addr(bus_number, devfn, where);
++
++        data = ifx_pcie_cfg_rd(pcie_port, addr);
++        if (pcie_port == IFX_PCIE_PORT0) {
++    #ifdef CONFIG_IFX_PCIE_HW_SWAP
++            data = le32_to_cpu(data);
++    #endif /* CONFIG_IFX_PCIE_HW_SWAP */
++        }
++        else {
++    #ifdef CONFIG_IFX_PCIE1_HW_SWAP
++            data = le32_to_cpu(data);
++    #endif /* CONFIG_IFX_PCIE_HW_SWAP */
++        } 
++    }
++    /* To get a correct PCI topology, we have to restore the bus number to OS */
++    data = ifx_pcie_bus_enum_hack(bus, devfn, where, data, pcie_port, 1);
++
++    PCIE_IRQ_UNLOCK(ifx_pcie_lock);
++    IFX_PCIE_PRINT(PCIE_MSG_READ_CFG, "%s: read config: data=%08x raw=%08x\n",
++        __func__, (data >> (8 * (where & 3))) & mask[size & 7], data); 
++
++    *value = (data >> (8 * (where & 3))) & mask[size & 7];
++out:
++    return ret;
++}
++
++static u32 
++ifx_pcie_size_to_value(int where, int size, u32 data, u32 value)
++{
++    u32 shift;
++    u32 tdata = data;
++
++    switch (size) {
++        case 1:
++            shift = (where & 0x3) << 3;
++            tdata &= ~(0xffU << shift);
++            tdata |= ((value & 0xffU) << shift);
++            break;
++        case 2:
++            shift = (where & 3) << 3;
++            tdata &= ~(0xffffU << shift);
++            tdata |= ((value & 0xffffU) << shift);
++            break;
++        case 4:
++            tdata = value;
++            break;
++    }
++    return tdata;
++}
++
++/** 
++ * \fn static static int ifx_pcie_write_config(struct pci_bus *bus, u32 devfn,
++ *                 int where, int size, u32 value)
++ * \brief Write a value to PCI configuration space 
++ * 
++ * \param[in] bus    Pointer to pci bus
++ * \param[in] devfn  PCI device function number
++ * \param[in] where  PCI register number 
++ * \param[in] size   The register size to be written
++ * \param[in] value  The valule to be written
++ * \return PCIBIOS_BAD_REGISTER_NUMBER Invalid register number
++ * \return PCIBIOS_DEVICE_NOT_FOUND    PCI device not found
++ * \return PCIBIOS_SUCCESSFUL          OK
++ * \ingroup IFX_PCIE_OS
++ */ 
++static int 
++ifx_pcie_write_config(struct pci_bus *bus, u32 devfn,
++                  int where, int size, u32 value)
++{
++    int bus_number = bus->number;
++    int ret = PCIBIOS_SUCCESSFUL;
++    struct ifx_pci_controller *ctrl = bus->sysdata;
++    int pcie_port = ctrl->port;
++    u32 tvalue = value;
++    u32 data;
++
++    /* Make sure the address is aligned to natural boundary */
++    if (unlikely(((size - 1) & where))) {
++        ret = PCIBIOS_BAD_REGISTER_NUMBER;
++        goto out;
++    }
++    /* 
++     * If we are second controller, we have to cheat OS so that it assume 
++     * its bus number starts from 0 in host controller
++     */
++    bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port);
++
++    /* 
++     * We need to force the bus number to be zero on the root 
++     * bus. Linux numbers the 2nd root bus to start after all 
++     * busses on root 0. 
++     */ 
++    if (bus->parent == NULL) {
++        bus_number = 0; 
++    }
++
++    if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) {
++        ret = PCIBIOS_DEVICE_NOT_FOUND;
++        goto out;
++    }
++
++    IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG, "%s: %02x:%02x.%01x/%02x:%01d value=%08x\n", __func__, 
++        bus_number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, value);
++
++    /* XXX, some PCIe device may need some delay */
++    PCIE_IRQ_LOCK(ifx_pcie_lock);
++
++    /* 
++     * To configure the correct bus topology using native way, we have to cheat Os so that
++     * it can configure the PCIe hardware correctly.
++     */
++    tvalue = ifx_pcie_bus_enum_hack(bus, devfn, where, value, pcie_port, 0);
++
++    if (bus_number == 0) { /* RC itself */
++        u32 t;
++
++        t = (where & ~3);
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: wr local cfg, offset:%08x, fill:%08x\n", __func__, t, value);
++        data = ifx_pcie_rc_cfg_rd(pcie_port, t);
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: rd local cfg, offset:%08x, data:%08x\n", __func__, t, data);
++
++        data = ifx_pcie_size_to_value(where, size, data, tvalue);
++
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: wr local cfg, offset:%08x, value:%08x\n", __func__, t, data);
++        ifx_pcie_rc_cfg_wr(pcie_port, t, data);     
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: rd local cfg, offset:%08x, value:%08x\n", 
++            __func__, t, ifx_pcie_rc_cfg_rd(pcie_port, t));
++    }
++    else {
++        u32 addr = pcie_bus_addr(bus_number, devfn, where);
++
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: wr cfg, offset:%08x, fill:%08x\n", __func__, addr, value);
++        data = ifx_pcie_cfg_rd(pcie_port, addr);
++        if (pcie_port == IFX_PCIE_PORT0) {
++    #ifdef CONFIG_IFX_PCIE_HW_SWAP
++            data = le32_to_cpu(data);    
++    #endif /* CONFIG_IFX_PCIE_HW_SWAP */
++        }
++        else {
++    #ifdef CONFIG_IFX_PCIE1_HW_SWAP
++            data = le32_to_cpu(data);    
++    #endif /* CONFIG_IFX_PCIE_HW_SWAP */
++        }
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: rd cfg, offset:%08x, data:%08x\n", __func__, addr, data);
++
++        data = ifx_pcie_size_to_value(where, size, data, tvalue);
++        if (pcie_port == IFX_PCIE_PORT0) {
++    #ifdef CONFIG_IFX_PCIE_HW_SWAP
++            data = cpu_to_le32(data);
++    #endif /* CONFIG_IFX_PCIE_HW_SWAP */
++        }
++        else {
++    #ifdef CONFIG_IFX_PCIE1_HW_SWAP
++            data = cpu_to_le32(data);
++    #endif /* CONFIG_IFX_PCIE_HW_SWAP */
++        }
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG, "%s: wr cfg, offset:%08x, value:%08x\n", __func__, addr, data);
++        ifx_pcie_cfg_wr(pcie_port, addr, data);
++        IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG, "%s: rd cfg, offset:%08x, value:%08x\n", 
++            __func__, addr, ifx_pcie_cfg_rd(pcie_port, addr));
++    }
++    PCIE_IRQ_UNLOCK(ifx_pcie_lock);
++out:
++    return ret;
++}
++
++static struct resource ifx_pcie_io_resource = {
++    .name   = "PCIe0 I/O space",
++    .start  = PCIE_IO_PHY_BASE,
++    .end    = PCIE_IO_PHY_END,
++    .flags  = IORESOURCE_IO,
++};
++
++static struct resource ifx_pcie_mem_resource = {
++    .name   = "PCIe0 Memory space",
++    .start  = PCIE_MEM_PHY_BASE,
++    .end    = PCIE_MEM_PHY_END,
++    .flags  = IORESOURCE_MEM,
++};
++
++static struct pci_ops ifx_pcie_ops = {
++    .read  = ifx_pcie_read_config,
++    .write = ifx_pcie_write_config,
++};
++
++#ifdef CONFIG_IFX_PCIE_2ND_CORE
++static struct resource ifx_pcie1_io_resource = {
++    .name   = "PCIe1 I/O space",
++    .start  = PCIE1_IO_PHY_BASE,
++    .end    = PCIE1_IO_PHY_END,
++    .flags  = IORESOURCE_IO,
++};
++
++static struct resource ifx_pcie1_mem_resource = {
++    .name   = "PCIe1 Memory space",
++    .start  = PCIE1_MEM_PHY_BASE,
++    .end    = PCIE1_MEM_PHY_END,
++    .flags  = IORESOURCE_MEM,
++};
++#endif /* CONFIG_IFX_PCIE_2ND_CORE */
++
++static struct ifx_pci_controller ifx_pcie_controller[IFX_PCIE_CORE_NR] = {
++    {
++        .pcic = {
++            .pci_ops      = &ifx_pcie_ops,
++            .mem_resource = &ifx_pcie_mem_resource,
++            .io_resource  = &ifx_pcie_io_resource,
++         },
++         .port = IFX_PCIE_PORT0,
++    },
++#ifdef CONFIG_IFX_PCIE_2ND_CORE
++    {
++         .pcic = {
++            .pci_ops      = &ifx_pcie_ops,
++            .mem_resource = &ifx_pcie1_mem_resource,
++            .io_resource  = &ifx_pcie1_io_resource,
++         },
++         .port = IFX_PCIE_PORT1,
++    },
++#endif /* CONFIG_IFX_PCIE_2ND_CORE */
++};
++
++#ifdef IFX_PCIE_ERROR_INT
++static INLINE void 
++pcie_core_int_clear_all(int pcie_port)
++{
++    u32 reg;
++
++    reg = IFX_REG_R32(PCIE_IRNCR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_ISR, "%s PCIE_IRNCR: 0x%08x\n", 
++        __func__, IFX_REG_R32(PCIE_IRNCR(pcie_port)));
++    reg &= PCIE_RC_CORE_COMBINED_INT;
++    IFX_REG_W32(reg, PCIE_IRNCR(pcie_port));
++}
++
++static irqreturn_t
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
++pcie_rc_core_isr(int irq, void *dev_id)
++#else
++pcie_rc_core_isr(int irq, void *dev_id, struct pt_regs *regs)
++#endif
++{
++    struct ifx_pci_controller *ctrl = (struct ifx_pci_controller *)dev_id;
++    int pcie_port = ctrl->port;
++
++    IFX_PCIE_PRINT(PCIE_MSG_ISR, "PCIe RC error intr %d\n", irq);
++    pcie_core_int_clear_all(pcie_port);
++    return IRQ_HANDLED;
++}
++
++static int
++pcie_rc_core_int_init(int pcie_port)
++{
++    int ret;
++
++    IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s enter \n", __func__);
++
++    /* Enable core interrupt */
++    IFX_REG_SET_BIT(PCIE_RC_CORE_COMBINED_INT, PCIE_IRNEN(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_IRNEN: 0x%08x\n", 
++        __func__, IFX_REG_R32(PCIE_IRNEN(pcie_port)));
++
++    /* Clear it first */
++    IFX_REG_SET_BIT(PCIE_RC_CORE_COMBINED_INT, PCIE_IRNCR(pcie_port));
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_IRNCR: 0x%08x\n",
++        __func__, IFX_REG_R32(PCIE_IRNCR(pcie_port)));
++    ret = request_irq(pcie_irqs[pcie_port].ir_irq.irq, pcie_rc_core_isr, IRQF_DISABLED,
++                pcie_irqs[pcie_port].ir_irq.name, &ifx_pcie_controller[pcie_port]);
++    if (ret) {
++        printk(KERN_ERR "%s request irq %d failed\n", __func__, IFX_PCIE_IR);
++    }
++    IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s exit \n", __func__);
++
++    return ret;
++}
++#endif /* IFX_PCIE_ERROR_INT */
++
++/** 
++ * \fn int ifx_pcie_bios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
++ * \brief Map a PCI device to the appropriate interrupt line 
++ * 
++ * \param[in] dev    The Linux PCI device structure for the device to map 
++ * \param[in] slot   The slot number for this device on __BUS 0__. Linux 
++ *                   enumerates through all the bridges and figures out the 
++ *                   slot on Bus 0 where this device eventually hooks to. 
++ * \param[in] pin    The PCI interrupt pin read from the device, then swizzled 
++ *                   as it goes through each bridge. 
++ * \return Interrupt number for the device
++ * \ingroup IFX_PCIE_OS
++ */ 
++int 
++ifx_pcie_bios_map_irq(IFX_PCI_CONST struct pci_dev *dev, u8 slot, u8 pin)
++{
++    u32 irq_bit = 0;
++    int irq = 0;
++    struct ifx_pci_controller *ctrl = dev->bus->sysdata;
++    int pcie_port = ctrl->port;
++
++    printk("%s port %d dev %s slot %d pin %d \n", __func__, pcie_port, pci_name(dev), slot, pin);
++
++    if ((pin == PCIE_LEGACY_DISABLE) || (pin > PCIE_LEGACY_INT_MAX)) {
++        printk(KERN_WARNING "WARNING: dev %s: invalid interrupt pin %d\n", pci_name(dev), pin);
++        return -1;
++    }
++    /* Pin index so minus one */
++    irq_bit = pcie_irqs[pcie_port].legacy_irq[pin - 1].irq_bit;
++    irq = pcie_irqs[pcie_port].legacy_irq[pin - 1].irq;
++    IFX_REG_SET_BIT(irq_bit, PCIE_IRNEN(pcie_port));
++//    printk("%s PCIE_IRNEN: 0x%08x\n", __func__, IFX_REG_R32(PCIE_IRNEN(pcie_port)));
++    IFX_REG_SET_BIT(irq_bit, PCIE_IRNCR(pcie_port));
++  //  printk("%s PCIE_IRNCR: 0x%08x\n", __func__, IFX_REG_R32(PCIE_IRNCR(pcie_port)));
++    printk("%s dev %s irq %d assigned\n", __func__, pci_name(dev), irq);
++//    printk("%s dev %s: exit\n", __func__, pci_name(dev));
++    return irq;
++}
++
++/** 
++ * \fn int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev)
++ * \brief Called to perform platform specific PCI setup 
++ * 
++ * \param[in] dev The Linux PCI device structure for the device to map
++ * \return OK
++ * \ingroup IFX_PCIE_OS
++ */ 
++int 
++ifx_pcie_bios_plat_dev_init(struct pci_dev *dev)
++{
++    u16 config;
++#ifdef IFX_PCIE_ERROR_INT
++    u32 dconfig; 
++    int pos;
++#endif /* IFX_PCIE_ERROR_INT */
++
++    IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s enter \n", __func__);
++    /* Enable reporting System errors and parity errors on all devices */ 
++    /* Enable parity checking and error reporting */ 
++    pci_read_config_word(dev, PCI_COMMAND, &config);
++    config |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR /*| PCI_COMMAND_INVALIDATE |
++          PCI_COMMAND_FAST_BACK*/;
++    pci_write_config_word(dev, PCI_COMMAND, config);
++
++    if (dev->subordinate) {
++        /* Set latency timers on sub bridges */
++        pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40); /* XXX, */
++        /* More bridge error detection */
++        pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &config);
++        config |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR;
++        pci_write_config_word(dev, PCI_BRIDGE_CONTROL, config);
++    }
++#ifdef IFX_PCIE_ERROR_INT
++    /* Enable the PCIe normal error reporting */
++    pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
++    if (pos) {
++
++        /* Disable system error generation in response to error messages */
++        pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &config);
++        config &= ~(PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | PCI_EXP_RTCTL_SEFEE);
++        pci_write_config_word(dev, pos + PCI_EXP_RTCTL, config);
++
++        /* Clear PCIE Capability's Device Status */
++        pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &config);
++        pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, config);
++
++        /* Update Device Control */ 
++        pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &config);
++        /* Correctable Error Reporting */
++        config |= PCI_EXP_DEVCTL_CERE;
++        /* Non-Fatal Error Reporting */
++        config |= PCI_EXP_DEVCTL_NFERE;
++        /* Fatal Error Reporting */
++        config |= PCI_EXP_DEVCTL_FERE;
++        /* Unsupported Request */
++        config |= PCI_EXP_DEVCTL_URRE;
++        pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, config);
++    }
++
++    /* Find the Advanced Error Reporting capability */
++    pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
++    if (pos) {
++        /* Clear Uncorrectable Error Status */ 
++        pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &dconfig);
++        pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, dconfig);
++        /* Enable reporting of all uncorrectable errors */
++        /* Uncorrectable Error Mask - turned on bits disable errors */
++        pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, 0);
++        /* 
++        * Leave severity at HW default. This only controls if 
++        * errors are reported as uncorrectable or 
++        * correctable, not if the error is reported. 
++        */ 
++        /* PCI_ERR_UNCOR_SEVER - Uncorrectable Error Severity */
++        /* Clear Correctable Error Status */
++        pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &dconfig);
++        pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, dconfig);
++        /* Enable reporting of all correctable errors */
++        /* Correctable Error Mask - turned on bits disable errors */
++        pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, 0);
++        /* Advanced Error Capabilities */ 
++        pci_read_config_dword(dev, pos + PCI_ERR_CAP, &dconfig);
++        /* ECRC Generation Enable */
++        if (dconfig & PCI_ERR_CAP_ECRC_GENC) {
++            dconfig |= PCI_ERR_CAP_ECRC_GENE;
++        }
++        /* ECRC Check Enable */
++        if (dconfig & PCI_ERR_CAP_ECRC_CHKC) {
++            dconfig |= PCI_ERR_CAP_ECRC_CHKE;
++        }
++        pci_write_config_dword(dev, pos + PCI_ERR_CAP, dconfig);
++
++        /* PCI_ERR_HEADER_LOG - Header Log Register (16 bytes) */
++        /* Enable Root Port's interrupt in response to error messages */
++        pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND,
++              PCI_ERR_ROOT_CMD_COR_EN |
++              PCI_ERR_ROOT_CMD_NONFATAL_EN |
++              PCI_ERR_ROOT_CMD_FATAL_EN); 
++        /* Clear the Root status register */
++        pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &dconfig);
++        pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, dconfig);
++    }
++#endif /* IFX_PCIE_ERROR_INT */
++    /* WAR, only 128 MRRS is supported, force all EPs to support this value */
++    pcie_set_readrq(dev, 128);
++    IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s exit \n", __func__);
++    return 0;
++}
++
++static void
++pcie_phy_rst(int pcie_port)
++{
++     pcie_phy_rst_assert(pcie_port);
++     pcie_phy_rst_deassert(pcie_port);
++
++     /* Make sure PHY PLL is stable */
++     udelay(20);
++}
++
++static int
++pcie_rc_initialize(int pcie_port)
++{
++    int i;
++#define IFX_PCIE_PHY_LOOP_CNT  5
++
++    pcie_rcu_endian_setup(pcie_port);
++
++    pcie_ep_gpio_rst_init(pcie_port);
++
++    /* 
++     * XXX, PCIe elastic buffer bug will cause not to be detected. One more 
++     * reset PCIe PHY will solve this issue 
++     */
++    for (i = 0; i < IFX_PCIE_PHY_LOOP_CNT; i++) {
++        /* Disable PCIe PHY Analog part for sanity check */
++      pcie_phy_pmu_disable(pcie_port);
++
++      pcie_phy_rst(pcie_port);
++
++        /* PCIe Core reset enabled, low active, sw programmed */
++      pcie_core_rst_assert(pcie_port);
++
++        /* Put PCIe EP in reset status */
++      pcie_device_rst_assert(pcie_port);
++
++        /* PCI PHY & Core reset disabled, high active, sw programmed */
++      pcie_core_rst_deassert(pcie_port);
++
++        /* Already in a quiet state, program PLL, enable PHY, check ready bit */
++      pcie_phy_clock_mode_setup(pcie_port);
++
++        /* Enable PCIe PHY and Clock */
++      pcie_core_pmu_setup(pcie_port);
++
++      /* Clear status registers */
++        pcie_status_register_clear(pcie_port);
++
++    #ifdef CONFIG_PCI_MSI
++        pcie_msi_init(pcie_port);
++    #endif /* CONFIG_PCI_MSI */
++      pcie_rc_cfg_reg_setup(pcie_port);
++
++      /* Once link is up, break out */
++        if (pcie_app_loigc_setup(pcie_port) == 0) {
++          break;
++        }
++    }
++    if (i >= IFX_PCIE_PHY_LOOP_CNT) {
++        printk(KERN_ERR "%s link up failed!!!!!\n", __func__);
++        return -EIO;
++    }
++    /* NB, don't increase ACK/NACK timer timeout value, which will cause a lot of COR errors */
++    pcie_replay_time_update(pcie_port);
++#ifdef IFX_PCIE_DBG
++    pcie_post_dump(pcie_port);
++    pcie_status_registers_dump(pcie_port);
++#endif /* IFX_PCIE_DBG */
++    return 0;
++}
++
++static int inline
++ifx_pcie_startup_port_nr(void)
++{
++    int pcie_port = IFX_PCIE_PORT0;
++
++#if defined (CONFIG_IFX_PCIE_1ST_CORE) && defined (CONFIG_IFX_PCIE_2ND_CORE)
++    pcie_port = IFX_PCIE_PORT0;
++#elif defined (CONFIG_IFX_PCIE_1ST_CORE)
++    pcie_port = IFX_PCIE_PORT0;
++#elif defined (CONFIG_IFX_PCIE_2ND_CORE)
++    pcie_port = IFX_PCIE_PORT1;
++#else
++    #error "Please choose valid PCIe Core"
++#endif
++    return pcie_port;
++}
++
++/** 
++ * \fn static int __init ifx_pcie_bios_init(void)
++ * \brief Initialize the IFX PCIe controllers
++ *
++ * \return -EIO    PCIe PHY link is not up
++ * \return -ENOMEM Configuration/IO space failed to map
++ * \return 0       OK
++ * \ingroup IFX_PCIE_OS
++ */ 
++extern int (*ltq_pci_plat_arch_init)(struct pci_dev *dev);
++extern int (*ltq_pci_map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
++
++static int __init 
++ifx_pcie_bios_init(void)
++{
++    char ver_str[128] = {0};
++    void __iomem *io_map_base;
++    int pcie_port;
++    int startup_port;
++
++    IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s enter \n", __func__);
++
++      ltq_pci_map_irq = ifx_pcie_bios_map_irq;
++      ltq_pci_plat_arch_init = ifx_pcie_bios_plat_dev_init;
++
++    /* Enable AHB Master/ Slave */
++    pcie_ahb_pmu_setup();
++
++    startup_port = ifx_pcie_startup_port_nr();
++    
++    for (pcie_port = startup_port; pcie_port < IFX_PCIE_CORE_NR; pcie_port++){
++      if (pcie_rc_initialize(pcie_port) == 0) {
++          IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: ifx_pcie_cfg_base 0x%p\n", 
++                 __func__, PCIE_CFG_PORT_TO_BASE(pcie_port));
++            /* Otherwise, warning will pop up */
++            io_map_base = ioremap(PCIE_IO_PHY_PORT_TO_BASE(pcie_port), PCIE_IO_SIZE);
++            if (io_map_base == NULL) {
++                IFX_PCIE_PRINT(PCIE_MSG_ERR, "%s io space ioremap failed\n", __func__);
++                return -ENOMEM;
++            }
++            ifx_pcie_controller[pcie_port].pcic.io_map_base = (unsigned long)io_map_base;
++
++            register_pci_controller(&ifx_pcie_controller[pcie_port].pcic);
++            /* XXX, clear error status */
++
++            IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: mem_resource 0x%p, io_resource 0x%p\n", 
++                              __func__, &ifx_pcie_controller[pcie_port].pcic.mem_resource, 
++                              &ifx_pcie_controller[pcie_port].pcic.io_resource);
++
++        #ifdef IFX_PCIE_ERROR_INT
++            pcie_rc_core_int_init(pcie_port);
++        #endif /* IFX_PCIE_ERROR_INT */
++        }
++    }
++#ifdef CONFIG_IFX_PMCU
++    ifx_pcie_pmcu_init();
++#endif /* CONFIG_IFX_PMCU */
++
++    sprintf(ver_str, "PCIe Root Complex %d.%d.%d", IFX_PCIE_VER_MAJOR, IFX_PCIE_VER_MID, IFX_PCIE_VER_MINOR);
++    printk(KERN_INFO "%s", ver_str);
++    return 0;
++#undef IFX_PCIE_PHY_LOOP_CNT
++}
++arch_initcall(ifx_pcie_bios_init);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Chuanhua.Lei@infineon.com");
++MODULE_SUPPORTED_DEVICE("Infineon builtin PCIe RC module");
++MODULE_DESCRIPTION("Infineon builtin PCIe RC driver");
++
+diff --git a/arch/mips/pci/ifxmips_pcie.h b/arch/mips/pci/ifxmips_pcie.h
+new file mode 100644
+index 0000000..c6f92f5
+--- /dev/null
++++ b/arch/mips/pci/ifxmips_pcie.h
+@@ -0,0 +1,135 @@
++/******************************************************************************
++**
++** FILE NAME    : ifxmips_pcie.h
++** PROJECT      : IFX UEIP for VRX200
++** MODULES      : PCIe module
++**
++** DATE         : 02 Mar 2009
++** AUTHOR       : Lei Chuanhua
++** DESCRIPTION  : PCIe Root Complex Driver
++** COPYRIGHT    :       Copyright (c) 2009
++**                      Infineon Technologies AG
++**                      Am Campeon 1-12, 85579 Neubiberg, Germany
++**
++**    This program is free software; you can redistribute it and/or modify
++**    it under the terms of the GNU General Public License as published by
++**    the Free Software Foundation; either version 2 of the License, or
++**    (at your option) any later version.
++** HISTORY
++** $Version $Date        $Author         $Comment
++** 0.0.1    17 Mar,2009  Lei Chuanhua    Initial version
++*******************************************************************************/
++#ifndef IFXMIPS_PCIE_H
++#define IFXMIPS_PCIE_H
++#include <linux/version.h>
++#include <linux/types.h>
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include "ifxmips_pci_common.h"
++#include "ifxmips_pcie_reg.h"
++
++/*!
++ \defgroup IFX_PCIE  PCI Express bus driver module   
++ \brief  PCI Express IP module support VRX200 
++*/
++
++/*!
++ \defgroup IFX_PCIE_OS OS APIs
++ \ingroup IFX_PCIE
++ \brief PCIe bus driver OS interface functions
++*/
++
++/*!
++ \file ifxmips_pcie.h
++ \ingroup IFX_PCIE  
++ \brief header file for PCIe module common header file
++*/
++#define PCIE_IRQ_LOCK(lock) do {             \
++    unsigned long flags;                     \
++    spin_lock_irqsave(&(lock), flags);
++#define PCIE_IRQ_UNLOCK(lock)                \
++    spin_unlock_irqrestore(&(lock), flags);  \
++} while (0)
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#define IRQF_SHARED SA_SHIRQ
++#endif
++
++#define PCIE_MSG_MSI        0x00000001
++#define PCIE_MSG_ISR        0x00000002
++#define PCIE_MSG_FIXUP      0x00000004
++#define PCIE_MSG_READ_CFG   0x00000008
++#define PCIE_MSG_WRITE_CFG  0x00000010
++#define PCIE_MSG_CFG        (PCIE_MSG_READ_CFG | PCIE_MSG_WRITE_CFG)
++#define PCIE_MSG_REG        0x00000020
++#define PCIE_MSG_INIT       0x00000040
++#define PCIE_MSG_ERR        0x00000080
++#define PCIE_MSG_PHY        0x00000100
++#define PCIE_MSG_ANY        0x000001ff
++
++#define IFX_PCIE_PORT0      0
++#define IFX_PCIE_PORT1      1
++
++#ifdef CONFIG_IFX_PCIE_2ND_CORE
++#define IFX_PCIE_CORE_NR    2
++#else
++#define IFX_PCIE_CORE_NR    1
++#endif
++
++#define IFX_PCIE_ERROR_INT
++
++//#define IFX_PCIE_DBG
++
++#if defined(IFX_PCIE_DBG)
++#define IFX_PCIE_PRINT(_m, _fmt, args...) do {   \
++        ifx_pcie_debug((_fmt), ##args);          \
++} while (0)
++
++#define INLINE 
++#else
++#define IFX_PCIE_PRINT(_m, _fmt, args...)   \
++    do {} while(0)
++#define INLINE inline
++#endif
++
++struct ifx_pci_controller {
++      struct pci_controller   pcic;
++    
++      /* RC specific, per host bus information */
++      u32   port;  /* Port index, 0 -- 1st core, 1 -- 2nd core */
++};
++
++typedef struct ifx_pcie_ir_irq {
++    const unsigned int irq;
++    const char name[16];
++}ifx_pcie_ir_irq_t;
++
++typedef struct ifx_pcie_legacy_irq{
++    const u32 irq_bit;
++    const int irq;
++}ifx_pcie_legacy_irq_t;
++
++typedef struct ifx_pcie_irq {
++    ifx_pcie_ir_irq_t ir_irq;
++    ifx_pcie_legacy_irq_t legacy_irq[PCIE_LEGACY_INT_MAX];
++}ifx_pcie_irq_t;
++
++extern u32 g_pcie_debug_flag;
++extern void ifx_pcie_debug(const char *fmt, ...);
++extern void pcie_phy_clock_mode_setup(int pcie_port);
++extern void pcie_msi_pic_init(int pcie_port);
++extern u32 ifx_pcie_bus_enum_read_hack(int where, u32 value);
++extern u32 ifx_pcie_bus_enum_write_hack(int where, u32 value);
++
++#define CONFIG_VR9
++
++#ifdef CONFIG_VR9
++#include "ifxmips_pcie_vr9.h"
++#elif defined (CONFIG_AR10)
++#include "ifxmips_pcie_ar10.h"
++#else
++#error "PCIE: platform not defined"
++#endif /* CONFIG_VR9 */
++
++#endif  /* IFXMIPS_PCIE_H */
++
+diff --git a/arch/mips/pci/ifxmips_pcie_ar10.h b/arch/mips/pci/ifxmips_pcie_ar10.h
+new file mode 100644
+index 0000000..99ff463
+--- /dev/null
++++ b/arch/mips/pci/ifxmips_pcie_ar10.h
+@@ -0,0 +1,290 @@
++/****************************************************************************
++                              Copyright (c) 2010
++                            Lantiq Deutschland GmbH
++                     Am Campeon 3; 85579 Neubiberg, Germany
++
++  For licensing information, see the file 'LICENSE' in the root folder of
++  this software module.
++
++ *****************************************************************************/
++/*!
++  \file ifxmips_pcie_ar10.h
++  \ingroup IFX_PCIE
++  \brief PCIe RC driver ar10 specific file
++*/
++
++#ifndef IFXMIPS_PCIE_AR10_H
++#define IFXMIPS_PCIE_AR10_H
++#ifndef AUTOCONF_INCLUDED
++#include <linux/config.h>
++#endif /* AUTOCONF_INCLUDED */
++#include <linux/types.h>
++#include <linux/delay.h>
++
++/* Project header file */
++#include <asm/ifx/ifx_types.h>
++#include <asm/ifx/ifx_pmu.h>
++#include <asm/ifx/ifx_gpio.h>
++#include <asm/ifx/ifx_ebu_led.h>
++
++static inline void pcie_ep_gpio_rst_init(int pcie_port)
++{
++    ifx_ebu_led_enable();
++    if (pcie_port == 0) {
++        ifx_ebu_led_set_data(11, 1);        
++    }
++    else {
++        ifx_ebu_led_set_data(12, 1);  
++    }
++}
++
++static inline void pcie_ahb_pmu_setup(void) 
++{
++    /* XXX, moved to CGU to control AHBM */
++}
++
++static inline void pcie_rcu_endian_setup(int pcie_port)
++{
++    u32 reg;
++
++    reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN);
++    /* Inbound, big endian */
++    reg |= IFX_RCU_BE_AHB4S;
++    if (pcie_port == 0) {
++        reg |= IFX_RCU_BE_PCIE0M;
++
++    #ifdef CONFIG_IFX_PCIE_HW_SWAP
++        /* Outbound, software swap needed */
++        reg |= IFX_RCU_BE_AHB3M;
++        reg &= ~IFX_RCU_BE_PCIE0S;
++    #else
++        /* Outbound little endian  */
++        reg &= ~IFX_RCU_BE_AHB3M;
++        reg &= ~IFX_RCU_BE_PCIE0S;
++    #endif
++    }
++    else {
++        reg |= IFX_RCU_BE_PCIE1M;
++    #ifdef CONFIG_IFX_PCIE1_HW_SWAP
++        /* Outbound, software swap needed */
++        reg |= IFX_RCU_BE_AHB3M;
++        reg &= ~IFX_RCU_BE_PCIE1S;
++    #else
++        /* Outbound little endian  */
++        reg &= ~IFX_RCU_BE_AHB3M;
++        reg &= ~IFX_RCU_BE_PCIE1S;
++    #endif
++    }
++
++    IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN);
++    IFX_PCIE_PRINT(PCIE_MSG_REG, "%s IFX_RCU_AHB_ENDIAN: 0x%08x\n", __func__, IFX_REG_R32(IFX_RCU_AHB_ENDIAN));
++}
++
++static inline void pcie_phy_pmu_enable(int pcie_port)
++{
++    if (pcie_port == 0) { /* XXX, should use macro*/
++        PCIE0_PHY_PMU_SETUP(IFX_PMU_ENABLE);
++    }
++    else {
++        PCIE1_PHY_PMU_SETUP(IFX_PMU_ENABLE);
++    }
++}
++
++static inline void pcie_phy_pmu_disable(int pcie_port)
++{
++    if (pcie_port == 0) { /* XXX, should use macro*/
++        PCIE0_PHY_PMU_SETUP(IFX_PMU_DISABLE);
++    }
++    else {
++        PCIE1_PHY_PMU_SETUP(IFX_PMU_DISABLE);
++    }
++}
++
++static inline void pcie_pdi_big_endian(int pcie_port)
++{
++    u32 reg;
++
++    reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN);
++    if (pcie_port == 0) {
++        /*