From 7185dc2440c81feae80539ecc8d1e0c9e133497b Mon Sep 17 00:00:00 2001 From: blogic Date: Sat, 15 Dec 2012 01:59:45 +0000 Subject: [lantiq] add linux-v3.7 git-svn-id: svn://svn.openwrt.org/openwrt/trunk@34687 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- target/linux/lantiq/Makefile | 4 +- target/linux/lantiq/ase/config-default | 10 +- target/linux/lantiq/config-3.7 | 165 + target/linux/lantiq/falcon/config-default | 15 +- .../files-3.7/firmware/lantiq/vr9_phy11g_a1x.bin | Bin 0 -> 65536 bytes .../files-3.7/firmware/lantiq/vr9_phy11g_a2x.bin | Bin 0 -> 65536 bytes .../files-3.7/firmware/lantiq/vr9_phy22f_a1x.bin | Bin 0 -> 65536 bytes .../files-3.7/firmware/lantiq/vr9_phy22f_a2x.bin | Bin 0 -> 65536 bytes target/linux/lantiq/image/Makefile | 4 - .../0001-MIPS-lantiq-unbreak-devicetree-init.patch | 41 + ...-lantiq-fix-bootselect-bits-on-XRX200-SoC.patch | 71 + ...0003-MIPS-lantiq-verbose-init-of-dma-core.patch | 50 + ...tiq-adds-xrx200-ethernet-clock-definition.patch | 29 + ...05-MIPS-lantiq-adds-code-for-booting-GPHY.patch | 89 + ...006-MIPS-lantiq-adds-GPHY-firmware-loader.patch | 144 + ...-honour-model-property-inside-devicetree-.patch | 59 + ...S-lantiq-adds-support-for-SVIP-SoC-Family.patch | 439 ++ ...0102-MIPS-lantiq-add-GPHY-clock-gate-bits.patch | 52 + ...03-MIPS-lantiq-adds-static-clock-for-PP32.patch | 219 + ...S-lantiq-adds-4dword-burst-length-for-dma.patch | 34 + ...0105-MIPS-lantiq-rework-external-irq-code.patch | 214 + ...0106-MIPS-lantiq-adds-minimal-dcdc-driver.patch | 106 + ...L-lantiq-pinconf-uses-port-instead-of-pin.patch | 92 + .../patches-3.7/0108-PINCTRL-lantiq-fixes.patch | 281 ++ ...-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch | 404 ++ ...vicetree-add-OF-documents-for-lantiq-seri.patch | 39 + ...ntiq-Add-NAND-support-on-Lantiq-FALCON-So.patch | 139 + ...ntiq-xway-fix-NAND-reset-timeout-handling.patch | 47 + ...2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch | 1047 +++++ .../0114-SPI-MIPS-lantiq-adds-spi-xway.patch | 1033 +++++ ...115-NET-PHY-adds-driver-for-lantiq-PHY11G.patch | 228 + ...-lantiq-update-etop-driver-for-devicetree.patch | 807 ++++ .../0117-NET-MIPS-lantiq-adds-xrx200-net.patch | 1414 ++++++ .../0118-owrt-adds-PHY11G-firmware-blobs.patch | 2221 +++++++++ .../0119-owrt-ATM-adds-lantiq-specific-hacks.patch | 508 +++ .../0120-owrt-generic-dtb-image-hack.patch | 26 + .../0121-owrt-lantiq-dtb-image-hack.patch | 44 + .../0122-MIPS-lantiq-adds-pcie-driver.patch | 4786 ++++++++++++++++++++ .../0123-USB-fix-roothub-for-IFXHCD.patch | 34 + target/linux/lantiq/patches-3.7/0124-pci_fix.patch | 22 + .../lantiq/patches-3.7/0300-owrt-mtd-split.patch | 230 + target/linux/lantiq/patches-3.7/0301-gptu.path | 1049 +++++ .../linux/lantiq/patches-3.7/0302-wifi-eep.patch | 319 ++ target/linux/lantiq/patches-3.7/0303-vmmc.patch | 79 + target/linux/lantiq/xway/config-default | 15 +- target/linux/lantiq/xway/profiles/arv.mk | 85 +- target/linux/lantiq/xway/profiles/avm.mk | 10 + target/linux/lantiq/xway/profiles/lantiq.mk | 22 +- target/linux/lantiq/xway/profiles/netgear.mk | 21 + target/linux/lantiq/xway/target.mk | 2 +- 50 files changed, 16717 insertions(+), 32 deletions(-) create mode 100644 target/linux/lantiq/config-3.7 create mode 100644 target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a1x.bin create mode 100644 target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a2x.bin create mode 100644 target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a1x.bin create mode 100644 target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a2x.bin create mode 100644 target/linux/lantiq/patches-3.7/0001-MIPS-lantiq-unbreak-devicetree-init.patch create mode 100644 target/linux/lantiq/patches-3.7/0002-MIPS-lantiq-fix-bootselect-bits-on-XRX200-SoC.patch create mode 100644 target/linux/lantiq/patches-3.7/0003-MIPS-lantiq-verbose-init-of-dma-core.patch create mode 100644 target/linux/lantiq/patches-3.7/0004-MIPS-lantiq-adds-xrx200-ethernet-clock-definition.patch create mode 100644 target/linux/lantiq/patches-3.7/0005-MIPS-lantiq-adds-code-for-booting-GPHY.patch create mode 100644 target/linux/lantiq/patches-3.7/0006-MIPS-lantiq-adds-GPHY-firmware-loader.patch create mode 100644 target/linux/lantiq/patches-3.7/0100-MIPS-lantiq-honour-model-property-inside-devicetree-.patch create mode 100644 target/linux/lantiq/patches-3.7/0101-MIPS-lantiq-adds-support-for-SVIP-SoC-Family.patch create mode 100644 target/linux/lantiq/patches-3.7/0102-MIPS-lantiq-add-GPHY-clock-gate-bits.patch create mode 100644 target/linux/lantiq/patches-3.7/0103-MIPS-lantiq-adds-static-clock-for-PP32.patch create mode 100644 target/linux/lantiq/patches-3.7/0104-MIPS-lantiq-adds-4dword-burst-length-for-dma.patch create mode 100644 target/linux/lantiq/patches-3.7/0105-MIPS-lantiq-rework-external-irq-code.patch create mode 100644 target/linux/lantiq/patches-3.7/0106-MIPS-lantiq-adds-minimal-dcdc-driver.patch create mode 100644 target/linux/lantiq/patches-3.7/0107-PINCTRL-lantiq-pinconf-uses-port-instead-of-pin.patch create mode 100644 target/linux/lantiq/patches-3.7/0108-PINCTRL-lantiq-fixes.patch create mode 100644 target/linux/lantiq/patches-3.7/0109-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch create mode 100644 target/linux/lantiq/patches-3.7/0110-Document-devicetree-add-OF-documents-for-lantiq-seri.patch create mode 100644 target/linux/lantiq/patches-3.7/0111-MTD-MIPS-lantiq-Add-NAND-support-on-Lantiq-FALCON-So.patch create mode 100644 target/linux/lantiq/patches-3.7/0112-MTD-lantiq-xway-fix-NAND-reset-timeout-handling.patch create mode 100644 target/linux/lantiq/patches-3.7/0113-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch create mode 100644 target/linux/lantiq/patches-3.7/0114-SPI-MIPS-lantiq-adds-spi-xway.patch create mode 100644 target/linux/lantiq/patches-3.7/0115-NET-PHY-adds-driver-for-lantiq-PHY11G.patch create mode 100644 target/linux/lantiq/patches-3.7/0116-NET-MIPS-lantiq-update-etop-driver-for-devicetree.patch create mode 100644 target/linux/lantiq/patches-3.7/0117-NET-MIPS-lantiq-adds-xrx200-net.patch create mode 100644 target/linux/lantiq/patches-3.7/0118-owrt-adds-PHY11G-firmware-blobs.patch create mode 100644 target/linux/lantiq/patches-3.7/0119-owrt-ATM-adds-lantiq-specific-hacks.patch create mode 100644 target/linux/lantiq/patches-3.7/0120-owrt-generic-dtb-image-hack.patch create mode 100644 target/linux/lantiq/patches-3.7/0121-owrt-lantiq-dtb-image-hack.patch create mode 100644 target/linux/lantiq/patches-3.7/0122-MIPS-lantiq-adds-pcie-driver.patch create mode 100644 target/linux/lantiq/patches-3.7/0123-USB-fix-roothub-for-IFXHCD.patch create mode 100644 target/linux/lantiq/patches-3.7/0124-pci_fix.patch create mode 100644 target/linux/lantiq/patches-3.7/0300-owrt-mtd-split.patch create mode 100644 target/linux/lantiq/patches-3.7/0301-gptu.path create mode 100644 target/linux/lantiq/patches-3.7/0302-wifi-eep.patch create mode 100644 target/linux/lantiq/patches-3.7/0303-vmmc.patch create mode 100644 target/linux/lantiq/xway/profiles/avm.mk create mode 100644 target/linux/lantiq/xway/profiles/netgear.mk (limited to 'target/linux/lantiq') diff --git a/target/linux/lantiq/Makefile b/target/linux/lantiq/Makefile index 1ab6dea1b3..a8e61709ba 100644 --- a/target/linux/lantiq/Makefile +++ b/target/linux/lantiq/Makefile @@ -12,8 +12,8 @@ FEATURES:=squashfs jffs2 SUBTARGETS:=danube ar9 vr9 falcon svip_be LINUX_VERSION:=3.3.8 -#SUBTARGETS=xway ase -#LINUX_VERSION:=3.6.10 +SUBTARGETS=xway ase falcon +LINUX_VERSION:=3.7-rc8 CFLAGS=-Os -pipe -mips32r2 -mtune=mips32r2 -fno-caller-saves diff --git a/target/linux/lantiq/ase/config-default b/target/linux/lantiq/ase/config-default index 48e9093287..79b559592a 100644 --- a/target/linux/lantiq/ase/config-default +++ b/target/linux/lantiq/ase/config-default @@ -1,11 +1,19 @@ +CONFIG_AR8216_PHY=y CONFIG_INPUT=y CONFIG_INPUT_EVDEV=y CONFIG_INPUT_POLLDEV=y # CONFIG_ISDN is not set +# CONFIG_LANTIQ_PHY is not set +# CONFIG_M25PXX_USE_FAST_READ is not set +CONFIG_MTD_M25P80=y +# CONFIG_RTL8366_SMI is not set CONFIG_SOC_AMAZON_SE=y # CONFIG_SOC_XWAY is not set +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_XWAY=y # CONFIG_USB_ARCH_HAS_EHCI is not set -# CONFIG_USB_ARCH_HAS_HCD is not set # CONFIG_USB_ARCH_HAS_OHCI is not set # CONFIG_USB_ARCH_HAS_XHCI is not set CONFIG_USB_SUPPORT=y diff --git a/target/linux/lantiq/config-3.7 b/target/linux/lantiq/config-3.7 new file mode 100644 index 0000000000..d4591cbea7 --- /dev/null +++ b/target/linux/lantiq/config-3.7 @@ -0,0 +1,165 @@ +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_CRYPTO_AES=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=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_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/falcon/config-default b/target/linux/lantiq/falcon/config-default index b12bdd835d..88a6f3c5f0 100644 --- a/target/linux/lantiq/falcon/config-default +++ b/target/linux/lantiq/falcon/config-default @@ -1,20 +1,19 @@ -# CONFIG_ATMEL_PWM is not set -CONFIG_CLKDEV_LOOKUP=y -CONFIG_FSNOTIFY=y -CONFIG_HAVE_MACH_CLKDEV=y -CONFIG_IRQ_FORCED_THREADING=y +CONFIG_GPIO_FALCON=y +CONFIG_GPIO_GENERIC=y CONFIG_M25PXX_USE_FAST_READ=y -CONFIG_MDIO_BOARDINFO=y CONFIG_MTD_M25P80=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_FALCON=y CONFIG_MTD_NAND_PLATFORM=y # CONFIG_MTD_SM_COMMON is not set -# CONFIG_SOC_AMAZON_SE is not set +CONFIG_PINCTRL_FALCON=y CONFIG_SOC_FALCON=y -# CONFIG_SOC_SVIP is not set # CONFIG_SOC_TYPE_XWAY is not set # CONFIG_SOC_XWAY is not set CONFIG_SPI=y CONFIG_SPI_FALCON=y CONFIG_SPI_MASTER=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set diff --git a/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a1x.bin b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a1x.bin new file mode 100644 index 0000000000..cdf3d30634 Binary files /dev/null and b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a1x.bin differ diff --git a/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a2x.bin b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a2x.bin new file mode 100644 index 0000000000..44fc39ef06 Binary files /dev/null and b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy11g_a2x.bin differ diff --git a/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a1x.bin b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a1x.bin new file mode 100644 index 0000000000..02b88a078b Binary files /dev/null and b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a1x.bin differ diff --git a/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a2x.bin b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a2x.bin new file mode 100644 index 0000000000..1fed6ad65e Binary files /dev/null and b/target/linux/lantiq/files-3.7/firmware/lantiq/vr9_phy22f_a2x.bin differ diff --git a/target/linux/lantiq/image/Makefile b/target/linux/lantiq/image/Makefile index ef289823dd..f0f4d4f8ed 100644 --- a/target/linux/lantiq/image/Makefile +++ b/target/linux/lantiq/image/Makefile @@ -21,12 +21,8 @@ endef define PatchKernelLzma cp $(KDIR)/vmlinux $(KDIR)/vmlinux-$(1) -ifeq ($(CONFIG_LINUX_3_6),y) $(LINUX_DIR)/scripts/dtc/dtc -O dtb -o $(KDIR)/$(1).dtb ./$(1).dts $(STAGING_DIR_HOST)/bin/patch-dtb $(KDIR)/vmlinux-$(1) $(KDIR)/$(1).dtb -else - $(STAGING_DIR_HOST)/bin/patch-cmdline $(KDIR)/vmlinux-$(1) '$(strip $(2))' -endif $(call CompressLzma,$(KDIR)/vmlinux-$(1),$(KDIR)/vmlinux-$(1).lzma) endef diff --git a/target/linux/lantiq/patches-3.7/0001-MIPS-lantiq-unbreak-devicetree-init.patch b/target/linux/lantiq/patches-3.7/0001-MIPS-lantiq-unbreak-devicetree-init.patch new file mode 100644 index 0000000000..e3ffdc1902 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0001-MIPS-lantiq-unbreak-devicetree-init.patch @@ -0,0 +1,41 @@ +From a15d129a352e5f6ab821b81bc3f692ecc952a815 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 9 Nov 2012 12:09:57 +0100 +Subject: [PATCH 1/6] MIPS: lantiq: unbreak devicetree init + +The bootmem was incorrectly freed resulting in lots of dangling pointers. +Additionally we should use of_platform_populate() as the Documentaion tells us +to do so. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/4518 +--- + arch/mips/lantiq/prom.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c +index 6cfd611..9f9e875 100644 +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -87,9 +87,6 @@ void __init device_tree_init(void) + reserve_bootmem(base, size, BOOTMEM_DEFAULT); + + unflatten_device_tree(); +- +- /* free the space reserved for the dt blob */ +- free_bootmem(base, size); + } + + void __init prom_init(void) +@@ -119,7 +116,7 @@ int __init plat_of_setup(void) + sizeof(of_ids[0].compatible)); + strncpy(of_ids[1].compatible, "simple-bus", + sizeof(of_ids[1].compatible)); +- return of_platform_bus_probe(NULL, of_ids, NULL); ++ return of_platform_populate(NULL, of_ids, NULL, NULL); + } + + arch_initcall(plat_of_setup); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0002-MIPS-lantiq-fix-bootselect-bits-on-XRX200-SoC.patch b/target/linux/lantiq/patches-3.7/0002-MIPS-lantiq-fix-bootselect-bits-on-XRX200-SoC.patch new file mode 100644 index 0000000000..2a305b4afe --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0002-MIPS-lantiq-fix-bootselect-bits-on-XRX200-SoC.patch @@ -0,0 +1,71 @@ +From 15753b6586710d788f36cfd5fbb98d0805b390ab Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 9 Nov 2012 13:31:51 +0100 +Subject: [PATCH 2/6] MIPS: lantiq: fix bootselect bits on XRX200 SoC + +The XRX200 SoC family has a different register layout for reading the boot +selection bits. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/4519 +--- + arch/mips/lantiq/xway/reset.c | 22 +++++++++++++++------- + 1 file changed, 15 insertions(+), 7 deletions(-) + +diff --git a/arch/mips/lantiq/xway/reset.c b/arch/mips/lantiq/xway/reset.c +index 22c55f7..2799212 100644 +--- a/arch/mips/lantiq/xway/reset.c ++++ b/arch/mips/lantiq/xway/reset.c +@@ -34,11 +34,12 @@ + /* reset cause */ + #define RCU_STAT_SHIFT 26 + /* boot selection */ +-#define RCU_BOOT_SEL_SHIFT 26 +-#define RCU_BOOT_SEL_MASK 0x7 ++#define RCU_BOOT_SEL(x) ((x >> 18) & 0x7) ++#define RCU_BOOT_SEL_XRX200(x) (((x >> 17) & 0xf) | ((x >> 8) & 0x10)) + + /* remapped base addr of the reset control unit */ + static void __iomem *ltq_rcu_membase; ++static struct device_node *ltq_rcu_np; + + /* This function is used by the watchdog driver */ + int ltq_reset_cause(void) +@@ -52,7 +53,11 @@ EXPORT_SYMBOL_GPL(ltq_reset_cause); + unsigned char ltq_boot_select(void) + { + u32 val = ltq_rcu_r32(RCU_RST_STAT); +- return (val >> RCU_BOOT_SEL_SHIFT) & RCU_BOOT_SEL_MASK; ++ ++ if (of_device_is_compatible(ltq_rcu_np, "lantiq,rcu-xrx200")) ++ return RCU_BOOT_SEL_XRX200(val); ++ ++ return RCU_BOOT_SEL(val); + } + + /* reset a io domain for u micro seconds */ +@@ -85,14 +90,17 @@ static void ltq_machine_power_off(void) + static int __init mips_reboot_setup(void) + { + struct resource res; +- struct device_node *np = +- of_find_compatible_node(NULL, NULL, "lantiq,rcu-xway"); ++ ++ ltq_rcu_np = of_find_compatible_node(NULL, NULL, "lantiq,rcu-xway"); ++ if (!ltq_rcu_np) ++ ltq_rcu_np = of_find_compatible_node(NULL, NULL, ++ "lantiq,rcu-xrx200"); + + /* check if all the reset register range is available */ +- if (!np) ++ if (!ltq_rcu_np) + panic("Failed to load reset resources from devicetree"); + +- if (of_address_to_resource(np, 0, &res)) ++ if (of_address_to_resource(ltq_rcu_np, 0, &res)) + panic("Failed to get rcu memory range"); + + if (request_mem_region(res.start, resource_size(&res), res.name) < 0) +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0003-MIPS-lantiq-verbose-init-of-dma-core.patch b/target/linux/lantiq/patches-3.7/0003-MIPS-lantiq-verbose-init-of-dma-core.patch new file mode 100644 index 0000000000..597cbc6f08 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0003-MIPS-lantiq-verbose-init-of-dma-core.patch @@ -0,0 +1,50 @@ +From b8b3acbe6077b4736f641ec445be8a42cdd1f08b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 9 Nov 2012 12:16:14 +0100 +Subject: [PATCH 3/6] MIPS: lantiq: verbose init of dma core + +Print the hardware revision and port/channel info when starting the dma core. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/4520 +--- + arch/mips/lantiq/xway/dma.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c +index 55d2c4f..b5d76d1 100644 +--- a/arch/mips/lantiq/xway/dma.c ++++ b/arch/mips/lantiq/xway/dma.c +@@ -25,6 +25,7 @@ + #include + #include + ++#define LTQ_DMA_ID 0x08 + #define LTQ_DMA_CTRL 0x10 + #define LTQ_DMA_CPOLL 0x14 + #define LTQ_DMA_CS 0x18 +@@ -214,6 +215,7 @@ ltq_dma_init(struct platform_device *pdev) + { + struct clk *clk; + struct resource *res; ++ unsigned id; + int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +@@ -243,7 +245,12 @@ ltq_dma_init(struct platform_device *pdev) + ltq_dma_w32(DMA_POLL | DMA_CLK_DIV4, LTQ_DMA_CPOLL); + ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); + } +- dev_info(&pdev->dev, "init done\n"); ++ ++ id = ltq_dma_r32(LTQ_DMA_ID); ++ dev_info(&pdev->dev, ++ "Init done - hw rev: %X, ports: %d, channels: %d\n", ++ id & 0x1f, (id >> 16) & 0xf, id >> 20); ++ + return 0; + } + +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0004-MIPS-lantiq-adds-xrx200-ethernet-clock-definition.patch b/target/linux/lantiq/patches-3.7/0004-MIPS-lantiq-adds-xrx200-ethernet-clock-definition.patch new file mode 100644 index 0000000000..771dd4f1b4 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0004-MIPS-lantiq-adds-xrx200-ethernet-clock-definition.patch @@ -0,0 +1,29 @@ +From f2bbe41c507b475c6f0ae1fca69c7aac6d31d228 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 9 Nov 2012 13:34:18 +0100 +Subject: [PATCH 4/6] MIPS: lantiq: adds xrx200 ethernet clock definition + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/4521 +--- + arch/mips/lantiq/xway/sysctrl.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 2917b56..3925e66 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -370,6 +370,10 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("1d900000.pcie", "pdi", 1, PMU1_PCIE_PDI); + clkdev_add_pmu("1d900000.pcie", "ctl", 1, PMU1_PCIE_CTL); + clkdev_add_pmu("1d900000.pcie", "ahb", 0, PMU_AHBM | PMU_AHBS); ++ clkdev_add_pmu("1e108000.eth", NULL, 0, ++ PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM | ++ PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | ++ 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()); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0005-MIPS-lantiq-adds-code-for-booting-GPHY.patch b/target/linux/lantiq/patches-3.7/0005-MIPS-lantiq-adds-code-for-booting-GPHY.patch new file mode 100644 index 0000000000..84aa965231 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0005-MIPS-lantiq-adds-code-for-booting-GPHY.patch @@ -0,0 +1,89 @@ +From af14a456c58c153c6d761e6c0af48157692b52ad Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 9 Nov 2012 13:43:30 +0100 +Subject: [PATCH 5/6] MIPS: lantiq: adds code for booting GPHY + +The XRX200 family of SoCs has embedded gigabit PHYs. This patch adds code to +boot them up. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/4522 +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 3 ++ + arch/mips/lantiq/xway/reset.c | 36 ++++++++++++++++++++ + 2 files changed, 39 insertions(+) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 6a2df70..133336b 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -82,6 +82,9 @@ extern __iomem void *ltq_cgu_membase; + #define LTQ_MPS_BASE_ADDR (KSEG1 + 0x1F107000) + #define LTQ_MPS_CHIPID ((u32 *)(LTQ_MPS_BASE_ADDR + 0x0344)) + ++/* allow booting xrx200 phys */ ++int xrx200_gphy_boot(struct device *dev, unsigned int id, dma_addr_t dev_addr); ++ + /* request a non-gpio and set the PIO config */ + #define PMU_PPE BIT(13) + extern void ltq_pmu_enable(unsigned int module); +diff --git a/arch/mips/lantiq/xway/reset.c b/arch/mips/lantiq/xway/reset.c +index 2799212..544dbb7 100644 +--- a/arch/mips/lantiq/xway/reset.c ++++ b/arch/mips/lantiq/xway/reset.c +@@ -28,9 +28,15 @@ + #define RCU_RST_REQ 0x0010 + /* reset status register */ + #define RCU_RST_STAT 0x0014 ++/* vr9 gphy registers */ ++#define RCU_GFS_ADD0_XRX200 0x0020 ++#define RCU_GFS_ADD1_XRX200 0x0068 + + /* reboot bit */ ++#define RCU_RD_GPHY0_XRX200 BIT(31) + #define RCU_RD_SRST BIT(30) ++#define RCU_RD_GPHY1_XRX200 BIT(29) ++ + /* reset cause */ + #define RCU_STAT_SHIFT 26 + /* boot selection */ +@@ -60,6 +66,36 @@ unsigned char ltq_boot_select(void) + return RCU_BOOT_SEL(val); + } + ++/* reset / boot a gphy */ ++static struct ltq_xrx200_gphy_reset { ++ u32 rd; ++ u32 addr; ++} xrx200_gphy[] = { ++ {RCU_RD_GPHY0_XRX200, RCU_GFS_ADD0_XRX200}, ++ {RCU_RD_GPHY1_XRX200, RCU_GFS_ADD1_XRX200}, ++}; ++ ++/* reset and boot a gphy. these phys only exist on xrx200 SoC */ ++int xrx200_gphy_boot(struct device *dev, unsigned int id, dma_addr_t dev_addr) ++{ ++ if (!of_device_is_compatible(ltq_rcu_np, "lantiq,rcu-xrx200")) { ++ dev_err(dev, "this SoC has no GPHY\n"); ++ return -EINVAL; ++ } ++ if (id > 1) { ++ dev_err(dev, "%u is an invalid gphy id\n", id); ++ return -EINVAL; ++ } ++ dev_info(dev, "booting GPHY%u firmware at %X\n", id, dev_addr); ++ ++ ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) | xrx200_gphy[id].rd, ++ RCU_RST_REQ); ++ ltq_rcu_w32(dev_addr, xrx200_gphy[id].addr); ++ ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) & ~xrx200_gphy[id].rd, ++ RCU_RST_REQ); ++ return 0; ++} ++ + /* reset a io domain for u micro seconds */ + void ltq_reset_once(unsigned int module, ulong u) + { +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0006-MIPS-lantiq-adds-GPHY-firmware-loader.patch b/target/linux/lantiq/patches-3.7/0006-MIPS-lantiq-adds-GPHY-firmware-loader.patch new file mode 100644 index 0000000000..56e43d552a --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0006-MIPS-lantiq-adds-GPHY-firmware-loader.patch @@ -0,0 +1,144 @@ +From 0224cde212df4abf251f89c3724a800b1949a774 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Oct 2012 07:52:50 +0200 +Subject: [PATCH 6/6] MIPS: lantiq: adds GPHY firmware loader + +The internal GPHYs need a firmware blob to function properly. This patch adds +the code needed to request the blob and load it to the PHY. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/4523 +--- + arch/mips/lantiq/Kconfig | 4 ++ + arch/mips/lantiq/xway/Makefile | 2 + + arch/mips/lantiq/xway/xrx200_phy_fw.c | 97 +++++++++++++++++++++++++++++++++ + 3 files changed, 103 insertions(+) + create mode 100644 arch/mips/lantiq/xway/xrx200_phy_fw.c + +diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig +index d84f361..c002191 100644 +--- a/arch/mips/lantiq/Kconfig ++++ b/arch/mips/lantiq/Kconfig +@@ -36,4 +36,8 @@ config PCI_LANTIQ + bool "PCI Support" + depends on SOC_XWAY && PCI + ++config XRX200_PHY_FW ++ bool "XRX200 PHY firmware loader" ++ depends on SOC_XWAY ++ + endif +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index 70a58c7..7a13660 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1 +1,3 @@ + obj-y := prom.o sysctrl.o clk.o reset.o dma.o gptu.o ++ ++obj-$(CONFIG_XRX200_PHY_FW) += xrx200_phy_fw.o +diff --git a/arch/mips/lantiq/xway/xrx200_phy_fw.c b/arch/mips/lantiq/xway/xrx200_phy_fw.c +new file mode 100644 +index 0000000..fe808bf +--- /dev/null ++++ b/arch/mips/lantiq/xway/xrx200_phy_fw.c +@@ -0,0 +1,97 @@ ++/* ++ * 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 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define XRX200_GPHY_FW_ALIGN (16 * 1024) ++ ++static dma_addr_t xway_gphy_load(struct platform_device *pdev) ++{ ++ const struct firmware *fw; ++ dma_addr_t dev_addr = 0; ++ const char *fw_name; ++ void *fw_addr; ++ size_t size; ++ ++ if (of_property_read_string(pdev->dev.of_node, "firmware", &fw_name)) { ++ dev_err(&pdev->dev, "failed to load firmware filename\n"); ++ return 0; ++ } ++ ++ dev_info(&pdev->dev, "requesting %s\n", fw_name); ++ if (request_firmware(&fw, fw_name, &pdev->dev)) { ++ dev_err(&pdev->dev, "failed to load firmware: %s\n", fw_name); ++ return 0; ++ } ++ ++ /* ++ * GPHY cores need the firmware code in a persistent and contiguous ++ * memory area with a 16 kB boundary aligned start address ++ */ ++ size = fw->size + XRX200_GPHY_FW_ALIGN; ++ ++ fw_addr = dma_alloc_coherent(&pdev->dev, size, &dev_addr, GFP_KERNEL); ++ if (fw_addr) { ++ fw_addr = PTR_ALIGN(fw_addr, XRX200_GPHY_FW_ALIGN); ++ dev_addr = ALIGN(dev_addr, XRX200_GPHY_FW_ALIGN); ++ memcpy(fw_addr, fw->data, fw->size); ++ } else { ++ dev_err(&pdev->dev, "failed to alloc firmware memory\n"); ++ } ++ ++ release_firmware(fw); ++ return dev_addr; ++} ++ ++static int __devinit xway_phy_fw_probe(struct platform_device *pdev) ++{ ++ dma_addr_t fw_addr; ++ struct property *pp; ++ unsigned char *phyids; ++ int i, ret = 0; ++ ++ fw_addr = xway_gphy_load(pdev); ++ if (!fw_addr) ++ return -EINVAL; ++ pp = of_find_property(pdev->dev.of_node, "phys", NULL); ++ if (!pp) ++ return -ENOENT; ++ phyids = pp->value; ++ for (i = 0; i < pp->length && !ret; i++) ++ ret = xrx200_gphy_boot(&pdev->dev, phyids[i], fw_addr); ++ if (!ret) ++ mdelay(100); ++ return ret; ++} ++ ++static const struct of_device_id xway_phy_match[] = { ++ { .compatible = "lantiq,phy-xrx200" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, xway_phy_match); ++ ++static struct platform_driver xway_phy_driver = { ++ .probe = xway_phy_fw_probe, ++ .driver = { ++ .name = "phy-xrx200", ++ .owner = THIS_MODULE, ++ .of_match_table = xway_phy_match, ++ }, ++}; ++ ++module_platform_driver(xway_phy_driver); ++ ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("Lantiq XRX200 PHY Firmware Loader"); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0100-MIPS-lantiq-honour-model-property-inside-devicetree-.patch b/target/linux/lantiq/patches-3.7/0100-MIPS-lantiq-honour-model-property-inside-devicetree-.patch new file mode 100644 index 0000000000..cef266fbc9 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0100-MIPS-lantiq-honour-model-property-inside-devicetree-.patch @@ -0,0 +1,59 @@ +From 60bc3043590bf74ca1c9dd88a4e5f28a40d5b348 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 6 Dec 2012 10:26:05 +0100 +Subject: [PATCH 100/123] MIPS: lantiq: honour model property inside + devicetree during board init + +--- + arch/mips/lantiq/prom.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c +index 9f9e875..fa12dc1 100644 +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -57,6 +57,21 @@ static void __init prom_init_cmdline(void) + } + } + ++int __init early_init_dt_scan_model(unsigned long node, ++ const char *uname, int depth, ++ void *data) ++{ ++ if (!depth) { ++ char *model = of_get_flat_dt_prop(node, "model", NULL); ++ if (model) { ++ pr_info("Board: %s\n", model); ++ snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN, "%s - %s", ++ soc_info.sys_type, model); ++ } ++ } ++ return 0; ++} ++ + void __init plat_mem_setup(void) + { + ioport_resource.start = IOPORT_RESOURCE_START; +@@ -71,6 +86,8 @@ void __init plat_mem_setup(void) + * parsed resulting in our memory appearing + */ + __dt_setup_arch(&__dtb_start); ++ ++ of_scan_flat_dt(early_init_dt_scan_model, NULL); + } + + void __init device_tree_init(void) +@@ -93,9 +110,8 @@ void __init prom_init(void) + { + /* call the soc specific detetcion code and get it to fill soc_info */ + ltq_soc_detect(&soc_info); +- snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN - 1, "%s rev %s", ++ snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN, "%s rev %s", + soc_info.name, soc_info.rev_type); +- soc_info.sys_type[LTQ_SYS_TYPE_LEN - 1] = '\0'; + pr_info("SoC: %s\n", soc_info.sys_type); + prom_init_cmdline(); + +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0101-MIPS-lantiq-adds-support-for-SVIP-SoC-Family.patch b/target/linux/lantiq/patches-3.7/0101-MIPS-lantiq-adds-support-for-SVIP-SoC-Family.patch new file mode 100644 index 0000000000..0de4005d6f --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0101-MIPS-lantiq-adds-support-for-SVIP-SoC-Family.patch @@ -0,0 +1,439 @@ +From 4d77ad216ad86b3b25c196a189fa28f3e53c3ffd Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 30 Nov 2012 21:32:00 +0100 +Subject: [PATCH 101/123] MIPS: lantiq: adds support for SVIP SoC Family + +--- + arch/mips/kernel/cevt-r4k.c | 4 +- + arch/mips/lantiq/Kconfig | 4 ++ + arch/mips/lantiq/Makefile | 1 + + arch/mips/lantiq/Platform | 1 + + arch/mips/lantiq/clk.c | 7 +++ + arch/mips/lantiq/clk.h | 4 ++ + arch/mips/lantiq/svip/Makefile | 1 + + arch/mips/lantiq/svip/clk.c | 70 ++++++++++++++++++++++++++ + arch/mips/lantiq/svip/prom.c | 43 ++++++++++++++++ + arch/mips/lantiq/svip/reset.c | 105 +++++++++++++++++++++++++++++++++++++++ + arch/mips/lantiq/svip/sysctrl.c | 81 ++++++++++++++++++++++++++++++ + 11 files changed, 320 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/lantiq/svip/Makefile + create mode 100644 arch/mips/lantiq/svip/clk.c + create mode 100644 arch/mips/lantiq/svip/prom.c + create mode 100644 arch/mips/lantiq/svip/reset.c + create mode 100644 arch/mips/lantiq/svip/sysctrl.c + +diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c +index 7532392..06b45e4 100644 +--- a/arch/mips/kernel/cevt-r4k.c ++++ b/arch/mips/kernel/cevt-r4k.c +@@ -176,8 +176,10 @@ int __cpuinit r4k_clockevent_init(void) + if (!cpu_has_counter || !mips_hpt_frequency) + return -ENXIO; + +- if (!c0_compare_int_usable()) ++ if (!c0_compare_int_usable()) { ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); + return -ENXIO; ++ } + + /* + * With vectored interrupts things are getting platform specific. +diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig +index c002191..edeb58c 100644 +--- a/arch/mips/lantiq/Kconfig ++++ b/arch/mips/lantiq/Kconfig +@@ -22,6 +22,10 @@ config SOC_FALCON + bool "FALCON" + select PINCTRL_FALCON + ++config SOC_SVIP ++ bool "SVIP" ++ select MIPS_CPU_SCACHE ++ + endchoice + + choice +diff --git a/arch/mips/lantiq/Makefile b/arch/mips/lantiq/Makefile +index d6bdc57..edeb30b 100644 +--- a/arch/mips/lantiq/Makefile ++++ b/arch/mips/lantiq/Makefile +@@ -12,3 +12,4 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + + obj-$(CONFIG_SOC_TYPE_XWAY) += xway/ + obj-$(CONFIG_SOC_FALCON) += falcon/ ++obj-$(CONFIG_SOC_SVIP) += svip/ +diff --git a/arch/mips/lantiq/Platform b/arch/mips/lantiq/Platform +index b3ec498..857548c 100644 +--- a/arch/mips/lantiq/Platform ++++ b/arch/mips/lantiq/Platform +@@ -7,3 +7,4 @@ cflags-$(CONFIG_LANTIQ) += -I$(srctree)/arch/mips/include/asm/mach-lantiq + load-$(CONFIG_LANTIQ) = 0xffffffff80002000 + cflags-$(CONFIG_SOC_TYPE_XWAY) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/xway + cflags-$(CONFIG_SOC_FALCON) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/falcon ++cflags-$(CONFIG_SOC_SVIP) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/svip +diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c +index ce2f129..9128ff8 100644 +--- a/arch/mips/lantiq/clk.c ++++ b/arch/mips/lantiq/clk.c +@@ -163,8 +163,15 @@ void __init plat_time_init(void) + ltq_soc_init(); + + clk = clk_get_cpu(); ++#ifdef CONFIG_SOC_SVIP ++ mips_hpt_frequency = ltq_svip_cpu_hz() / get_counter_resolution(); ++ write_c0_count(0); ++ write_c0_compare(mips_hpt_frequency / HZ); ++ enable_irq(MIPS_CPU_TIMER_IRQ); ++#else + mips_hpt_frequency = clk_get_rate(clk) / get_counter_resolution(); + write_c0_compare(read_c0_count()); ++#endif + pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000); + clk_put(clk); + } +diff --git a/arch/mips/lantiq/clk.h b/arch/mips/lantiq/clk.h +index fa67060..c169e2b 100644 +--- a/arch/mips/lantiq/clk.h ++++ b/arch/mips/lantiq/clk.h +@@ -75,4 +75,8 @@ 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_svip_cpu_hz(void); ++extern unsigned long ltq_svip_fpi_hz(void); ++extern unsigned long ltq_svip_io_hz(void); ++ + #endif +diff --git a/arch/mips/lantiq/svip/Makefile b/arch/mips/lantiq/svip/Makefile +new file mode 100644 +index 0000000..74308b2 +--- /dev/null ++++ b/arch/mips/lantiq/svip/Makefile +@@ -0,0 +1 @@ ++obj-y := prom.o reset.o sysctrl.o clk.o +diff --git a/arch/mips/lantiq/svip/clk.c b/arch/mips/lantiq/svip/clk.c +new file mode 100644 +index 0000000..3a7b665 +--- /dev/null ++++ b/arch/mips/lantiq/svip/clk.c +@@ -0,0 +1,70 @@ ++/* ++ * 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) 2010 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "../clk.h" ++ ++#define STATUS_CONFIG_CLK_MODE (0x1 << 1) ++#define STATUS_CONFIG_CLK_MODE_GET(val) ((((val) & STATUS_CONFIG_CLK_MODE) >> 4) & 0x1) ++#define STATUS_CONFIG 0x0010 ++ ++#define SYS0_PLL1CR_PLLDIV (0x3) ++#define SYS0_PLL1CR_PLLDIV_GET(val) ((((val) & SYS0_PLL1CR_PLLDIV) >> 0) & 0x3) ++#define SYS0_PLL1CR 0x0008 ++ ++#define SYS1_FPICR_FPIDIV (0x1) ++#define SYS1_FPICR_FPIDIV_GET(val) ((((val) & SYS1_FPICR_FPIDIV) >> 0) & 0x1) ++#define SYS1_FPICR 0x0014 ++ ++unsigned long ltq_svip_io_hz(void) ++{ ++ return 200000000; /* 200 MHz */ ++} ++ ++unsigned long ltq_svip_cpu_hz(void) ++{ ++ /* Magic BootROM speed location... */ ++ if ((*(u32 *)0x9fc07ff0) == 1) ++ return *(u32 *)0x9fc07ff4; ++ ++ if (STATUS_CONFIG_CLK_MODE_GET(ltq_status_r32(STATUS_CONFIG)) == 1) { ++ /* xT16 */ ++ return 393216000; ++ } else { ++ switch (SYS0_PLL1CR_PLLDIV_GET(ltq_sys0_r32(SYS0_PLL1CR))) { ++ case 3: ++ return 475000000; ++ case 2: ++ return 450000000; ++ case 1: ++ return 425000000; ++ default: ++ break; ++ } ++ } ++ return 400000000; ++} ++ ++unsigned long ltq_svip_fpi_hz(void) ++{ ++ u32 fbs0_div[2] = {4, 8}; ++ u32 div; ++ ++ div = SYS1_FPICR_FPIDIV_GET(ltq_sys1_r32(SYS1_FPICR)); ++ return ltq_svip_cpu_hz() / fbs0_div[div]; ++} +diff --git a/arch/mips/lantiq/svip/prom.c b/arch/mips/lantiq/svip/prom.c +new file mode 100644 +index 0000000..01d2018 +--- /dev/null ++++ b/arch/mips/lantiq/svip/prom.c +@@ -0,0 +1,43 @@ ++/* ++ * 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 ++ * Copyright (C) 2012 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "../prom.h" ++ ++#define SOC_SVIP "SVIP" ++ ++#define COMP_SVIP "lantiq,svip" ++ ++#define PART_SHIFT 12 ++#define PART_MASK 0x0FFFF000 ++#define REV_SHIFT 28 ++#define REV_MASK 0xF0000000 ++ ++void __init ltq_soc_detect(struct ltq_soc_info *i) ++{ ++ i->partnum = (ltq_r32(LTQ_STATUS_CHIPID) & PART_MASK) >> PART_SHIFT; ++ i->rev = (ltq_r32(LTQ_STATUS_CHIPID) & REV_MASK) >> REV_SHIFT; ++ sprintf(i->rev_type, "1.%d", i->rev); ++ ++ switch (i->partnum) { ++ case SOC_ID_SVIP: ++ i->name = SOC_SVIP; ++ i->type = SOC_TYPE_SVIP; ++ i->compatible = COMP_SVIP; ++ break; ++ ++ default: ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ break; ++ } ++} +diff --git a/arch/mips/lantiq/svip/reset.c b/arch/mips/lantiq/svip/reset.c +new file mode 100644 +index 0000000..4b41dd1 +--- /dev/null ++++ b/arch/mips/lantiq/svip/reset.c +@@ -0,0 +1,105 @@ ++/* ++ * 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 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define CPLD_CMDREG3 ((volatile unsigned char*)(KSEG1 + 0x120000f3)) ++ ++#define LTQ_EBU_ADDRSEL2 0x0028 ++#define LTQ_EBU_BUSCON2 0x0068 ++#define LTQ_BOOT_CPU_OFFSET 0x20 ++ ++#define LTQ_L2_SPRAM_BASE (KSEG1 + 0x1F1E8000) ++#define LTQ_BOOT_RVEC(cpu) (volatile u32*)(LTQ_L2_SPRAM_BASE + \ ++ (cpu * LTQ_BOOT_CPU_OFFSET)) ++ ++#define SYS0_BCR 0x0004 ++#define BMODE_SHIFT 16 ++#define BMODE_MASK 0x1f ++ ++#define SYS1_CLKCLR 0x0008 ++#define SYS1_RREQR 0x0044 ++#define SYS1_RRLSR 0x0048 ++#define SYS1_RBTR 0x004c ++#define SYS1_CPU0RSR 0x0060 ++#define SYS1_CPU0RSR_MASK 0x0007 ++ ++/* This function is used by the watchdog driver */ ++int ltq_reset_cause(void) ++{ ++ return ltq_sys1_r32(SYS1_CPU0RSR) & SYS1_CPU0RSR_MASK; ++} ++EXPORT_SYMBOL_GPL(ltq_reset_cause); ++ ++/* allow platform code to find out what source we booted from */ ++unsigned char ltq_boot_select(void) ++{ ++ return (ltq_sys0_r32(SYS0_BCR) >> BMODE_SHIFT) & BMODE_MASK; ++} ++ ++static void ltq_machine_restart(char *command) ++{ ++ local_irq_disable(); ++ if (/*mips_machtype == LANTIQ_MACH_EASY33016 || ++ mips_machtype == LANTIQ_MACH_EASY336)*/ ++ 1) { ++ /* We just use the CPLD function to reset the entire system as a ++ workaround for the switch reset problem */ ++ local_irq_disable(); ++ ltq_ebu_w32(0x120000f1, LTQ_EBU_ADDRSEL2); ++ ltq_ebu_w32(0x404027ff, LTQ_EBU_BUSCON2); ++ ++ if (/*mips_machtype == LANTIQ_MACH_EASY336*/ ++ 0) ++ /* set bit 0 to reset SVIP */ ++ *CPLD_CMDREG3 = (1<<0); ++ else ++ /* set bit 7 to reset SVIP, set bit 3 to reset xT */ ++ *CPLD_CMDREG3 = (1<<7) | (1<<3); ++ } else { ++ *LTQ_BOOT_RVEC(0) = 0; ++ /* reset all except PER, SUBSYS and CPU0 */ ++ ltq_sys1_w32(0x00043F3E, SYS1_RREQR); ++ /* release WDT0 reset */ ++ ltq_sys1_w32(0x00000100, SYS1_RRLSR); ++ /* restore reset value for clock enables */ ++ ltq_sys1_w32(~0x0c000040, SYS1_CLKCLR); ++ /* reset SUBSYS (incl. DDR2) and CPU0 */ ++ ltq_sys1_w32(0x00030001, SYS1_RBTR); ++ } ++ ++ unreachable(); ++} ++ ++static void ltq_machine_halt(void) ++{ ++ local_irq_disable(); ++ unreachable(); ++} ++ ++static void ltq_machine_power_off(void) ++{ ++ local_irq_disable(); ++} ++ ++static int __init mips_reboot_setup(void) ++{ ++ _machine_restart = ltq_machine_restart; ++ _machine_halt = ltq_machine_halt; ++ pm_power_off = ltq_machine_power_off; ++ return 0; ++} ++ ++arch_initcall(mips_reboot_setup); +diff --git a/arch/mips/lantiq/svip/sysctrl.c b/arch/mips/lantiq/svip/sysctrl.c +new file mode 100644 +index 0000000..d2b636b +--- /dev/null ++++ b/arch/mips/lantiq/svip/sysctrl.c +@@ -0,0 +1,81 @@ ++/* ++ * 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) 2011-2012 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "../clk.h" ++#include "../prom.h" ++ ++void __iomem *ltq_sys0_membase; ++void __iomem *ltq_sys1_membase; ++void __iomem *ltq_sys2_membase; ++void __iomem *ltq_status_membase; ++void __iomem *ltq_ebu_membase; ++ ++/* bring up all register ranges that we need for basic system control */ ++void __init ltq_soc_init(void) ++{ ++ struct resource res_sys0, res_sys1, res_sys2, res_status, res_ebu; ++ struct device_node *np_sys0 = ++ of_find_compatible_node(NULL, NULL, "lantiq,sys0-svip"); ++ struct device_node *np_sys1 = ++ of_find_compatible_node(NULL, NULL, "lantiq,sys1-svip"); ++ struct device_node *np_sys2 = ++ of_find_compatible_node(NULL, NULL, "lantiq,sys2-svip"); ++ struct device_node *np_status = ++ of_find_compatible_node(NULL, NULL, "lantiq,status-svip"); ++ struct device_node *np_ebu = ++ of_find_compatible_node(NULL, NULL, "lantiq,ebu-svip"); ++ ++ /* check if all the core register ranges are available */ ++ if (!np_sys0 || !np_sys1 || !np_sys2 || !np_status || !np_ebu) ++ panic("Failed to load core nodes from devicetree"); ++ ++ if (of_address_to_resource(np_sys0, 0, &res_sys0) || ++ of_address_to_resource(np_sys1, 0, &res_sys1) || ++ of_address_to_resource(np_sys2, 0, &res_sys2) || ++ of_address_to_resource(np_status, 0, &res_status) || ++ of_address_to_resource(np_ebu, 0, &res_ebu)) ++ panic("Failed to get core resources"); ++ ++ if ((request_mem_region(res_sys0.start, resource_size(&res_sys0), ++ res_sys0.name) < 0) || ++ (request_mem_region(res_sys1.start, resource_size(&res_sys1), ++ res_sys1.name) < 0) || ++ (request_mem_region(res_sys2.start, resource_size(&res_sys2), ++ res_sys2.name) < 0) || ++ (request_mem_region(res_status.start, resource_size(&res_status), ++ res_status.name) < 0) || ++ (request_mem_region(res_ebu.start, resource_size(&res_ebu), ++ res_ebu.name) < 0)) ++ pr_err("Failed to request core reources"); ++ ++ ltq_sys0_membase = ioremap_nocache(res_sys0.start, ++ resource_size(&res_sys0)); ++ ltq_sys1_membase = ioremap_nocache(res_sys1.start, ++ resource_size(&res_sys1)); ++ ltq_sys2_membase = ioremap_nocache(res_sys2.start, ++ resource_size(&res_sys2)); ++ ltq_status_membase = ioremap_nocache(res_status.start, ++ resource_size(&res_status)); ++ ltq_ebu_membase = ioremap_nocache(res_ebu.start, ++ resource_size(&res_ebu)); ++ if (!ltq_sys0_membase || !ltq_sys1_membase || !ltq_sys2_membase || ++ !ltq_status_membase || !ltq_ebu_membase) ++ panic("Failed to remap core resources"); ++ ++ clkdev_add_static(ltq_svip_cpu_hz(), ltq_svip_fpi_hz(), ++ ltq_svip_io_hz()); ++} +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0102-MIPS-lantiq-add-GPHY-clock-gate-bits.patch b/target/linux/lantiq/patches-3.7/0102-MIPS-lantiq-add-GPHY-clock-gate-bits.patch new file mode 100644 index 0000000000..e979e1e112 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0102-MIPS-lantiq-add-GPHY-clock-gate-bits.patch @@ -0,0 +1,52 @@ +From 05d6c964722224e8cf2902606744e29a835e7d5f Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 3 Dec 2012 21:35:01 +0100 +Subject: [PATCH 102/123] MIPS: lantiq: add GPHY clock gate bits + +Explicitly enable the clock gate of the internal GPHYs found on xrx200. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/xway/reset.c | 9 +++++++++ + arch/mips/lantiq/xway/sysctrl.c | 1 + + 2 files changed, 10 insertions(+) + +diff --git a/arch/mips/lantiq/xway/reset.c b/arch/mips/lantiq/xway/reset.c +index 544dbb7..1fa0f17 100644 +--- a/arch/mips/lantiq/xway/reset.c ++++ b/arch/mips/lantiq/xway/reset.c +@@ -78,10 +78,19 @@ static struct ltq_xrx200_gphy_reset { + /* reset and boot a gphy. these phys only exist on xrx200 SoC */ + int xrx200_gphy_boot(struct device *dev, unsigned int id, dma_addr_t dev_addr) + { ++ struct clk *clk; ++ + if (!of_device_is_compatible(ltq_rcu_np, "lantiq,rcu-xrx200")) { + dev_err(dev, "this SoC has no GPHY\n"); + return -EINVAL; + } ++ ++ clk = clk_get_sys("1f203000.rcu", "gphy"); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ clk_enable(clk); ++ + if (id > 1) { + dev_err(dev, "%u is an invalid gphy id\n", id); + return -EINVAL; +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 3925e66..6e0e135 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -374,6 +374,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()); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0103-MIPS-lantiq-adds-static-clock-for-PP32.patch b/target/linux/lantiq/patches-3.7/0103-MIPS-lantiq-adds-static-clock-for-PP32.patch new file mode 100644 index 0000000000..1ab7b07da1 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0103-MIPS-lantiq-adds-static-clock-for-PP32.patch @@ -0,0 +1,219 @@ +From 8cbac4b30bed1552503b95bc0ac6276e3cdda9d8 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 30 Nov 2012 21:08:49 +0100 +Subject: [PATCH 103/123] 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 +--- + 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 9128ff8..292ef1a 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 c169e2b..4739366 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,16 +67,18 @@ 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); + + extern unsigned long ltq_svip_cpu_hz(void); + extern unsigned long ltq_svip_fpi_hz(void); +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 6e0e135..75e1b7d 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); +@@ -377,10 +379,10 @@ void __init ltq_soc_init(void) + 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()); ++ 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.7/0104-MIPS-lantiq-adds-4dword-burst-length-for-dma.patch b/target/linux/lantiq/patches-3.7/0104-MIPS-lantiq-adds-4dword-burst-length-for-dma.patch new file mode 100644 index 0000000000..5ef0249c6a --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0104-MIPS-lantiq-adds-4dword-burst-length-for-dma.patch @@ -0,0 +1,34 @@ +From 07f7321c0f79c0b800d28898a480d044f839e813 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 6 Dec 2012 11:59:23 +0100 +Subject: [PATCH 104/123] 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 b5d76d1..986fbce 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_ENDIANESS (0xf << 8) /* endianess swap etop channels */ +@@ -195,7 +196,8 @@ ltq_dma_init_port(int p) + * Tell the DMA engine to swap the endianess of data frames and + * drop packets if the channel arbitration fails. + */ +- ltq_dma_w32_mask(0, DMA_ETOP_ENDIANESS | DMA_PDEN, ++ ltq_dma_w32_mask(0, (DMA_4W_BURST << 4) | (DMA_4W_BURST << 2) | ++ DMA_ETOP_ENDIANESS | DMA_PDEN, + LTQ_DMA_PCTRL); + break; + +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0105-MIPS-lantiq-rework-external-irq-code.patch b/target/linux/lantiq/patches-3.7/0105-MIPS-lantiq-rework-external-irq-code.patch new file mode 100644 index 0000000000..88659c2448 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0105-MIPS-lantiq-rework-external-irq-code.patch @@ -0,0 +1,214 @@ +From edd237c93d564e698e169a89d1b1b35248c5ef4a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 3 Dec 2012 21:44:30 +0100 +Subject: [PATCH 105/123] 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 +--- + arch/mips/include/asm/mach-lantiq/lantiq.h | 1 + + arch/mips/lantiq/irq.c | 104 +++++++++++++++++++--------- + 2 files changed, 73 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 f36acd1..48407f6 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,64 @@ 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 +194,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 +223,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 +339,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 = <q_eiu_type; + + irq_set_chip_and_handler(hw, chip, handle_level_irq); +@@ -323,7 +361,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 +378,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.7/0106-MIPS-lantiq-adds-minimal-dcdc-driver.patch b/target/linux/lantiq/patches-3.7/0106-MIPS-lantiq-adds-minimal-dcdc-driver.patch new file mode 100644 index 0000000000..78967efd4d --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0106-MIPS-lantiq-adds-minimal-dcdc-driver.patch @@ -0,0 +1,106 @@ +From 3aa46ed76b27df771f75db9c74ff011aca505fc5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 5 Dec 2012 17:38:48 +0100 +Subject: [PATCH 106/123] MIPS: lantiq: adds minimal dcdc driver + +This driver so far only reads the core voltage. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/xway/Makefile | 2 +- + arch/mips/lantiq/xway/dcdc.c | 74 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 75 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/lantiq/xway/dcdc.c + +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index 7a13660..087497d 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,3 +1,3 @@ +-obj-y := prom.o sysctrl.o clk.o reset.o dma.o gptu.o ++obj-y := prom.o sysctrl.o clk.o reset.o dma.o gptu.o dcdc.o + + obj-$(CONFIG_XRX200_PHY_FW) += xrx200_phy_fw.o +diff --git a/arch/mips/lantiq/xway/dcdc.c b/arch/mips/lantiq/xway/dcdc.c +new file mode 100644 +index 0000000..0ac73a5 +--- /dev/null ++++ b/arch/mips/lantiq/xway/dcdc.c +@@ -0,0 +1,74 @@ ++/* ++ * 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 John Crispin ++ * Copyright (C) 2010 Sameer Ahmad, Lantiq GmbH ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Bias and regulator Setup Register */ ++#define DCDC_BIAS_VREG0 0xa ++/* Bias and regulator Setup Register */ ++#define DCDC_BIAS_VREG1 0xb ++ ++#define dcdc_w8(x, y) ltq_w8((x), dcdc_membase + (y)) ++#define dcdc_r8(x) ltq_r8(dcdc_membase + (x)) ++ ++static void __iomem *dcdc_membase; ++ ++static int __devinit dcdc_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "Failed to get resource\n"); ++ return -ENOMEM; ++ } ++ ++ /* remap dcdc register range */ ++ dcdc_membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (!dcdc_membase) { ++ dev_err(&pdev->dev, "Failed to remap resource\n"); ++ return -ENOMEM; ++ } ++ ++ dev_info(&pdev->dev, "Core Voltage : %d mV\n", dcdc_r8(DCDC_BIAS_VREG1) * 8); ++ ++ return 0; ++} ++ ++static const struct of_device_id dcdc_match[] = { ++ { .compatible = "lantiq,dcdc-xrx200" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, dcdc_match); ++ ++static struct platform_driver dcdc_driver = { ++ .probe = dcdc_probe, ++ .driver = { ++ .name = "dcdc-xrx200", ++ .owner = THIS_MODULE, ++ .of_match_table = dcdc_match, ++ }, ++}; ++ ++int __init dcdc_init(void) ++{ ++ int ret = platform_driver_register(&dcdc_driver); ++ ++ if (ret) ++ pr_info("dcdc: Error registering platform driver\n"); ++ return ret; ++} ++ ++arch_initcall(dcdc_init); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0107-PINCTRL-lantiq-pinconf-uses-port-instead-of-pin.patch b/target/linux/lantiq/patches-3.7/0107-PINCTRL-lantiq-pinconf-uses-port-instead-of-pin.patch new file mode 100644 index 0000000000..5d9e5a308c --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0107-PINCTRL-lantiq-pinconf-uses-port-instead-of-pin.patch @@ -0,0 +1,92 @@ +From 84ce6d4b2802fd428a76e5f2692fd4c102ed35ea Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 30 Nov 2012 21:11:22 +0100 +Subject: [PATCH 107/123] 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 +--- + 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 b9bcaec..48ab09b 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.7/0108-PINCTRL-lantiq-fixes.patch b/target/linux/lantiq/patches-3.7/0108-PINCTRL-lantiq-fixes.patch new file mode 100644 index 0000000000..9ce8a785f9 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0108-PINCTRL-lantiq-fixes.patch @@ -0,0 +1,281 @@ +From 13e754b5fff5be1930e2b8fe534a52b608c9e479 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 3 Dec 2012 19:27:28 +0100 +Subject: [PATCH] PINCTRL: lantiq: fixes + +--- + drivers/pinctrl/pinctrl-lantiq.c | 54 ++++++++++++++++++----------- + drivers/pinctrl/pinctrl-lantiq.h | 1 + + drivers/pinctrl/pinctrl-xway.c | 70 ++++++++++++++++++++++++++++++++++---- + 3 files changed, 99 insertions(+), 26 deletions(-) + +Index: linux-3.7-rc8/drivers/pinctrl/pinctrl-lantiq.c +=================================================================== +--- linux-3.7-rc8.orig/drivers/pinctrl/pinctrl-lantiq.c 2012-12-03 20:22:37.000000000 +0100 ++++ linux-3.7-rc8/drivers/pinctrl/pinctrl-lantiq.c 2012-12-14 22:59:40.687563565 +0100 +@@ -64,11 +64,13 @@ + 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 @@ + 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 @@ + (*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 @@ + } + + 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 @@ + (*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 @@ + { + 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; + } + +Index: linux-3.7-rc8/drivers/pinctrl/pinctrl-lantiq.h +=================================================================== +--- linux-3.7-rc8.orig/drivers/pinctrl/pinctrl-lantiq.h 2012-12-03 20:22:37.000000000 +0100 ++++ linux-3.7-rc8/drivers/pinctrl/pinctrl-lantiq.h 2012-12-14 22:55:26.591557194 +0100 +@@ -34,6 +34,7 @@ + LTQ_PINCONF_PARAM_OPEN_DRAIN, + LTQ_PINCONF_PARAM_DRIVE_CURRENT, + LTQ_PINCONF_PARAM_SLEW_RATE, ++ LTQ_PINCONF_PARAM_OUTPUT, + }; + + struct ltq_cfg_param { +Index: linux-3.7-rc8/drivers/pinctrl/pinctrl-xway.c +=================================================================== +--- linux-3.7-rc8.orig/drivers/pinctrl/pinctrl-xway.c 2012-12-14 22:55:26.567557195 +0100 ++++ linux-3.7-rc8/drivers/pinctrl/pinctrl-xway.c 2012-12-14 22:55:26.595557195 +0100 +@@ -443,7 +443,7 @@ + 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: +@@ -466,6 +466,11 @@ + *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; +@@ -489,7 +494,10 @@ + 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: +@@ -515,6 +523,14 @@ + 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; +@@ -522,9 +538,25 @@ + 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; ++} ++ ++ + 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 = { +@@ -532,10 +564,9 @@ + .confops = &xway_pinconf_ops, + }; + +-static inline int xway_mux_apply(struct pinctrl_dev *pctrldev, ++static int mux_apply(struct ltq_pinmux_info *info, + int pin, int mux) + { +- struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); + int port = PORT(pin); + u32 alt1_reg = GPIO_ALT1(pin); + +@@ -555,9 +586,18 @@ + return 0; + } + ++static inline int xway_mux_apply(struct pinctrl_dev *pctrldev, ++ int pin, int mux) ++{ ++ struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); ++ ++ return mux_apply(info, pin, mux); ++} ++ + 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 = { +@@ -598,6 +638,10 @@ + { + struct ltq_pinmux_info *info = dev_get_drvdata(chip->dev); + ++ if (PORT(pin) == PORT3) ++ gpio_setbit(info->membase[0], GPIO3_OD, PORT_PIN(pin)); ++ else ++ gpio_setbit(info->membase[0], GPIO_OD(pin), PORT_PIN(pin)); + gpio_setbit(info->membase[0], GPIO_DIR(pin), PORT_PIN(pin)); + xway_gpio_set(chip, pin, val); + +@@ -618,6 +662,18 @@ + pinctrl_free_gpio(gpio); + } + ++static int xway_gpio_to_irq(struct gpio_chip *chip, unsigned offset) ++{ ++ struct ltq_pinmux_info *info = dev_get_drvdata(chip->dev); ++ int i; ++ ++ for (i = 0; i < info->num_exin; i++) ++ if (info->exin[i] == offset) ++ return ltq_eiu_get_irq(i); ++ ++ return -1; ++} ++ + static struct gpio_chip xway_chip = { + .label = "gpio-xway", + .direction_input = xway_gpio_dir_in, +@@ -626,6 +682,7 @@ + .set = xway_gpio_set, + .request = xway_gpio_req, + .free = xway_gpio_free, ++ .to_irq = xway_gpio_to_irq, + .base = -1, + }; + diff --git a/target/linux/lantiq/patches-3.7/0109-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch b/target/linux/lantiq/patches-3.7/0109-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch new file mode 100644 index 0000000000..c76a2a146a --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0109-GPIO-MIPS-add-gpio-driver-for-falcon-SoC.patch @@ -0,0 +1,404 @@ +From d4911be1cc44c8d3ca72b03d5da13f792d4a02d2 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 23 Jun 2012 15:32:33 +0200 +Subject: [PATCH 109/123] 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 +Signed-off-by: Thomas Langer +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 47150f5..0647e07 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -114,6 +114,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 9aeed67..2a9e4a2 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -21,6 +21,7 @@ obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.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 ++ * Copyright (C) 2012 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* 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.7/0110-Document-devicetree-add-OF-documents-for-lantiq-seri.patch b/target/linux/lantiq/patches-3.7/0110-Document-devicetree-add-OF-documents-for-lantiq-seri.patch new file mode 100644 index 0000000000..a553f49a02 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0110-Document-devicetree-add-OF-documents-for-lantiq-seri.patch @@ -0,0 +1,39 @@ +From ed881db69430dd62765d02e2f4f1321ddc2f5fb5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 20 Jul 2012 18:58:34 +0200 +Subject: [PATCH 110/123] Document: devicetree: add OF documents for lantiq + serial port + +Signed-off-by: John Crispin +Cc: Rob Herring +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.7/0111-MTD-MIPS-lantiq-Add-NAND-support-on-Lantiq-FALCON-So.patch b/target/linux/lantiq/patches-3.7/0111-MTD-MIPS-lantiq-Add-NAND-support-on-Lantiq-FALCON-So.patch new file mode 100644 index 0000000000..3f15b2180c --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0111-MTD-MIPS-lantiq-Add-NAND-support-on-Lantiq-FALCON-So.patch @@ -0,0 +1,139 @@ +From 72112b91624dca6c636bd3a592471642d3988b27 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 20 Jul 2012 19:09:01 +0200 +Subject: [PATCH 111/123] MTD: MIPS: lantiq: Add NAND support on Lantiq FALCON + SoC. + +The driver uses plat_nand. As the platform_device is loaded from DT, we need +to lookup the node and attach our falocn specific "struct platform_nand_data" +to it. + +Signed-off-by: John Crispin +Cc: Artem Bityutskiy +Cc: linux-mtd@lists.infradead.org +--- + drivers/mtd/nand/Kconfig | 8 ++++ + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/falcon_nand.c | 82 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 91 insertions(+) + create mode 100644 drivers/mtd/nand/falcon_nand.c + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 4883139..a3b05be 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -572,4 +572,12 @@ config MTD_NAND_XWAY + Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached + to the External Bus Unit (EBU). + ++config MTD_NAND_FALCON ++ tristate "Support for NAND on Lantiq FALC-ON SoC" ++ depends on LANTIQ && SOC_FALCON ++ select MTD_NAND_PLATFORM ++ help ++ Enables support for NAND Flash chips on Lantiq FALC-ON SoCs. NAND is ++ attached to the External Bus Unit (EBU). ++ + endif # MTD_NAND +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index 2cbd091..a97e1b5 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -53,5 +53,6 @@ obj-$(CONFIG_MTD_NAND_RICOH) += r852.o + obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o + obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ + obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o ++obj-$(CONFIG_MTD_NAND_FALCON) += falcon_nand.o + + nand-objs := nand_base.o nand_bbt.o +diff --git a/drivers/mtd/nand/falcon_nand.c b/drivers/mtd/nand/falcon_nand.c +new file mode 100644 +index 0000000..b3b850c +--- /dev/null ++++ b/drivers/mtd/nand/falcon_nand.c +@@ -0,0 +1,82 @@ ++/* ++ * 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) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++/* nand flash */ ++/* address lines used for NAND control signals */ ++#define NAND_ADDR_ALE 0x10000 ++#define NAND_ADDR_CLE 0x20000 ++/* Ready/Busy Status */ ++#define MODCON_STS 0x0002 ++/* Ready/Busy Status Edge */ ++#define MODCON_STSEDGE 0x0004 ++#define LTQ_EBU_MODCON 0x000C ++ ++static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL }; ++ ++static int falcon_nand_ready(struct mtd_info *mtd) ++{ ++ u32 modcon = ltq_ebu_r32(LTQ_EBU_MODCON); ++ ++ return (((modcon & (MODCON_STS | MODCON_STSEDGE)) == ++ (MODCON_STS | MODCON_STSEDGE))); ++} ++ ++static void falcon_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct nand_chip *this = mtd->priv; ++ unsigned long nandaddr = (unsigned long) this->IO_ADDR_W; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ nandaddr &= ~(NAND_ADDR_ALE | NAND_ADDR_CLE); ++ ++ if (ctrl & NAND_CLE) ++ nandaddr |= NAND_ADDR_CLE; ++ if (ctrl & NAND_ALE) ++ nandaddr |= NAND_ADDR_ALE; ++ ++ this->IO_ADDR_W = (void __iomem *) nandaddr; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, this->IO_ADDR_W); ++} ++ ++static struct platform_nand_data falcon_nand_data = { ++ .chip = { ++ .nr_chips = 1, ++ .chip_delay = 25, ++ .part_probe_types = part_probes, ++ }, ++ .ctrl = { ++ .cmd_ctrl = falcon_hwcontrol, ++ .dev_ready = falcon_nand_ready, ++ } ++}; ++ ++static int __init falcon_register_nand(void) ++{ ++ struct device_node *node; ++ struct platform_device *pdev; ++ ++ node = of_find_compatible_node(NULL, NULL, "lantiq,nand-falcon"); ++ if (!node) ++ return -1; ++ pdev = of_find_device_by_node(node); ++ if (pdev) ++ pdev->dev.platform_data = &falcon_nand_data; ++ of_node_put(node); ++ return 0; ++} ++ ++arch_initcall(falcon_register_nand); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0112-MTD-lantiq-xway-fix-NAND-reset-timeout-handling.patch b/target/linux/lantiq/patches-3.7/0112-MTD-lantiq-xway-fix-NAND-reset-timeout-handling.patch new file mode 100644 index 0000000000..447c0615c0 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0112-MTD-lantiq-xway-fix-NAND-reset-timeout-handling.patch @@ -0,0 +1,47 @@ +From d8d9b9055d704d6f84ef6346d6826b8a9640f209 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Oct 2012 10:25:39 +0200 +Subject: [PATCH 112/123] MTD: lantiq: xway: fix NAND reset timeout handling + +Fixes a possible deadlock in the code that resets the NAND flash. + +http://lists.infradead.org/pipermail/linux-mtd/2012-September/044240.html + +Signed-off-by: John Crispin +--- + drivers/mtd/nand/xway_nand.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c +index 3f81dc8..4731300 100644 +--- a/drivers/mtd/nand/xway_nand.c ++++ b/drivers/mtd/nand/xway_nand.c +@@ -58,15 +58,23 @@ static void xway_reset_chip(struct nand_chip *chip) + { + unsigned long nandaddr = (unsigned long) chip->IO_ADDR_W; + unsigned long flags; ++ unsigned long timeout; + + nandaddr &= ~NAND_WRITE_ADDR; + nandaddr |= NAND_WRITE_CMD; + + /* finish with a reset */ ++ timeout = jiffies + msecs_to_jiffies(200); ++ + spin_lock_irqsave(&ebu_lock, flags); ++ + writeb(NAND_WRITE_CMD_RESET, (void __iomem *) nandaddr); +- while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) +- ; ++ do { ++ if ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) ++ break; ++ cond_resched(); ++ } while (!time_after_eq(jiffies, timeout)); ++ + spin_unlock_irqrestore(&ebu_lock, flags); + } + +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0113-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch b/target/linux/lantiq/patches-3.7/0113-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch new file mode 100644 index 0000000000..863f790539 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0113-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch @@ -0,0 +1,1047 @@ +From f2ac37c0a5297ca4663da9e4328c77736504b484 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 20 May 2012 00:42:39 +0200 +Subject: [PATCH 113/123] 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 +Signed-off-by: John Crispin +--- + 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 e9df461..e03f821 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -460,6 +460,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 395b516..74a789a 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -45,6 +45,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 ++ */ ++ ++#include ++#include ++#include ++#include /* for kzalloc, kfree */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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 = <q_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 "); ++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.7/0114-SPI-MIPS-lantiq-adds-spi-xway.patch b/target/linux/lantiq/patches-3.7/0114-SPI-MIPS-lantiq-adds-spi-xway.patch new file mode 100644 index 0000000000..d8c6dd08d5 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0114-SPI-MIPS-lantiq-adds-spi-xway.patch @@ -0,0 +1,1033 @@ +From db447f1a18106aa4d32438ab72ff57024b34cee4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 16 Aug 2012 09:57:01 +0200 +Subject: [PATCH 114/123] 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 +Signed-off-by: John Crispin +--- + 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 1acae35..d79a587 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -434,6 +434,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 c48df47..7e344a9 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -66,4 +66,5 @@ 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..8441085 +--- /dev/null ++++ b/drivers/spi/spi-xway.c +@@ -0,0 +1,977 @@ ++/* ++ * Lantiq SoC SPI controller ++ * ++ * Copyright (C) 2011 Daniel Schwierzeck ++ * Copyright (C) 2012 John Crispin ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 __devinit 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 __devexit 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 = __devexit_p(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 "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:spi-xway"); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0115-NET-PHY-adds-driver-for-lantiq-PHY11G.patch b/target/linux/lantiq/patches-3.7/0115-NET-PHY-adds-driver-for-lantiq-PHY11G.patch new file mode 100644 index 0000000000..7d3a6b9d15 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0115-NET-PHY-adds-driver-for-lantiq-PHY11G.patch @@ -0,0 +1,228 @@ +From 12f4b99d63edc15849357c09e22a36445c2752cc Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Oct 2012 09:28:30 +0200 +Subject: [PATCH 115/123] NET: PHY: adds driver for lantiq PHY11G + +Signed-off-by: John Crispin +--- + drivers/net/phy/Kconfig | 5 ++ + drivers/net/phy/Makefile | 1 + + drivers/net/phy/lantiq.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 184 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..ba4d7b7 +--- /dev/null ++++ b/drivers/net/phy/lantiq.c +@@ -0,0 +1,178 @@ ++/* ++ * 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 ++ */ ++ ++#include ++#include ++ ++#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; ++} ++ ++/* TODO: add vr9_gphy_22f_driver and drivers for external Lantiq PEF7071 PHYs */ ++static struct phy_driver vr9_gphy_11g_driver = { ++ .phy_id = 0xd565a408, ++ .phy_id_mask = 0xfffffff0, ++ .name = "Lantiq XWAY VR9 GPHY 11G", ++ .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 }, ++}; ++ ++static int __init ltq_phy_init(void) ++{ ++ int err; ++ ++ err = phy_driver_register(&vr9_gphy_11g_driver); ++ if (err) ++ goto err_out; ++ ++ return 0; ++ ++err_out: ++ return err; ++} ++ ++static void __exit ltq_phy_exit(void) ++{ ++ phy_driver_unregister(&vr9_gphy_11g_driver); ++} ++ ++module_init(ltq_phy_init); ++module_exit(ltq_phy_exit); ++ ++MODULE_DESCRIPTION("Lantiq PHY drivers"); ++MODULE_AUTHOR("Daniel Schwierzeck "); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0116-NET-MIPS-lantiq-update-etop-driver-for-devicetree.patch b/target/linux/lantiq/patches-3.7/0116-NET-MIPS-lantiq-update-etop-driver-for-devicetree.patch new file mode 100644 index 0000000000..cf93292e75 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0116-NET-MIPS-lantiq-update-etop-driver-for-devicetree.patch @@ -0,0 +1,807 @@ +From c7b0e371e1c5e2f6258decfeb948e0dda7109afc Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 24 Oct 2012 19:50:30 +0200 +Subject: [PATCH 116/123] NET: MIPS: lantiq: update etop driver for devicetree + +--- + drivers/net/ethernet/lantiq_etop.c | 470 +++++++++++++++++++++++++----------- + 1 file changed, 333 insertions(+), 137 deletions(-) + +diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c +index 003c5bc..dc5457a 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 ++ * Copyright (C) 2011-12 John Crispin + */ + + #include +@@ -36,6 +36,10 @@ + #include + #include + #include ++#include ++#include ++#include ++#include + + #include + +@@ -71,25 +75,56 @@ + #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 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 ++ ++/* 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 +134,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 +197,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 +209,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 +226,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 +240,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 +260,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 +270,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 +285,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 +296,55 @@ 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); + } + + static int + ltq_etop_hw_init(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; + +- ltq_pmu_enable(PMU_PPE); ++ clk_enable(priv->clk_ppe); + +- switch (priv->pldata->mii_mode) { ++ if (of_machine_is_compatible("lantiq,ar9")) { ++ ltq_etop_gbit_init(dev); ++ /* force the etops link to the gbit to MII */ ++ priv->mii_mode = PHY_INTERFACE_MODE_MII; ++ } ++ ++ switch (priv->mii_mode) { + case PHY_INTERFACE_MODE_RMII: + ltq_etop_w32_mask(ETOP_MII_MASK, + ETOP_MII_REVERSE, LTQ_ETOP_CFG); +@@ -264,39 +356,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); ++ priv->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 +433,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 +444,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 +455,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 +469,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 +542,11 @@ 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; +- } +- } ++ 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,7 +554,7 @@ ltq_etop_mdio_probe(struct net_device *dev) + } + + phydev = phy_connect(dev, dev_name(&phydev->dev), <q_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"); +@@ -408,6 +568,9 @@ ltq_etop_mdio_probe(struct net_device *dev) + | SUPPORTED_Autoneg + | SUPPORTED_MII + | SUPPORTED_TP); ++ if (of_machine_is_compatible("lantiq,ar9")) ++ phydev->supported &= SUPPORTED_1000baseT_Half ++ | SUPPORTED_1000baseT_Full; + + phydev->advertising = phydev->supported; + priv->phydev = phydev; +@@ -433,8 +596,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 +651,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 +672,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 +694,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 +711,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 +721,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 +804,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 = <q_etop_ethtool_ops; ++ else ++ pr_warn("etop: mdio probe failed\n");; + return 0; + + err_netdev: +@@ -680,6 +849,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 +875,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 +913,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 = <q_eth_netdev_ops; +- dev->ethtool_ops = <q_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 +993,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 = __devexit_p(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(<q_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(<q_mii_driver); +-} +- +-module_init(init_ltq_etop); +-module_exit(exit_ltq_etop); ++module_platform_driver(ltq_mii_driver); + + MODULE_AUTHOR("John Crispin "); + MODULE_DESCRIPTION("Lantiq SoC ETOP"); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0117-NET-MIPS-lantiq-adds-xrx200-net.patch b/target/linux/lantiq/patches-3.7/0117-NET-MIPS-lantiq-adds-xrx200-net.patch new file mode 100644 index 0000000000..3d3596344c --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0117-NET-MIPS-lantiq-adds-xrx200-net.patch @@ -0,0 +1,1414 @@ +From a0a6f7f03c914327064364767b7ba688cdbcf611 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Oct 2012 12:22:23 +0200 +Subject: [PATCH 117/123] 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 | 1191 ++++++++++++++++++++++++++++++++++ + 4 files changed, 1362 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 ++ * ++ * 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..458bc11 +--- /dev/null ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -0,0 +1,1191 @@ ++/* ++ * 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#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; ++ ++ 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) ++{ ++ 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++; ++ ++ /* 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 __devinit 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 __devexit 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 = __devexit_p(xrx200_remove), ++ .driver = { ++ .name = "lantiq,xrx200-net", ++ .of_match_table = xrx200_match, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(xrx200_driver); ++ ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("Lantiq SoC XRX200 ethernet"); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0118-owrt-adds-PHY11G-firmware-blobs.patch b/target/linux/lantiq/patches-3.7/0118-owrt-adds-PHY11G-firmware-blobs.patch new file mode 100644 index 0000000000..32e583b327 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0118-owrt-adds-PHY11G-firmware-blobs.patch @@ -0,0 +1,2221 @@ +From 870dad40d334e9e8342f28dbcad1410cad12a945 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Oct 2012 09:26:24 +0200 +Subject: [PATCH 118/123] owrt: adds PHY11G firmware blobs + +Signed-off-by: John Crispin +--- + firmware/Makefile | 1 + + firmware/lantiq/COPYING | 286 ++++++++++++++++++++++++++++++++++++ + firmware/lantiq/README | 45 ++++++ + firmware/lantiq/vr9_phy11g_a1x.bin | Bin 0 -> 65536 bytes + firmware/lantiq/vr9_phy11g_a2x.bin | Bin 0 -> 65536 bytes + firmware/lantiq/vr9_phy22f_a1x.bin | Bin 0 -> 65536 bytes + firmware/lantiq/vr9_phy22f_a2x.bin | Bin 0 -> 65536 bytes + 7 files changed, 332 insertions(+) + create mode 100644 firmware/lantiq/COPYING + create mode 100644 firmware/lantiq/README + create mode 100644 firmware/lantiq/vr9_phy11g_a1x.bin + create mode 100644 firmware/lantiq/vr9_phy11g_a2x.bin + create mode 100644 firmware/lantiq/vr9_phy22f_a1x.bin + create mode 100644 firmware/lantiq/vr9_phy22f_a2x.bin + +diff --git a/firmware/Makefile b/firmware/Makefile +index eeb1403..4259bed 100644 +--- a/firmware/Makefile ++++ b/firmware/Makefile +@@ -135,6 +135,7 @@ 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_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. ++ ++ 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.) ++ ++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. ++ ++ 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. ++ ++ 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 ++# ++ ++# ++# 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 +diff --git a/firmware/lantiq/vr9_phy11g_a1x.bin b/firmware/lantiq/vr9_phy11g_a1x.bin +new file mode 100644 +index 0000000000000000000000000000000000000000..cdf3d3063405c1239d15bae873e11f4ad8644e0f +GIT binary patch +literal 65536 +zcmb5X3t$x0y+1xP`{Hwh#m8v=+Zu*jkyNP?_NuO=)KUV^avoK07O +z-7zusg=CaqwGSzTS_P7=y?9&u0@<{;|NCzxLcQ1a_W$c?yR$wZ*`swrfF%3>oZZA) +zulJ={=gfJ0&+B`>?=z|&3&N~fvpALghnsa-hgI&!nmy0ND}cdY%l+0(D-FP=8_=ASkxJgym%*C%d05^rgSG6-U^RRhthJwq +z75Fk(TfPid@ISyZ^`^oPb0&@FoZ1Vr_M}sLlGnb;bKm3zC%>pdL-sdaS`UxE#9OGu +z*Ui+facXbXXusvP_xabZ@Yc$bf^uzIwPyEd&JwMzM7piyLQP4>bk_DOrP}rqEn2eW +z`4Y}q@@uDdq(plj-SGG`KgpkQ@+n#SL%H@%8J(h!JKy4!QhsZ-R)9V#vwi&P3U875 +zAGowPUD_3=c7@mMB|c}#bIy{etetggb@(gMZYx>!m68@`$s{N1H;TU!?)j3XN9nVv +zeP`*bZ&w_pGx5UxR34qq{XF+^9UA&|q9!HL(Yk^3z;N9~?~50|{q2Ea3w<|# +zbmZ;ChsIF9?0kHHc8^|vPNE^&pRP%X-jTX@tqm{6oBdtMrQ4vjr8k3Fx9J+F>E +zhsT~HW6x1dMt=@dAo2as(FwI*-<8(h-F3o8MDd9wv>z#{M|Zst +zl~a=zA0J2;xKg8JcG^Y#>DdG6ktzD<6hm;$9==w8_F97*XxrCHQ(g2`BO#wS_kr=@ +zxsT3W4t;R$cSM +zNb~%W4Xxs7>*+J4FL~a$PJb!KW4hjQUF;f#i(J=g)SNK5*#j>bq)kwB&WL@vXO3*J +z_Vu;)v36~(eSLkTudNT~b{1gKFT&+db2!w(Y0GKzY1Ri3M9m-b8-<0vDG%>2tbI!$ +z{**r!9Ck%r4;>Z`3&D}#sOt%rmPro_!J^n@*Z*_a`j!wB0+U=;E&f(vFX7O%@XWQu +zqgu5OsOlqxvuZIUcwfpHf6HmGcW1u8*H0!Bt42ru=qIMwgfrBM@(MLuPTYcjz}b^F +z>aY5pT-3=&g$cd9Kb0o_I#%E`>i{sBL6 +z8Yq(RzDOPM4+s-H3)om$BY`;cow4{^hS`_G!zqoA(P7m@e_^i>;G+H%AEf}89Z$-S +zrp`2(Vg=0Jw}gP{daL%GE_#Fh7M-e30pP;kGzpsgg~+#)u2C}8xMBYX@5J9i6O6Yv +zEi{|Zbxr_P3MJF2^i+MSKk$p5lD+w~SwCG2% +z_!g}n!nVY(xT5}HS4m*lH9IikT7c)ez*Sds;9A#awA&R-VO_O}D)ke)mDgyBI7!-W +z)&7Bw__6M*)u^8oB}f<5d@Ak5;4PL2mY}KaHQPy^ns?tQ|Y^yeb +zcF_cmi`GX|{yuA;*k|su^a*_?pf3+JJOVTX7WVN#!`4}(kCN-G+GW=?|A#I)5GcAS +z5WL>CP`{z)fr0yjQ?1%pf@VLtp1#3rGEH>dpihmtQcHuHHwaw00dq)O(OnFMDIck_ +zJ|a`E^3mRpf>Sqsw2@cLw&K7IQ#bb8kqqVrZrF$>T6^o%cA2)_Kye6KaR~Zw2yiwy +zmS&05pF0E^4uM(ODE=IarBO3Ulb`X2&SQZRRo;O9;Td!A{*Cu-T&FXfc({X)<^>+s +z+rM&O`?~gjxc~k@`+fJncHc2kyRe#g$R5foIRp!&0q@hqWL<>4*2;({L@KTK7~I`t +zpQznq+~YRxo|6b!19#sO;_j}*zN_>e9Yr?o?hsit@g76$`|z9v3v0r{@>p0#7e0T! +zDHc}4!h#&3HR)<1v#jfYbeb4g#poDlO2O%|$#Y|q-+;+;n7n|=n?V7rpa7sor>UfD +zv +z!ot*LlYfyPd1>T{EHC+d_^S7%xHUYO5aQP4vx({6Zzem#gURB-Pxpljf@$JT7D=ZD +z`IZCWec=ge8codnslmYm;RLT;N#MNvDRtlKbZ6Kan78;!lKcr+BKms+$V~1FTQy|* +z3E@#i42yx(l~0rVh7a%i#8aOhQD@nG!c;rHs|VO6-5fZbzj3)l9fr>ztH1N#*zV0vY!YOUU+ra|KZ +zsV7z!#y^~bd8U_n51lsyKpX&W1%TL1s(|8q;}@m`o(-o9QJh3^`8fPy!1wpwt3R_P4y*YDv9En +zQO(Am@}qLGIx4y*1ya0{9>o^CifIT~vEk8mqg{L +z!584XZ4mqD7ik4p7C8dMJuMswNx<7!mzyVHKo>^_%Z`rIw;zdhWBN{3Mi^?%FiUq$^@fuFh_2oRSLxae9G +zdgt&WJ)WmxUH|p4(6FFqp{`1cUVZ<_+t&B6Xhyz&NNqm9RPsL6d82EoZdI2WOWhu2 +zX>6%6tQ>uAk+I0F$4{~53*z|=sNv +zVQfK9C6;jE3xXpj?Q!)lvMlNun(0+~ewq5^9+WzA_HqmMzaX4d7j`%Ti*&EDXi{=$ +zky}0bT>9*Vv#DDwN1qddx9ER->}E^U8Igxa5+1Ae?qk#ZNv-hX6qU;;aUuP=z{Rp@8M}AwK~&NlDuc?G!w7 +z>Khl7H+aRNCG0nt)sJ{q4$+i4UgsUW<23(U-tjN@-gdXTtl{=`cipq@D-B%EP4idQ +z`*VD5=iURyIQgdfOXxZnd^<~LFAyJd}L&Uiqq9$D?YUz5sh+2X|R=-2*r^_$W +zl_G`g4sbG)qui*7Y6Qt;s8+AhRboHAQ=|=|RxfH7Sv4FX5waNN00hT~(5n#40Mv|k +z3x4=B)3jn$c*x&`nuRF6q`3qKuW%wr5JXS%Ox~Gv{yEEEJo(~=ylb6WirX`CATH*4 +z5+mPE*tOwr6~@Ukd(6ZcN$-sc`B7&@mGt>PObB(D!WpobnX2b(rFZd|W7wo8Ern%dW|KPH_M91pxEOJ^>W +zqkRJH8$(XxD*(dI30|uJE)NM`I!a$vMoo?!?Glg^ypZWH1j<(QgEO8)YPE?6Q^Zm0riVFA3dp)*cB1(Kp6N_o +zH0lQxAduTthD+xlWNLxh4}4_32&`fx+Y9_D!0(Uvow9>sfPHabUs3H?uH%miZ`Zz? +z>zJeD*&}&TiY&V#*)=IjE0K&q#W)5YPoQFGP%-@Ew>FJ{RcrK=KdSuBHjL~u^g$(8 +z5c0?~b}b{*-a|ZbQf_q+2YJ2{! +zUieGuArJNZ9w2ouag#XoJJ%sziRL|@)_%EqHBBAm&}BwHLmuR6bv3%Mexcs_|4{Fj +ztC9Czj$mN)2&a+n;@O2KU@;XEUBuCa>zAv8(bOxPhHMHIke~}OllQ%B-kazx_3Gcs +z&#W#KQ?F&;*>-Z;o2xPHV{A-JHbgm}I?idVJ8J%v5cG*7|LLdXdmOUN$a)RMnKFz+ +z9v_EQQ=*XN%D~e`&E6+EM`u#Sldc&se`?Ar!;qOw6*Uo0hb(h@oqI~7rN{GLw?;U9 +z!Wm~!Xf7Qdx$3k=yiw;K=j+1mm3yrbHOlXHz8(c5LcP|4mRb+t5v4; +zBkm5@{_C44G7)~VSwSP{rN(^=t4R%>dFn6T=@Ytu{CI<6+xYDRNR;6c_hE{ +ze9E1=+iR?J5$EhgD?~JdSE>xX+BhJ%M#{_das%?<@V?e(%kD;@GQ+5)q@D`yyYR_gk7>hv$RH_-k7uT;48=?0XukztqaX2K@S +zYtMGk3O%1r*8|SxlxN{#K}(2vU38w+)O>;F9Vrds9M!S8Q84hjg#Abz>_Mu(t_~tk +zx*i0SNi-S-J)|Y}w6fT~9w`XUayVzrmyJvWng2l6WI +z$COrkUgmuw-mB~$MX<|ww`B61$aBLMK}7=_z=Qw_!Gp;R1MbP1K0GIq41PHVRrKZ?cIs&MdIF6c`A=Qt{Xf}#{ +zAE^T@){M*_@9$;_bu)m_PGbGy<(9Ew(FJ4mGvj16S>N-d&-Fbr*7x!Z95#0RuAlbr5 +zJGLqiWH%tiLzb;yWwZGg-&xJ&EL#$pAbA`UqE2T_w;O--r=ZR{O9bM-y@d4*YsT!itzp9SN8W$=J#B=zS^GPgE!V1z +zJY-@tK@4Cm=k!}*R7;y+H$VxkANHdjjs{;iN@|%M71eg48uF+aNXFsl)S-On(pUp@ +zSrg=&O=vycx5!VXs?>{NF_67lA+t5qTA^Aj0xA%!DJ;uW!ajyrZ>U2E_CF(IAVW(_ +z7)K@p;AR?Vv-zcx@*6_^z) +z%Kxk^fW<_;YphzX$e$!^jYIMRmb)(RoP1v>S8%X&V@STo3i%RiDYLZuv&5qCXATp_g~ +zCT}oC{UM<-q$TDxby1JihIMELGh2^6%(ZHN7*FRQ&CF7aN9{10hyi0B&}1i*g{)d0 +zfq}conc-Is@Co85vTB>LHspRVL$;Ly7990yw*I6{{Vad85ggiDO=R}qT4oQv1Qz;z +zu+TzYD_qGdaY=+N(I^shzR%arqTp +z=ditwRj54N2Rl7595M4ZvT7Nzhyg)ZKI3huYo{on*zFB5Q8m!X_jzpt8#Aw6L-6>K +z20YJ<^Hkb|@t9fP7+#@fV>YB(>mn6XL?)J^P;(KF2P=kc{_|NhQXvXs9cA=GNba9K +z1-`R>tspyZO#dvV6pL*zc%0S^*v2C08%1nya73Fh*4cbSrdaDUIId+s1DUmIHnZ19 +ziZnS6JGleckyh|*9#P#8D(3}^zIGVWb +zK?r7iL{x*O=8M?7^$@SgJ!;;$1;OnfUP-oxpA09Zk4(W$lY^UzTf=$hI^#*JcB2=x +zu*fR5P_;c=r6#2-$rqjspO3y!{83c!aw;rvo$-s46suQIN55rylZ)D5cjEm6Vox0y +zpO!w2^HpT^(E3x@-#pA^ww!l_f?de?W&W|*d?jn|ve|ON?)f&GjcazV^@rWud_VN` +zhqwB|i}9@9zV!h-d`qg=tmJaGZSghOY@zkHfgUQ`S1e&QeASDqm*BZ#34^uZ&Na7j +zInD0shE+CY>-u{EHfPOkYyHjc1$VOAIS+5Vs~VY}TIa$r6tQaVVf=iz)o{LLYihVV +zmoBa08mhUa&F=YYc!A5o7X1WU6k`4iXN;zdtNs+utTV%>z!WAzxrQ=*B*FN}U~9OS +zJPnzLo#6(1M>ug*jh +zqb5iM9LOA~vk5p*VuJVyUe-108%R&X`OM4MW3cy(vCCkq6`%_t@tV-2&^L;|H8~^> +z?g=LtZ~E@UL8&<=oij$jCz@l;&7cJtz5u!o_D~wTfjA4}NKL}CFy$o9MU39(`XLKC +zK>VjdcL#k*HEtX*HSRYDmhWon@B@(6Y>arw%c3(f?N5{2g=pKPcG$9q$-8-u5nr}k +z5R{k2jSuKlhY}uu622Zv_&F%yuR#fisq_Ji>kNjI0&i`u{fmd#aUe)(7M~q3GB{yJ +zC~lt|ZM)__z~&{u_Jgs527ne#^C#QFMKN2>NdX6Jvbw=gt!-iCP23Q(1=-xgCUt|T +zO>TqW@}215<6$+|5qUf;dINlO5=Teqn-jjtm0Fk>+#8h# +zi!uH^qBfxc{4`r13@-UtSY~>tq#R0E{2Y#N@o-WaE!;kkE|gM(#V~IS+8zHVI#Uqz +z$ZW_EQ=(2KtlL|p*)3T1pEAAX`NBp1UG{T%Ul+ArjhYl#i>z>uLdtN)qE6!y`c*)! +z8oHZ8E>xFMz0iIp?|G)jdY}(lwfsEz`aqjW6T_t=d1vnFg%F(Yqkc%dX(osG8C{_D +z1d@XtqH&=QIS^Y54uRK0_N6`~@Nm*eN}f_>*T9G!G@*Q~d?IR4RbM~nSC2rf&VzBP +z4rk0a4yPV)m{dL92Sh;lf9;wR02y2gxG+CxNIC$!O8FQTIlu$#PYCp4x=_;U5_w15 +z!`L6_{tOrCs;g{UFOu|-pJHPL$q75q&kw^R8GS08&<7O=f8T78TBI2*EsutO^+Ag? +zvfbEb2rc1oXnTva+2A)>wJip0pi*mbJ2?wvW@on@FKwsWu1OOFzkmT@8Q{B3jWTS3 +z(|5$bsz1%>XT9pLgZr`%)hVjCqk0=ui!{^qwNaCQgdFC$$jfm)A~XcKz5N6HUcsu3 +z-U)Pa5ZJ++m;mu8dbQItQLHrknqe2Yyht +zJR4$f?qURFLoA12=&5qZ0zi>yxV%A?um~Gs+go}CKh6R5{}DJJ+F;Z-g~}VlqIy>1 +zTOjw&ZDD6JCm=KrVnNy3HOKB$aUN2W+m{kXSJJR7`3JW3Y%}UlwYN@ghvRJ0_BTk^ +zwl@YQS)U3|6oBcCp=-TQHH9iBw@*}Ao33pJ0WhKgDA3V*;VpsWI$EMpn^y%6hezKM +zyiU+5Jc$#d|7ti)g0~eXF{jz%`aOIQA_Y?hexFgpy}L$5e-bW1iN&CVf8|Qe9vJ+p +zI#@gd=dOeqaYleJZ2uB`a3_;>(R2>VXgGXoR|{K=LUlWPw1l^}Fnqz0`IS|Ba~Jk% +z`kye2qAj`>G~XD&a2^Rm){sV89(Ai9wB$M-jmD-nsEk$s$BMZY=~@Cr#}V=QaE7*o +zD4PNwo|0{2*FI?Zqo4344Cj$hw-^b4a05gxK*Ruou<-zd%E|#k3n8%~CTv0IL<-%8 +z0RsGnw*$l=7u +zDPk*>=mrB*a-i9DEMCG1VYWoE5XGd@#t?n%!KP4YFAA9sgRx(KJu1%zkI^7>VOOoF +zQ^4pLrYnC6vR*2AWLT^|E60)J8SjBEL42ugvBWmR%kYT}4(KW}u`LFf0I1sBzLI!= +zc9WS`w+P5t0OU*pIlV&X`RR?JS#nWRh~c*%aN4&Sr^f3Bw{Ild7;l9m9-zR!w#8)h +zvlxU1LwOiOT_(0X9UgXV)|p)-P$UL|R+lJl4%PLdE>^8@TTcbf$M#rrYE-lYS4pa{ +zITYL0GYKx??K#K{iNRHZYT3M5-)vccXbsoiKv9Vh91U1pY0b7+*rw-Lx9eRL;Ammo +z={JGCAa;d7j-|aS>=lq>nB1d+0KW1}_;7=r^(dP2)l2e;`z;Rf!O_Lo*to)BUmuXx0c{%Y%%LS%DZ#o|l|;O_Athg5&_zCJmxJ;d$D%f1Q3tT70v2@@78Rl%cxN19 +z;3ywhw~7}wg)F>QuOjRFD)etmYZBWNOuTTq>LHA$ph`fSp;~$|X}~UI%i>V;Rkj9f +ztypCVl{SS$)gq{(Ym>gIXi;Ef`ePVbQK0BPJc}__Az*dw3@ML=OsZrga&|)X*^#Tl +zzt8UsA!!(3SS)Xc;q(~^}2~h>IX5|$(A@@0AJ^)vD7-~`tuV{Zn^utkp3F^wFq_hLp-JJ#(#cZQk +z!?Ah!A!KEAj|pm>L1H{&Eq0)2Cr|LJ`Y~BR%W+_{v`yfn` +z36QJ1b{HFCI}PAJcvrq3BHfO5G-UWdcj#?&r{2ZteQq7gBu|M7Je=xBqaA3((MxP@Knh4Fq=b{*#;Z +zo1L*-5!opw_f|B9Ds?bEr+9J`lhNS9xdau<+K$*8Mq|JR+rS39810EnI43(E$>O6G +zgpAoOl23)xc@2l+k6}F^3ObUi^(m&yqu*X0$_KJVWVemyA!KYkuYD}9(29XPfr`$E +zS+zT!iK4j<8xhg24GO)G8dw}h1eMf|D5Gg+zX>?!AZW&Tg59dF?ltJZTrXg}jY~31O%2Y+gQ@g|mw{Z?& +zGo{j#vuermWl%_w_T>Q3{05|&-OjaeCL^`Pxx}|P=dPuR>y}|W!9RC2GFT*oem|l@COmpd%@njQtChvCZQ1P!E74B=NUG+?>yyuo5&%OTk +zw-?u~UbM3HAB?*{dEnog9(wTXrauS&<)OtbZ#=yB(Z|D2@14Hqf#|m*>F9e=Pl1!0 +zS6m^@E^?V~xxOlImO~ciPnnT3cXF9^;nbNEYA2TPH(fj3HfNICvfzeldtG6v>E`Py +zb8oz+TwFA*CV%y74X>^_e%G<1wBv)L^qF7p|8Tf!xauf9+E1U}|Iu*OtG_{-K11E} +zeqG)1;RyW=$}SAkIipAE{*DXuwO^b)N^knjOgel^V)X6mqaP(k=$wo4@GZlI?;jaJ +zd`Mwx6lgD}AvV>G)b)?bh~ItR)2w5gW`V48*lNy(7jDm%Zm4{Y?uk(+^uMi4|9i0e +znkb6*SaUt^IpPH&&mBvi_Le+(WjB)@TlC3~l-NClF+(KxQzn?yP0?xyY$ZU2V~ZkPi(YBE!x@ +zwtbvV6bClwTeJ<6a#(%{gJv@DNJ4(sbu~47Wgsm)Gs?|Qv_0Kwhc!5l=YF4O912UD9G%Jj?me%L5WgWL35 +zoZ5$Zbt4CMsKewSaer9YYD8QH945o?X^b4;71!12I@}rpH~UIO>oXMwz1a4&O8dJ; +zrt8-t8?nR{`m+@Vr^DF+w(L5UPu=PSp4+C&&qZJ_aMVSe+jQOTfxeU=mOl|QA~NYr +zF8x^8p4`3VFSTGzXDa` +z!KfudBi~rf?Ku(U_H%nJ`&=ER@0303K2g@)+vAG4JIeQ$K0AG11?LIP2zhHmbs=t6 +zoqnF5rF%OCFmoeu^ev;(m|3OAs}3e=4?@iMR2zwX2dnk?tb>U<6&ZEznfgq<#?W(b +zIcZ&RvgVfk-+kx1?=0w8&~eND@4Zvgy{Kd1{u!~E-N)aV-g8sOJfv$f>6<&|??-%^ +zuH5K#x+@QQjh9`Y^Fz%w^c#%>F5<3Cv>HuqSa4FA~TZzr@nRBpi;O5iABV=H1kF}i~th;Ofnj0I8 +z)rPay+BY6My(qS@2So!`RID4A-7^Oj4OHwy#XeRMF|jGc#11SmD*JCW=3iWF+|=VW +z<_)0X2paaFA)3!!b7MPMjfPe1?l2Pd^Dv0zH?=Rj +z1A|zGL9Dk$4HVgvIjpfGDIli$$%)}s!#EOsE_|bIU7snsR0B*+sFno;g +zwu4JC01V$)X81A#Sn+X9cdb6tQfXAW&5u=E40DZMX(VPE>#Hn +zu5OlI_owx<44|9bfid?ZCiJL4LkNv?x6PqC)CogIY;6S)e+Cf$9A6!pnCHpUE34ay +zQ>zAy18UxxDz{h3VIAsu=c+0Xp0BFxUgn(%Y_96A>98LA-aD1u;&FhF)f}(?xT<^R +zF_y0EuK##?4={cI@k~9IwjMj4satb=MW$Y!XT`^QwMFxG#;ap;ySD?CzBoqu5ynU_ +zbita^&Cc+};RNNz@EpUpyb8M|;s-6`{%aIn#1S~r27fB)x@$HYXl39@=hfpQC~f^*pG%f)B;EPjaOuEJURTL#0M)f@*RZ +zQ(UN?Fdj4#28^p7V>6APl^Sh{$kGb;AgcKo=nrq2_z^qKaT_P8un9hJc< +zy{f3vB4GQBDhute?5Wc68pLangU5pRZGkI+YzV +z#agBQwpODgZ9R7NZ)aURy{3a4WM@{ynJsZx`}Ws#IQLaQJJVgMR~gs@3y94XY(n}f +z>8cwLde}M6+_%6v28rlkw1kDu>&_BKjZsr$ES#w?sMbM0tBqKdaZC5io*GYRxO)HW +zj%oX5JzKZ5R0UWSeEw8OSy!UGu)PXLihUVpezE*Ge3Ygq7Ife@z)A!rNEuk5% +z)U|42hG9-jHx_QTY%v}J^2`D9z?QTfaML~Ix(9yFGB(~CfvLi_5TboIjS-U21EQItUN4=Elmv)n`OV`rh*|bN)cC|$+Z$Xkv +zAb3R@42S>CrVS$ei7ND`-B0Hv#n~-r1u~MJO +zC-ba!Z4yBk0f*45Oq`XLu&At(MftVuT|f%KkKGiiVLX?`Xgqis<~_JteY~<=durX#apg3a#)*G&6sMj-Wt9H=GOUpKeg(l3Sbz9pE{oJ`hzg6H +z$h@z^bPzB>8zpKENEG?7gC)k~NnTl+u`#Q#oo&SLcKkMDO`n3fIRO(k)#UbTVht`-=}Kj$Ld?t`pi!G5D)S_2!FnP%17IYW=SP(h +z*u*(35%dT_85Qx0y4K9v-yB~QArR*7LjAA-ErW0xmZ8qL-3Y7q~yuY>Mp=qvE+d>fXJC!yA +zd6eZjza+2ZOY-C|$t(R_-bvWa?AkOFq6wb@uDS4p^O3sodhSx#7N=PPy +zpdolAcop#45{}d0aA)$7G@NG1;=G2zvPZ&$a}&jBMN}C*+Z{eXSj=@MA)Y5ECnaez +zE;%H_VVRb5@aH5YjIo}LkzK_E8gmbC^k;N%W#byC*1^!NE8!JJYGwTr8+7gkx8sG& +zS$Zqe)ibd%@aV2tDKQ4`nOw^`WkR`C>e{oW)D;M0_~s^DW_WHtyKD9M6r_ +zw=ZTf;F)s&(lrgp3%jdV+=c4SjE$h4Tf;eCLgY4V7!@loJVo>G2?%NIjRu; +z-#{;!9DO^_QJA?C3ELO5l|G8evG{fPlHsH6CB(_BK%|oS1bazWDP9VTVSpr$Fg0)yX!FC198>5HcdlPg^L_-#n&gdcIMQ~pfdd&!0OeX~92N1PsxYI=5 +zSS?~u`KxiCd>_VBnf`7N?`)qGuobde>_DjTw!LNCi +zo(7)8SbYI8(j!>A77@uI?0Y|XgY7#SAgnEmYw%;4e3XCzv9&e>5Mos*3)|KJ2)ks* +z>kwWKjmICtK0>{SLuAhYp4nK#uOO}s4q3VqzwLG({6h$@0s3TO@nF)WDRl5h7T2gT +z2NhjB_FSKdR|GrF;`e1yGb8@40}ap%`o9AH>KpPQ1Ej?ZfFn6zv`BOKrE?Hdm_@w< +zHoUXA!RN6q8Mh?J1&#y9>an=Pe}t790H#o>}@{5d{kB%zeOYt++OjVzF8%`-NTbL=kkP +zgGdrPkeA`=>)0sJ*t+q(MlgqnW)mQlWMVEtkWLss4Hv7wA)aJQFe&AgH-;vt*D&i4 +zWR|@DD6@P%wkr+N3-Lt*-%U9l!c}_s86e$>g3=-$%ym$Nh%#UIF*+7>&*Jd&5fchD +zz`7@}m!F6x2ss%U4SRxYAC~B}XY&4lyo)wo`P9Y(601LgA8W(fAHWZ*3mN5k+jB^>J%sp+l`eW-qtDsQ +z%0JuK{^73(V>_$#*)*grkd5A9a*(2&v;7(wEIy%F`OR3GS1iiE*m^Q^Gbsrg6Dd15 +zI3vnynYrBn8SHz?tGM8bpjyO$f0KU0rhUt%CBBV}m+?D<-*@oq!jHkuQ;yh1AzTtg +z(()y3L_^GQBl(@Q(58);nj1+fJ~7ZaknZH929D=WO`9tAvw93(@5OmQFm=gM7N;nErLMzntHmK?J +zS$Bv07^jtKa|~kOI>$}!_PLSyW$oP|*j;ZrG2acR$WeSm!GP7OyX<%!Vi4Ws9Usnc +z<7I|Jj2^5@eOTT-N}z}KAL@?}zkG3qQ4^YO{KgaFoY68pDws}&W_75wx~tKA| +zbitHuTN{g2bbN^RbdDgW8lWl +z8T$F=={o1dExIcGd|jo^b+QX}bM>+Y9k=gZRGZ6t@~3zO-@v>%Xg{7OG;oodRX&2Y +z@dh-g)Z+`Y?dEvZX7i`b<@!*0!;9*?9wCam_yeD!F~FGtPWN(=7YE?e>_%k9sK0++ +zYM!yGR^JpTJDx6DSL^Pc*-^c}&OKW{Ht+ZdaOFmQS-E2b8P&&T9xr!z*?pyD{qwpb +zkv`b9+p)g!{&~*G*}Qq(&oG|Z{~Rjp>A$g?*}%V!boGgu-A=sJSw?c(%PZQe+NW2Z +ztkq}KT7*TN=WF$d)$3Ki5#^&?#O!gWeluslV_2ZL|4X@0D+lZJZvzI%fMcF$;-;=g`>9AcY$J|ep=)@x^Xtu(?)BD#C+Et7@M3C5Af{H44yH+ +zco{CeL2LnC;FZ=G$KifyPv?NzxmfUWh;Ck~L@%md;9Yd(V&O`e1D~zX5((LW^j&I< +zsepgeO0}4wHzx1-C}-LBXKi{xMHq7r8;Leo#X^n-J6O#_o~eozpXY8 +z3>}}+@M28vEVH884MB-+~q|>tW4n9`z?%1|=5u +zri8H!gII>I90RVG83{`$Rc^SB7SK6~k(|&+!?YSkwp%do=^YhD`oqf!gjhxPnoU36 +zi$Q!FgTVe@1U?P$vDyZAw{Se1T33s^o+6VmVAB~oF_LbBYS6}99Bw!`2FC0P?f7MV&m +zrhXY6Fa;kd2&_k-@xLo&_{M=5>86gx{r{C5mE0Yc{`+_|F}lR5=|bQ@qHve(4CpRo +zr~f{7^3k!_NraXRirUS!)UNy$+n#7a%ww`eI>uy1A{R74Ov}Wb{4G9hN+v$7%&Og; +zl^cbO+;}VGMmyqeVe;V35MP+w!{Wu*WegTShAU?ju!ttR;EBw$I}n&Q&PQK|UFImb +zSKc|Vs-P{Z=4a&<&dy{i1_Rv1??CX`-@~c0zm6a@W)sNpV_XV50x1NSM)C-P&@=?0 +z`4LleaV!W8;S)n}PhE;2P{mOOKW-*=@6#6O2`(nyKpj9o>~6<1BE)V1f;M*@0)~@c +z4WCCiVP!Z(Z0fGuWY5N{xeDR_o>l(1CTB3jfZ5vyct +zaW{r~wTGKRnL7c9LB_gUAW^F<)-QZB9B-etgwM;a7N~)l*hxw9a=6N#49k;wR)!&F +z;=Fc;ee8ZrlEv0xJxBQ9PF&;%uho-~9?gd?#{SZI6T*<-p84cHGSe^UvE2FqLGZq6l5${zL +z7iTCg&M3Gz!`fgg+)N%K5LU-T+cEi?*?@dQxHj{5`Nwg~m`O_bE5g;8Q3V1I%q~2( +z<8CISGI{hKWKc+{)Pp^`7xo^=zH3?D71-Iv?&IJJj>IfV_+xMyaRrCTV?5fYnL#Wg +zzK#pM@Pp$D&S&G`Ak#8B!S3onW`iy;A_5N6>*{8+ewZHjg?l)xE0o{z*}WSG!R)?G +z7Jv3bor76CM4T}xL!^?w(0oGx +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 +zCTNp`!7@|>oDu&uD7!E|PE^W{Gi1wv4A>aR +zPVx^nk?*Iu2rz~u*8O@&0y*Ewo)Te +z3GG=wJ>+sp!%5EEGm<>=c1{>By7%Mhq4zGP5A_dneJ@+YSEQ40M#}m*(9#0P>G+hO +zORv!({2}dzudY)+m`Ru8(_WYLD-y$_>2!wPq_4vo;2Y=`AbYc{%WnyR9E`h*$>lW= +z9(?2YkGt$P?b8;>gKsdLtOL_wx;$Y)4@{xQ`FjV8gd_73NWF3%sC6A%V@wo}^O&Op +z4}#V8;t_yMz_WX$N>u(4qig8X&en?EIrYdtglsSYwEg;wkM<8Ol4V?9Jt1k=L2U&b +z2ZJ*a;BwBB7Ms|R)A_eMQ5B6C`eY0}A7`^9b7u<5FDnl(!o7WD?3`$UO>%a_I4`wp +zlT9LYV36ZDd@Q-y*kZvRz+@3`Aj-3#Ya&KelYi8dckXsc!FMNzWcMCiuT7pO6TFhw +z(TP_j@66<(R`*84RJR%y#3P)D!*l0$o7A7I~^hbbi!xb}$%QQoAa5An&N +znmySvW(BDq>Ls6udt4?zuH+ASp_H>QH|9sfC!mN0jrO1sifYF6^+28npEhtt$}r#Z +z2*#!$PBk+=DDW2WZMcx-_aRsw`4e>5tW3yk+_Tu))Xr*=urzC|*og5t3SiFTGzCin +zyV8^f*wm*(r1gV#N?51!VA}AX35tRQupke=uY)hY=!~q!-rW<8vHS7N0)w-I5bneg +z7PAAX1oZ^`J_}1x!t^_E!?$Q_YPmFd6+sWp=fhTf$3T4L3G=5Z>zTnP!pSGXAAofg +zJ`oMA-HR+8$c_LYs(rcKr(EL8ju^ZX?VmD^ECI+YJBq#fln|?)gbv= +zm!us+65pRc#@Vz(tC5sg5EPQQr122qQB>+^iaMVF(bo>BlQoRzKZ~47AaW`rWqmdi +z%<4Nftr7n}3h3399&E^Y+|$H-Qc;svO~qx2#bqE~BkGZdP5k9h5C{U}VX}cf +z`mgL0R;KawhOK!e;{v+s~5mGH4NKJhRMvA_NJ_;R=+xH2!8+)cntm<4_uGGtd+azmN1Ty+ +zv2TGW+<7{VYC^t;mJlYx_)bqt +z@!;25+QR(clVSb@oJfap%^Pa|-Dw0~BwolLeMQ^n!N!=C(FcsDC!cTL;Ek41-`bR`egjU@9N!vrAS +zWjG}zta*c9gLulf6who7HK=QwLMU4nL0QxcWEE5%aJc4yLvd9ZQp1*p7u~A$YiN^T +zMo$wYiNs{p8fV`fYEDX~hNn9bj*id1l{Ny-k081AO7ysROw^Li$zmFw8bq9E@@rVK +zGz;Uzr(vnv~MYzIs^40K6af=@_lfe+tENWvuNr`QYS2k`C6)WKi{p9q^!l!KzW +zq)8P-@XaT}^mSHWyKTrX(;tX$W#zjISjs1&E$#UFVOyB(!TaYgKS>R>xfy+X=|L4) +zIY}&Rc(6TuGHGoSwcnd@c27PD`W=2hepzt6&sNq{t4D3f;EK* +z7MVD?DE_x9ST`Q)c1$GrP-g-}n8Wyz*7mSA~fnDqxdD#JD(8^rr4&y&3&B5_nh-PzxVkx +zNSIA(Oz79k+y5fnD;n^?RgFG}!iGD#&>YoKJBB)Fe0TiVgSLv)E!6R}vHQ +zljnQn@kq*>*)?= +z&YX!NVCMA*;)8H!%izSOSg!^DI{RbmoR2IOfPi{A)ZjdC#g{-OS;Kid!>gVh9ICs| +zaU0R8iT?3HvpnSb)KlGCrH#;X>T|-sV8Cn9G-982AOm6kTc@P6Uv5iQbWhp2 +zP9*7XZsGDKWPK;?Z%MTbWQrwEyW}_Z*(*)*uX{ZFQb^Gfu4DYWgMYl@-47q!Z6B<) +z&G-_dT4ZD~xnXnn{Ng2x6XublnDsrIyyUyWB+s7eJ|nh&d4^ek*t7kAmbd-O{O6s| +zFMH<*;wIVAhJ2)g${nRl#a@PIsqbFCOw~^*2WvouEO$FCf +ztV%7&`qrf)CGmB?SYni2W;?DwFuP!8n8oiU+wpYK;o08=n{Z20%hk5u4#lf2I$BLf +z>nC;l8~1kmk@KUTZk^;vhh1O8;j@Q7?#vzT-xlvOw#BzMMZdTG9z-c|8;L+>UP!{! +zv*NG>x-aqjF#aAdzbqGp8_*+-5a +z&29Uu#pXga8Od1#xr1~#(7S(*)#lm`4BUJvE}Jmw;VA>rZP{RLYK4(pO=gqFhHHMh +zA|9R)j~g|&cD}K~c;HXEBC_9+zV3&q^pGR*K%#5x=@?;ezH;+fbN}m&t9BmgT=f%U +zGXZE)_gasKT|1B_E|4nkU+|SZmnt8^PjJeRv-^jqcLmx<2H&sVRnmTzpVQf)nb)b3 +zWSe_75~!g=;zvuF7iv>==^7HO>^xGF%}!~oMR!@_&`IH8U{^b7;sZ1>8lun|nAS+= +zyX4!b>jHypX2e%hw0ZajBUI9nUHy}X*&bHYt{db&XQ1-PipQ>bAHk(^@V502v +ztTSwT&g6lN#~ZI>`QAiz*bPKJSr}}^0OlZ2h#joTQp@vl#hoHoY*dTnA?#&?@Rd&m +zZxX(8Y@V-#2+e}MM23dgc{225`Q~9YzTWpX@@l2XJq)bKJ?ZvLfZTkjegf%c_|{0L +zRSyT=btL+BmYkPSVQeH1Nt#4Z+u3P9!I4aOd_Rdu*mmJDickqTFZg}0an=vnEmct` +zk6$Q$Wsq++`TL7D8A;(YVoq7L6CJS9Fv+Z&=ztmH|M)DOnc|ctpL4z-M|}QKkjv@| +zJUeJ~NIJEe67%4hw7+Ctuw-7buX+6$S*tbKkzlQ_c^xWk@*^$w#~|vlqiZVzEYc|i +zp5;b{Yd8|*Xl^9&dUhlk8zhcy(2Sk=aB%S%dG_%i@Ymq?wq)#RK%E)kXp9=tLgx-u +z7Ll`muyO<%lwfVJ%){`C*Aq5VT~DquXQiq7A}#abl`K-@)BfAjY)50jA1f(UQnmvZg1B42vi$<2{J% +z$#};t;eS-q)uC2oe3mqR6;7i1blqNwX&H@Cf&2#?EMbzrIZUU852TNtxj>Dd8R8SO +zuWqXe|5$=)%jL=NC`NQW+%n%;sjR+;eDy@Hdq_Uc_cdV}#qsH|&eLmRPU4APf@kTU +zQB3fW9h`mz`PrD)r>+YX*ORMl3*vpVseaa*t#f|9$5fj{KFjZAzk^rs_S2l~r=k~$ +zvDZ=eAmZCTwsg;~BD{g*RkioC$7kFL!f*7zTO1j29uE+V3cH|QtMJER^-B3}4^44V +zquN*Vfm|nP7YiASFtc_3b!9qPrLC8As+RNrSz*$DOQ`eVw+6>OFBCAGM?(TVhKC2{i$t +z)_i$m1)|H!x8gGP_XYbROD5g7Y)Ql1$Efx(wqnP-ALxtB4PiwJf?y%>At%+dhM}JMkv-aSZolvxu^>t_o9(nEcG}x!y@qH(xqINS1+zhUNwsC +zpGsh)+sZ$&Rq`Veu<*031uQIKzuphA>_4d;8 +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 +z!+}q)MCp&CuWO#fz$9sqxqhU1{@CUg +zp~u#9zWAH|*KSq6>F}Fxv)-DxN#dGsZPBVvYuxPNZ)&8~BsMKK9slRoQZ@|p+scV +z8~48avgu`0Bpn&^{?yxNq(@DW{6AjxreB_a&J;0)r+973<#Ty`ghSK3^EVT}Y*Rj@ +za*zLIemO6rIS!CD{yOd?-Aan=E@qGh&$JvxO`5t1XFmp#fdq(Y>E)d>obM9cqqlkDLTx|Clx|dw@z-c +zq0hc^rm*=ooAR4ZTBx3*)6`qgabBNJBIZyY@|~n}j7-xC58d|Z<#T9)`S$6iMl*(X +zgi$3=G@sR{sne{5R#QY!esfL;k>_XF6n%GYhzN=!Y*WZ{G)XH6G8J72p;$Oa>#ks1 +zE?@J;L!;iJ@ThloIPG1C=Z5fgZ)5mo?>4mC6G;KC%4C^_h||U^G)3G*ciNPHpy?3c +zzE+NhNPd!ZQY9$UK1|M-kLfS-nV4@eW-5DfTppV;jgGOlQ)HPYD`wl29NI~fI4(** +zO@;bx{X)N`-`a2LHv@fnpy4r~A+WHY2O74_BK?%yYE!Oyr-wfBdcxuSJHnCMyo=Pr +z?oGoFN2b}7FGegOavOb(S3ES?S*T7+cvH(GN+1GUDa0Cd8-`1uFcl<~wkJF^Abq_5 +z5 +zhrl9j6@Ct2DbzxA@&&RZf6Z+U3T29@E&0G82(8_zcdU=;usVI`IwWi)Tmprsrb^VmY#o +zCKmqe$jI}tB(Gdc;=H_{YG12&#BAZ6ORf!)_kkrss4tAn!2>ayg3J&hJgNvWA)LB) +zY4E^ka_16)xoYfiwjOFBonO#+DmA;X{%bHotPE@;32! +ztW#eRIC{Z?4tmkSQgo2GLpGuK3%#G*5s+6lUWq^q)Z&=Gsj6jph>FvKgeK^JCM}hXq9rCOuRXgmq(zoj)5w +z{IB%PU +ze)_kx0xXLh1LF3WjzvY_?MY=)n=Aj-y#D@AOtk-4KZ^SqUZeOJLu(X^C_af|7mE8C +ztzl@7;vN*6P~3lJlB(pLv}IZ3{8wGCPX4R9+^fQgyKnx$J}BD5e57FN1yAo(N1S@R +zhn&s)eqXy@?Uvfc7A3uHExwj^((0tGhdduy-Y7p*t=OR2H*0%+SKoPaxJ7Ge)mrC_=Jj55r1OS)e?zCiOx~yIOvvB~W&xwN*P7%-qwAZ +z+c$h$i+0)zIpm^BQ>~F(x7;@17%=7Kc~0+3^P%=T4{-+<9$3)kJiqA9ww49@^U2l% +z$O~ttIo-|yi|sMP_(E-w`tD&%Wa0965BuZlVl5e7xO`D! +zVRr?YP$xh5tgZsV@&Wk=JQ^jZsr9lL?)55DA{Mip;K`3bJ4mO_x&%w@WOZ!l +zAOPm7fN-?Y+iZnkpcuFH6Nwf|u4%713t!_u=9Q$9w8)((=UpE9g}Yk#74>&-xbJ}t +zU!1jM!yWaUW7hmtbs6s#0-dy}}T^N1&?&1!D9? +zh}l$oSbIp@q-|6hwDk%-%}42XIdOo0JZjnyy*&Aru)BF^a*oRspFFdsaNmjjYbNW- +zjd(s(gD29~$$b3ZTw81YO;47aOj}GYq3fSO+#*9kjL*M8`MW{Gs3}Np#b8_WGa;`XJV?-jEB$@EJ}r!xBOyk2r@pM|)a_5E>EUfeAxPAATz0GW<*P#h=@ +zExir_Pry##-HIRnEL4|l5)V0^P>U%}FDqV?iRGRd1FN1-u6Cbhf}8LfiF!6hH>po-vP2rTtlzRxUEd~#H#M|v+;~zP +zFu69p>JiU>Qi1kKv|k@}YwOVoJ2QCoqq}IcXGeo;PTaj70~|mZ(9q*u1f}`-+VQ%AU?Y9jkUJDtMh5AZzs#l-nTQ|j-v#;mHTpWQKy2xR?Da#O56H+r;1h-n+lk^IvGWRf +z7IHql$h)N>e%N@IrODEt_&*!zfFw(ocnCqLLnR`O@R!Hy+$3G$uNvtd)VaoAH_~NN +zKpN%KM*4{Ki8Kb8FG&kEszXK4NQj6KK^Q(x&(n$158>CQEFhhuMC6`K6HB*gZ||Gd +z7^Byri~7~j&?s365yKkeni;>-!S9B~)?9=@Th~d4Mf8(m{Y+;v^aA2XH#T(B+0F0e +z(2a%hPjvGRx?z$^2;HniH&%30i*9suGlXs!+3Z9&f*-=jU(ZMUf1VFiJf|ND;$Qb; +z`G4-m1m#SDP#YppIB6Y7oKpb18b7 +z7~%8U^HQ0nmTS+OylJ0T^=Xg?Mh~+3XWvUSL(@KzbkWG(9H%cod(0j;aCtR~E0M=y__JCuu +zw;xBr9RKjVmOL;SBB`66fx|9F6c({=R=S?fs%03zv2D%t1hmz3Y}raW?L{A@fxK<`V5kNBA><0w>w +z?B{fE)!X&))NxKJ#hhV^AlQFlpIjd&$oT7eJO8d;NZx7GBYxmkAMr`OIfh}ITL^?T4)MB|rzV|8;$Rgwv2X1V+bmKfq +zY)WJd3KZnS3z8-9mEp0OG~2IN>y}Gqf{jI7eDe`jK)R|#zw@EL->g& +zn>u2A(}9=*1@#va;kSV({{eKelSU+knkBcCVe7NJFe)q6^(70xrlPq61@?f-ucNubd=IqS4&+Dev)7 +zIwwhT1?Bij8jV%a?ghgmdk4jl0{SIUVfm;QQuO1K`p`yA=Z5NrMrqYxLHex)Nga^! +zjub3Tipn5c-FDzeE{bw%lX{it@VHoCQH%FpbPhl +z3bJ$Ydtfv~9%5Zj3igK9v$|%~`!lQwXvkm_Bzs}`qC9qEANDrLOGkwcWE{co1^m8= +z-?#D0%nopUNdR1Vz||DgrQ-s#lrynnVjI*R8SJLrYdyagG(s +znsY?ID<|%DCkk`4_d+SC&Thv4@f>F78zQdYSs0G?A%g%xK^$uty%5gof>E|K9A*53 +zohzs9tj=WO4bfHMwc)4OIkq`B% +zkFc>}Ihp;YEk+pc`+VC8I# +zg5`~o8sn@A;oYc)JZj-lI8OhsU^|Qk7_vITH=6NM9$XwE(_|XJv{;vYN*=Ro)Yxbs +zxHyQO86E)SBKCSOYm7RC>_bi+b_p4ZE@B==2D-1dfpqj_=C+?z8r4w=tY0qc$ZVBfln`Um2bi$cYMY{VXB +z*_3xD(hj7V2@H~vzDsK$8k9(&$!?$&_AZ;in4a&3&}CCx1U5}qzD?N%*pP>be=&ol +z2>?fZnyEjC}p;Ou=)^&cCfupl*v3?6}$Z~QZZ9IvT7JH +zNB}`tK7oLSx=xA$cD6Ss1X;r%-{+OhY|gxL6XUx-R)FWmJkRQ8%*VpU##HkZJF{2S +z*cQuJA~LbGJjF}=en8BoWc}eSG?L-9031CShmfp4_$LM53MrE^>&Eim!jck+%^Hu> +zx*6M;4_zjo?G27-1LiuLhh7F)pU28d-bf1}n_{;>7RgsUy|Br#N%O2?H9keO82Ludn6Pl>p7ORv8#cP9Ke7U0d4+O|J*%E8pg)b!Tz@v$2-h +zp6p|c&UqNEWB=^-V9|s3+3k*)Z@%4b=Nf$vhGIT$eh4;z$F~P#OYp4PxqTBJ!DUtJ +zR&kCUj|A)O_UOjDKo6yzE0?hv!Kx)y%kW&ejP9JSCW$-1Z?cgr!lT&x=!fz1MZZliPp0%O3EMGSG#na8q;?CknDN_wH$E +z51}KaQA_*Dalzd@{r#zJrg-a=HrUNaiPPyy2Q%d+6L{K|O)6EP;D@2$Z-jzB00sY5 +zDEKhTZo<6IV>+qhg3?&?M?Z1mK#<}LKD%H5aW^}i1#MH~tvCH$-@J&vH-HB-4<7-M +z8ElQ^C+v<;|ipwG}>%4dfL;c{(P9xcJ~m++;cf +z6AiSC+S<+C`jxuNq2cEEX085AmJ3H!*o=+FGL_g(I9q21pGMx~&2SF#P0casQj@qj +zLD?}&O$OldP{?RP3|!Nksf+gM5X)zkhA{wp;!%fVI?RXac$TL@*#XElE9H#t+-Jxv +zP=A&k1qaeWr@4JxGt7?X;k{HzIdxHu1l +z&N+|`m(wryKumfGt}5j`v>;$ej$6AN%5q8ZsIC4?L)5!oC!PUD7tcdBhnVg(gDEJv +z*?u?EBS?gyQeO7?M@TIcxge}u>_C97@gNH!S_h0Sax&W0$85|Bv@NXE37PchQ8;KJ +z{|0+Wxh>nNOa3E$T%1ONpQCDF%(%oNyR4W;4Wv5YVW! +z6o_!r+<(=rW +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 +z6wIuwP@zoy>yo) +zs*|m6m#)Ml+E&bCleps^OhU#a?!_clU=nv@5@?PZcgEF$NB_^;^yZgvpm;k1)-J>l(5*(jEH6}3A8h^XOSEE*0E3|6$_ZyQlHA^MZe)X-5 +zel4$8%d1g+wQ7wD32K&BJ1gP&=|+ZRLx?LqZlY0y+xc4Oz#aW@tLCAfbOPlM0p$-2 +z@Kw>t+5T*`qN?p9w^9X!dS1>xU+D`-4s>6kW)DHD;z +zOE?9PCaO=hzFpZh^CU}GbhWQ5nA8H(H4#KYS!6FriY2v69l0ZGD0if +z^daa1p?`$K8M#JUHNBd2UL0HWF|v%9gW*<Kf36!*aTmE}TPo999U +zf};gRf^k(zjuD08g-TE>BFrB*hB%kU>98b3wyU1R&hF{Bt??)#H??inV(n31nKUQ) +zs21I+?exn0_MP2dQgb4YCbTKul!i*?FGY*JsT>*AZ%Gzwi?(ZD(te8oF)=Rm2tB`@ +z{M*Ts;su|j-En_^O}n|jy5F0y_E&(YRaz_CO_54f%dfC998fE*G*Z!BiB}U|HN1X; +zR~4@jyynwtwY3tJOsK@#sHlWG{=ceIqqe3h+C39+1*o~}*r;LMc9PMQo(xS94y|h^ +zj8-5DzrMW4Wt(}Zy1nv1)xiv{8s9@~RJVSekpYuIFA($Ij<*Rq;6e<4wOU@JR%_KW +zwM8?vg;nZ;O0}vwQK?mR&Fr4;kB-(Jn%!P=VAjD|%WKr7wW_{!wkp^oDM90BM>QM* +zca2)(pK{X&wcWE5HK%8FwYL|!4x*8aM$I*eqCt5L#JcEOt>?ZXSG|Uz%Q1BAY+tgbR-LWYY9Pk5<|J$8 +zBxlV^)M&al>d|~!GFqyY6esxNvi9FjKGIvJwwdd_WvaI<;5%}`SJq}O^_8h}lBJrp +zL@VhnR*MG$fsUQskEnLw!i@IvL$dHl)Z$IuyeJ9Yn=IF)ZPrH^^6Uii=*gX$AN`he +zm#V#Rg0lH0tF^1%Q#IO*gsO +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 +zXYl(c{C*;2&H}ru@Y$YUAu?AY&U4neB2en +zZ5((oKu>~kxSR`lDCd0Bmt(k=TM&bfg2k3d@t-v@U7g{HCjeQ=54yEF?nd>TCXsJ? +z+Z4Y9dhsU)!4+|GZNy;k#?Vi*Y0S^iaJuIsWr%aed^(mcb(^ugyr9zCkZibpR{`76nh +zu@K@q#AfIVt_^W_JK*r2*?6}7HO)-jNHbvuZYNK13a8A`=4wQ4yTjM!Zk}H7El@9fel8ZC6qfqR= +zzzg#5yzbn2nkgtZ&N1@1<`-nRxa-y4J6Xl@-bM8?Yd0q#cUUgX +zRaNW{wx`q-FRc%vu{uw)G+HjrL3c8mHa43A9D)k@p?O_n+M2OYV>mTW&xfrIFsVA! +zuy(dOv;9=M)|b5fLqVzM*~ztp$PyG-;bC#8;iMdZYOb|aF9M3qN!8I +zx6r7n-{Nf-XH(d5T2KVt=aZ8tvZH7SMaNO3qNtt8ZoYPpt|hG}Q?O?oM<9MO?eKmK +zCk(SKQdO^NFVhx$bTwIykTKn2{t0K2<^8E%_kOwY;!ASl@RdYO{R>^DQ+%pnt?w9Y +zD=<}j(4t*=C#|<0GPzn$e{gL0={z`UO0`m-Rx)S!hqAq-t);xJL~SiAW&Q-js4w-` +zFmX2&9T$Li?ApEx{=q7U!CVzy9)EoIs#U1K(!1bDWRW +zXc331bm+P)2JIfQ9u1 +zvA$U>ChId_{k)8?p4kKt+g}J?2S^qndd0=--nZr_7bnvv +zai?RHNxG~qiu;l%#5JB;b +zsI!eJ2e>{&yqMgaWcn0L`xNH8+%zUPipeSHSiwc35C#Mp#Hjjl?Cm6~UeCRb$+7WI +zVsfLHT&C`J5m$_ai|T^{V4z;ZoXNwAm9(P!IAHv3%!j_j#$pMK +zXXrXCULE(942QXqaXp0V7@=HTbb0`D{{ntNf*4jH3my;Rtb;CmQKUCv&Ho@OCBdH1 +zL#z%IG4#qKxd6KNdZv@1TWZ9^V>Jk)2{uoIoR=GSXXE|~*4%BZ84F{6BoWF7&eVtz +z&w+Rx=+#a(9SND3vX~zx-vC)qGVr(oX85=Wn>&uV&eU5L4vTY$Z&akxAq2!Clo)r9 +z>+r1}+RbPID?`Z77>je&FS9`JHA5^m0Cle1P^TCC2SM0IOl^vND+=Wl^XR6Vq{HkS +zgBBu|uK{<}48y`Jh|uHnP=L@Pv4A;)!d9Lp&*-~s?Z?Fg~>PXG_+_7wp!LjvfS$mkeYi3j1^BLwhc +zFbN_k9cC%ZVGu%?0Xz#pbeLX_kZs}HY|j~fp>rrjAxca(m@GP>-qI+(tHo@P6L;ZZ +z)8hQ#=Z*(Mo5syq5^hUyj(TbPYB25x!MGXwb|82LQ8b)y;j+8yVQca&ypPptbof`+ +zF}{!Z7iP(7bkwY@TUlqs#LRNkj;HO8*|^lfZG4Q~=-9D&*AwfQNU)j71`{zhY^;T= +zR>8K_=!0m$V>vUh91gAv*tZ{S0J<3W8JZz;H|>j!fGwL(0yuFnSLg;!xTsRYXgk6$ +za#_qeqq}j$9WuQEl)oNd|EqM-Q6BGBk12Hn;{ +zw>N@cbNJCKjAu{K{s}to$3;yK>ROHZZq)ZVzdgMXg=cN|8ttp?rb8o +zIv1qe!ssfmr1wLX=sTz>Jb&mZumKvi6jfZ?9n83zTxE3L)nPkTO~K%CMuT{x)iwd +zmqr#eGAg5i6F&@Z%u(`+j263?EX%&>#%MaDh5v;Nd*_WZY}wz)unB$sT~LTV%l@$<)ipl}yf2D65ASZ*~B->D^Q0*n9xOv8yKMJ +zputx>h_!w>69fJ*j^1P(FW}-8&~CRAarIZ2EC*MHmz@E)G7J=iJW!qHyl;(KW~WFN +z?lZfgOD(^MuSAT9jOsX*k^NZtt8Q#Q^R^mn6PQg_h>9in=g=zT1#y<}a7Th-()ES +zJc?|A%RgYUu!1-X9sPFTUVAJ%(B|kE+%MbA^}U|n9OR{vmipEUxNGDFZz)D&eTe&adZsr-zdXJc_MI}A_Iq&H%o9FZ93=tvXCC{Q +zN;CqhP#i(O8^#sK2N)e^@!e1(?qz#Tsu_icz)(v3FvEjXziERR{!Lu@#J3x9rxW#m +zkVV|l$@Q1n-KKppPk&Z;>Y%{jg$YG5A;uE`4tD+ae!%hz07pz|Wco`fqcI{tv6A`V +zK?4aX7Zv9Jn89%4j|Oh!0|YafEnJW`iknzIGVn0N>A3mw59qrv)7QVy5G?^XJvQ7i +zLFfx&4o|^eKMVG?wQ&qrm=F6JL8l<6!09SB?sbr>nhXsVKBK`uxVy>~$K=8Pp1iT& +zne6TgobdI|j$YW~M0qBQu!-7|Sq)J-TU3}09eoCD5pp|Qw;SumWpDA$8*o5WtWBV0 +zK1O#-QF)wA{Kalv2Hz|eXOuDW$<7Mkfg6&cs=uwz==ID@S@jKNupBCk9)Fg{_n0}NsaE2cV)GorSPCBp8pln}KWvUYLxH2aoPoG2JahR*F!Q +zt{CtHqTw2J$e>49O#Nk_3&&dU0D;(fRB9u4VK@=PG21BZvGpvu9pqdD(Hu^bc|!lt +zs1w0G*n4LTdk-`0lYY;oCU^7Wz_pbIuHiUy3~r3&0lhJPxrpQywhxoW_rcgtjL-Iv +z@w|!sV$db@11y>OFVZ22%wp^NHEiY^Ato4gAtpddND@q)J_%%HHsZfpLvG}*ULdQj +z{-%yh{OwVkg+-X(dSiZ8Hox+Wtf$<^?oOJV)DIsFgeE*fOm4!1;Nix@hmTzZS$)R8 +zoG==XCU3z#s&uW>wIvRaIh{BgMF3XjdsP+d9dXZ-w*C05%Ybtqi)mqWBw*{ffa5c_ +z{^kyX@7c`kOOw{2(cI8QNH-8;^66@P1DLe5#%ZUD0(*N1(1f;{aHZ<@vjoWvTOw56mBptRAD$ri15B2 +zE;`ipQ0(=e!AcP~Jor?OD-y?*;zF?h5eSfkef#A9k6sE=@IA&y*%>~;WFcg*ZDAQ1 +z-DV79pMd(rq9*8jm{uB8dxY^xKR!aGlNrXK`tDcIKMgryhkIqlX>F-P)BO@ +zVzqY+u~0`n+IKtzjo2Mnk2*(W6d_5ARYqgJ?|8$>&Y%b2fU0K1A1t~19u{KYsJiE_ +zRa+k29^xEDk4?lPIO@M(pSY92ArgV9YD~w10{0+PH9J%@q-$2r`Yc_$av3fxusZ9i +zmOU6m5W@WVoZF3W^{vK@24vsAav4-SH1f@l*d^SXV0S2D^B?8#hYF3ppl^O7t1t@{ +z*}UpjEvgDGU%8Csq4zpydye^Q?!JfVfGgLq-1_^O9T*8|Rd6+WSh#A9$T7j1 +z^4S^FH|QJ4?+F}g(B(ID^bR>VOTFIwuI$|Rzv9d_z`u9nNqzJO?XHDz@>Bn}s_TXcqXqjn)>Aj5U>6o5kmn_67c~Fk? +z=#jpELsJD!6+g`J?&c!U#QZqykA*^5iZzMTo0_b0;KL@-tRpinnOsZ{-Wzk(M}_bp +z^W!3H>F}D2J0G4an79pFK~G4B-w`GT3bIWm@Ejo5-@{!ZhR;x~DZ2)!Cv(mViZ&U? +z7$(7i8v2ixU +zyYWqXA>Zc9*xvj&;v$&%wl~Jd_r`=*6d^JgB;OZe_y8B<6Cl%H#Jp|n+jp=7>G@$? +zoWfAG!&C=RSMVDpQIK>E}i}+Z2hM{RJBX4c*c|+k1?(S>mPF?aBCFy2lbv@1c8N +zAKVw~id#E+aSfFaUy8=l$?KS^8K06X2q2MseUFo3!Z2S?=m7BN?l=6ouxr>nN<(y9 +z-(vQfMTpFfiD1lN%}q(Vw=vnZCr#hT%nR!^DfGOZ&G&80XB_W)p5;4i=abpi!|>As-yG5i`V(a<-rfbsMlA5@-v}2t?d{A5FU2X3tK2T- +z*XK+D=~woaSdxNwk5qT`f>+#L&v%g93**&+gP)4cN=YQeWJrw9z9UzH;c;&@Y|fD; +zi4!u7Y-@=@thtrRlgMVgcq(MIQlp&N+kObS8l8VzROSWHd2>tLpF-!NQW^n!`#PH= +zP?fN?L3a<+94SgVL?s5~lWn$^uTS(H{jBc;D5fw45K>?_`bL1le53CV;|kk}ah10;>oVq>pzaAX>!mKc>sN<>u4z$H`0;POBNI%ack9Bhfj +z?BQIXL=!FrHNjjr{tb!J^+59rOa@S_9>iGw$_^QoDqan7sPja?(Wf9fStBorSa+#X +z#?mJHR=ZNJ%wXwlc7y;_;42Jj&lRs%93A8=-RZ$&Z#oRWCS>tVU-#=@rBLih9*KDwr+OK3bx_sD8^xm*N|Ry2=@AEJqk~qq@b20j{v(AT%Nf +z;G8M!z`XyRNhq;&(z75uxVNQ!G&qwROwEQgF@3AJ_&DEgcnHUOuR)i-QAO`aiY>a!EZ3A43eg6um +zP3wSuKmT=!ivp$D+yfmK$nDCl0saE{w&-HW0bt>A0dTV8L74sr^J6jY`Foroo~-XE +zfZl+M*wAOe(VBB`c5k!d>?-dziA;CD6}me)BPw`YxY@3_@F3`pjny0%>K%-q;P}_W +z{#=%ePMF+0ZX>`bjwV(v#TL{vI#BX^Mp!r00g0mF_0m2xr +zg$tgOh*QHy2NW1gvj^%#$)hE&czpXf7#1#&oPZc8=)kL#eSYed7T*@!-DuIQxTt~{ +zVep&ZTEGRav6#-x{~G}#86Wz(Cg3}UW=TKF4_>L>mnm}_Wqr5^xX)`2!8UOvFm9Vb +zs1Y?o*0-Y3J~Tp6HHrkt-QjFMt;14F0I$+!%uT9{yN&t5j=|#Cdohm>5F~Q!W_(x0 +z@5x5pNvJ!E(Z#j1VB3-6&*SqcS!|7dtkK#e0{3~%&Feo(F+oCO9~3cu-+c(6G9dW1;g2FGQLBJSwpi2aBrzMF%<+K>%pai +zVRkKCu*PW}i$x!lVsGkje9yv%tcUO+>sU!-Rd!@>F9BVJnW+~n(oCRhbo4^dchp-$ +zmfR3XY%b3lN9|t89`a +zB!I_^?tIscV4_>V03QILnEwLdfS5e^M3SZz`a3&#KI9|AZ}>l0>=NVC1OZIQ0_`%& +z#&Di8WsFXujM+|^dv)U5oF?fX?EpVTm_N@c&#%3plM^fmCiXu7!j2x!=R08l!p3gy&&-S*H7y(Xz{d;-u-rE6&`S&FRMR{ +zKH>kafDazNr&l>74C9TD3 +zHgAd)PeByYl&2z|<(mK#&*@_0`wa?V(SOXt&c0T85>IqI9^jXxlAX!6QvAaKCL3XI +z>@Muh1<%cfEiY~k$f;hB$kvQcz{LHYE9f}~cvo-WouTKC>(NOI@yF%EIy=AkG{R?l +zc$kk{aMeKe9K@bGz0s1W!)GgvI>(aGj>GxG-l^ey<<^Kv#(Bc|nViN!l=0nCr1RNX +z5|nKAJ<Il +zWcKsiNszAnWB32pBWG^zeee6e_xnE2^FGh>KJW8>VssC=t>}--zgzgdvsaRBtsB|a +zYLH@f6X_x%CkCtIb1a(5}ww|XUz?Fz~n1OXg6-X@3#~9 +zA4rf~vbW!ZV#VGb#NM|2aFb(iC$P6EVzag6Gxj#CqY@jmrinzc;y2>@jx5*Mxi-tX +zMx6YyyqZCj4mvh5$3Xoxn;0?T_&)eaU=zPrn(Hk;CT2BNz&}~|=UsHo=08g;e`9zF +zvS9uv|Hm}{7cAeXjcI{>9;G~wY$UX7pBEhP?#G4V9+|gg`(2Cgc5fXLj|8jnd_Tn=%k%1=TqmOMJL$Oz}z=JaFoOmc*6Oq-eWTOz$rO&{v53S6|L3Xzk( +zoVW7aX3P!O+B!DR#InR5@@#$w&oUku0YkBObGodxe^+{@-)t>p5O~LqB(>HEjUI8wH4~{Ew+`st_QK!Bceo95Z@4Y;<=G3r5`Px3 +zv`Fm->2r^!iMQQEo#8Y*)mxr?)R(=YS$vJ+8?qS-<5kv#!!|h(4(Q28({3JB@MwCk +zJFDsFI*+F9{ZihXdp;hvU)5cu^^p@mxg&!|-^D=57<_PIaEo2YwzkrCll@edTNS+k +zn?AQGy_dZfy7^xoWcqTc%S` +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 +zWnX1XU|WXlpSg#cik98Gum*4+> +zi`Fy38;frVl81u>v&?X7p@KD688=y1MK#E*ur|2IrM&|$UVX0DPp`C_g` +zo-o-XFUi?zJ2vKgDj@{@@2TS+!G+;7&)D~w_Px4`h8>>2qS-op=6IOvK3Tu;uRG%O +zGtES>)|1$HWDqN$wN#)4crOSI7HH4K8W5PJ?i04@* +z!j)5ws@gbZBn^|c<iq3t+$b7m46N5{PcaX|3^KTo7w-hUgx6dl@moQZGZ{q2 +zJS^_^F|qRP5X5pse3WKeT39Fy@|P +zr;V5TtBA!Y%%GbcNX1xJAX@jNZGA(so!5V`<6Q&l;DoV^26@F3SzYq +zcsVi|;)d{~pQ{Xl97B1+o^dCQS-1?!6 +zSula`@hL8$`%)}5@@JSKczM*`7C$I4{KF-C`+eEIu>g@Y-NxX?6(>0rw#hybDK@gl +z<70M!#ED7$v_Qk0wq*!8$=hQ(&zM%#AmQu&lVOo^5o&L=D$VmPHK@DU55He)rI{!_#io +zP;bvbj_98@p-47NZLAOhCkYA2NrVm|GRl4*a&b-ILg>awIBS+JWWAI71_-s!*zCK7 +z?j(l49F7&bx392oGG4me*4IeNv-F93GxuW5H)71c*`kf<^?tiyIsb=&;hXw#FGULzXZB;|P3*&% +z9ut_bt~0vz3}$Zx8)u+%?Di9!BN8Q4Zmc82#L0vYZ3cpT7eHV~HWM}_xnb*l>2zQ- +zNnhxbhhrv!+^ah1J$N;Fr*w&{>buxKM7pwfoOt4EKaD4ra_gcm%@p6kE&MTYcf`qG +zj3}w+JDd2t@Z37BHPkULIX193L(}7FdPaf0ka5=YhzF`h9M8Z^U+6t}56|_3gi+2S +zjMDdxcXyxUNDE_2Z|@H0vu`(5P+x3rotyd=P~Xx^^)cn{%Clz3yy?VyiA{bF{UPPr +zL9uZ8gyG%fVB!_Xn-+DW@EaO?% +zW8S!pX6ETs>8V5yq)3=$J;SW-2?>o;#|6Y0x2v+EC~;lBExddiq&h4His(j|V9@-h +zWm9y}410naub~#$<~vzUbS#%~l{K!l)2>Mlv^H)V-o`8FvmW%>NG*|bn}jzYed%Je +zw8*O(T+@SDc&lzceyM#kJh{uqzCVgOa@35qrYPcLDz~>4b7!gcMyj<+{rGLLz~pYP +z$gi*%j3x%-Q1O^2v?AZ!d`LTK{mk(d)=J*z7x_q;D`mdb&E`XhxSzG+8z+vyFH-){C6n +zC|2W&y!qC#T277fagJCuSm=)OA+Vy{ijE+<4K)#CVx4VE+3gKAsXDVKkYYU)ZwOyq +zL68=|Quo1Y18(YY6d2_7Kxr*xzK$Ddz(#?wQDE*vN5L`_db=0;Xh+oHSCTo9KCn2A +zG>Hui+wld}aSzMYw#=VnSvxGn#|vIld?~bnK&rlmF)a#~@;%mvF7r8e^6c2eS?g1c +z+GV2hdUS;FCAmBQv->;1@oN%RYbQ(NA~YpB-ne?Jk*>9kPqyVTOS>^kEzD9EW@#~5 +zGE2RO_zjYjI1#)fk<=nzW7|=USjSW+HvbJe1_U-tyO)IWK680NjNr@b$_HY_m~VTV +z2V=Kn26VZ~?>`1U;gzsfyBMmaZx~phDHQG84U8YxG36ZRMr{G0^SYT27@B +z1LtC*T!}zWL6o|g2=L8VJ7HUNO`~mGWg8z?FP%??i?Cw_?V?|o+J*gWhrX$wF1Tzz +zJVU=FUbl+Q3hjx$&Ub12{K>J47~E02h^`3@drEEQY`Xs9yMfdsy6(tCqw*AIh}pij +zP2*0P*H2g}c;f}L+-<+Jfiht2mib%G$Dw(G8{{NG{0ZV78D>ZlqjsBM4uv;yKMS4C +zFY)>9KEyz^gnpYGR>&3=8J8qfJCF9k#t%R37FjSLIss}K3GJ2y&>JuR&xt!8y&(%{ +z{_$hAZ?bXp>&qT)sUW$T8QxkJGRmqr#@IOn6_q8&(WuU~&W25&E-``n+A>U4*)t@Y +z+(898Z4#YE=T>5L-{Z{7&Qg_ledraQamT-$2u34!5ohr2q+9kuc6dk5TV*rkTVn#> +zTbFTnRB^V0y}R^~^n!t>IS{jhhuQCC+(h5c$k;!S^IN&(;kS(nHxECT-I3u9lj_z< +zd<@m`aHT2|0}99WnKVm)yoXvPaY=MW)HEYHq!2h$BX9ynf?-0|znJF4Xn^ +z_Rx^TV{4qX4o=HPaUwRj33XymI3LU=_F!ZZX(Yrevs7un$;VDCGRuB8h7|2;#?Os^ +z)qlygV}Jb$y_k!Vj(}eSV`_B2cr%6;#UxLclb=rZ++%t-8{+w&3%>;CI;{4x4{7G&Rn7dv`97wONA3wNZHx>)6mS +zb?H6xr!Jj&_g9zBo4WY!rLk!@Fa2U;eCf0ea~fxqm2K!4EvtSN0tx|zfI>hapb$_9 +zCy)5Kssx1QY@a0fm4hapb$_9Cy)5Kssx1QY@a0fm4hapb$_9Cy)5Kssx1QY@a0fm4hapb$_9Cy)5KsvGH3a?#8=B1Q + +literal 0 +HcmV?d00001 + +diff --git a/firmware/lantiq/vr9_phy22f_a1x.bin b/firmware/lantiq/vr9_phy22f_a1x.bin +new file mode 100644 +index 0000000000000000000000000000000000000000..02b88a078b85fbf73a4e0540ada7d49ea6287cd3 +GIT binary patch +literal 65536 +zcmeIb4R}*kx-Xucq}fSJ`jHl7oEc3++VV|HK^^=ch43YXwy>q3ASrD@ECWGz;#Nx& +zTkE%U3uRzfgm#$YoO|xvnE_hPxju7dlmpI{GuOE;C5fW6*^VZnw8{OwJ4q?Z +zIP-u0=l}fgbDtn#uf4wZ+V5KH{k^|;t-bUw)o7xKd}vwL8DH45|PNEJC2_gk#aCs +zhhjpqm>P*WYjjLLzx6XG+lDtNf*DsUTi^=O($?Mn2>!sv%FnJwHUSqNi&pV(z8-C_( +z<<+;wA5f;^M)PZ1Y=Y*uS&t&#rW}cTF8)3+Eo@X`5n7lPk(ayE6>NLKfCBr{@L?iBetH?x$#_kIL2lS*cBl>V;~;S3&!FRt$k=PH<*_j +z(adOjruErymomVP+myEB%JEZ}pY@tkp0?xM{{8i>$B%1{bH}y#=`mLg$206dE#%1sa6Wmg`HAPVeU^qj)!{i(EWHQ!W^=-a(Ji?56!?sc|4S9d3j;a!pKR75Y`F# +z`HE*zL}NeTm=S1=L|S_cqPb&Kd{L1#!@Vfz|7TAA6({Rh*C5M^WIl6pU|~0kastYAbtyyd +z3#_Z?K2gzlFD`PTthqgERojvuV*^8+faaX+iY?)AVN20C0d?`O#+0RDA%tq8sHQzX +z-zbD)vxVr=A>lr8&j%qqe9s437d_`BGB9h7@G2to^ng7dM2)xxagA8)=20^Pt;iE; +z!Q5-nx-^_Hz{M2253+s`T^bI*^Vyzjp!dB +zdM4|NQGaY-gv#@G#O*N6y*LutG|G=e#K}mcTpuV^ +zUxpX$*jU0v-;SU}K>V4FEu=x>df?Pz`=j82Jj +zvf^Y`W=_5@L^H~-3<<~H`K&xE!>HBbCEocgGb32&3gZ$ +zL!sdjY~)yl$bzSotl+5;lLpg)YsFmIk4b|++$l_&>6kRvj(9fp|8ZMfhiOY#+lFa- +z3e&b8)3y!McK?WJd;E3}e_XTJV-*{D8Ly#N^z0wmKXEd2XWX&(4C_?_j>hG|^Fh74 +z##i0A%!mFCuI%924X%~T+pCw&<>eX-WyW$?sT|1>+FKX5?+movUVW%CqO;Vz2DN(* +z)v`GIFC4>-1C^dfrFS*139d$^8sD9b94cKGT-UK0mEN_c{Z8iOWp#d_(sopOJ1UK! +z(%0@hRC!Kkxr;UPvY5$A(UTme*^GaI>~PC)D>|8I$SPC({HgP&Gaxt}v<(MsLwW@2 +zh4e@plnt~@+bJ1>gAT-KVO@8#>4vo!jB_8ds2*4mZ^~q`F@99tRx120?DIEIbLlUn}i08c8AjY(QqhauD +zeqVlgNko_}D_89`vfEGr=B}}v5Zf}jS +zr4iOv6=7ZM7$?h$);@#%mdl4KITN}#@j8d5Q;g@IM=-y7@0l^;?<%xL?WWcM_OwN~8Bm|BJAS>u3j5b +zN%8!?y=Ow#aALK?j|$HwD@2}(nx#FXuXTk7k>&guG+LbG*gNu>OM^e~$5G*EWJb1{ +zS2@uP8E6Ml$tRh*Ia-&#fs_AI_}>NAa}>f-5MVbLQx^!^EPR#Yy=%JDkWx+Ax7;spSe7~=(_A@R;bYX{Gc-v2{CvIUSW +z9+}o7BAbe5$abM;=<$7-8OdZBZ +zUpT8LD>#%7mA?di5e~-<0;cT}=#yU;F(r&Ji258A +zi{yBq%?S0_rA#)!*m5`-+ms0WaD+~3Z_R3lC6SrJga8__Rq0Z`)t>9np4rr%#c0oJ +zw|$hWz;1N)+x4!Pf3bUvJu7I^>s)=Nc842}g>H^9)Dcg$iENf+GbW=M3bltG&Dt{C +zllgu~?~azj{+R;=%g@)d((B{X{GzpZjP;t+8Q1ihHQJ1v?!%hqft=Pzs~zUhg`g5{ +zb7wc^oE~#pdy?4>Q#noZ5O-V0t=k>X&VJ^$_PpM`Pt0wy$%s6I$1WQ5Bwr4Hdz@YJaCc +zxa5TP)!E$+gWp(@|Iz-pE}qSQYXNj@r@!G$(CBzFa-)5LXz(wHEGQ~Hx}ax4(V)ot +zD;QkX)Df(ROmZQrXLfhyHcfN? +zcAcd=UTPOBGAjP{;?y~_yZ<$;X?FDJEScLnW^dmfeB19BZE;l;%@Hf&p^Ft^KY#S- +z+n>H2ye)$VI^Skn@$#*OK5NU%gF}JW_*W~x_mN3sG`SYI$Bqfv`o?g%`)y~e_VU1u +zLg?Zd&eeCAv)C+0ISXg`CvN(xh1Dz8&Y!Ny{L!+?Dxun0WSzD15bmnhZ;J%7Y`r;= +zY}=ULYa-Q=K;+IyO+<*?6}dZdM`T50ZNwF+mk8uu +z@RcOentk>XH7(%t+0{U^l1f9Mn4+{F`kpq+deS|mxxMk+(;4T&Ieh)jF;=-012)jd +z=k5s%y@))6*8D!^Yt}PsOMIJIJIdEPo~z`Zocm1VsYt-~%;{%#d$#vlpPbu!I{d5$ +zuf07cumzkDRu=NGuW-Z50R``SyWFoVx}a3@%3=r)8DaVWC4?}tF$5PPTa~OV<@=Q- +zyi(1}RlIyb4%jgJySXm>8@)zSq$mK(7|qAeC^AkCbSbAkgg)<5N=3+6EENNs4uBX^ +zkrLZb_3`H}7IkJ(Tiy&VLt;-`ZtZC7g +z_hm?DA&}x={HxZ8(T-OM07BcCz*+$>gG(iXo4d`|c@9)zhX|SziT5#x#)ok#KHE46?l;F_R;G~Fua%2h0fB&21#YvH!lTo>S +zXnZg@KBCpk7%XXhwj?qs*!Ik15T%B2j#8m@S#$DAb8bq$1NyM+QwY1P^?iV6>c_cxGZ4sMNc;|rlMPyET_2IDb-6+ZJmIkOP0@Hnb}Zu>pIlu +zoU&;nJ<$M#Y=J^v;aQ+PQR}g~Rt9Z|$|W7`v99o}@-){r-Vx+L4QgF08Uhrquv*+LHrjf1d$J0dp=>M`F;L)K##CM9Ks+A3^dqc +zXyApy-~@MJB-eRYqg~XBdhqx=8z+bv6?*sD#=9G}2x(m&0ElcmxSWcdp>gWS=qx}3 +zeXFyz9zzYc92xGDY%4OkH$pJ$WYA3fF-^Dxlcx+-bWQ?*l!h--v64K)I4)zY)o`>*WJ$SE}5N@!x +zG(@s3g+7BESWQZonG7=5aJ}$Qoz}&&MuaozZD4d64RZ8x>24!PQ)y^y6m*=?JCyi2 +z=gH6$64^(197k`(IR}L&r15@C!4NCe35YT)$V?ddgmj%>Ks=NOPk9*<{K}VTQ4ik$ +zLz#7OGWF?moO}%~%phlj3$ZdicMLe|Wci`TC8!<9*hGeQnRJ6+$wy=<6p%r-vkyy| +z5!Gfn-xk>S{EC&K{iTQySk{M1uJdhlgC9v{-uom4;@~i*#z*~9fZ8I#VX(I^{NhHT3MpZ5`Ib?9k{=UrdhVdEA^*Sif8?|mBLcogCQuL>13gp4thdue8d +z_i5>AN#{p?!%XC#?ar{rLyt;mkFiU6J2Ny^3Fr4&1s#nI_!01$%1OV#WRNF;nGJsU +zIFLOX+2hLFGAF@c=Ndv!Bg%!S(A&8v00m2QxCOdZs`6QzheA(Rnk^a>mn9fK@EARc +z;aA?!9luz0Oc-eBX^5at!#<2tEwgHbFz18%;h=uXk=or5sq(!MdJ1jiIryxMcyA@! +za+eR{T^)T?iarIz$E$=ExC%<`PFI0^tDEmbha1r08D1(^=EuA-qWXMheM<5hy?Q_I +zy$(a~T0ECYNPzY76psWQ5+uV@5~_PDW-kIb=DMDcZgh_gWP1J1Q1l5Y(?2%Ad3{1C +zYQ?>Ltv?!L+quUj))h4x4$^oYi-uPr1NtqBW;arwSz9X2*_N$|Hj8NR3tpqwIMIvt +zzbWC?Yt(wt)J<~K$i-2~hQr`Q&)#!HdAseIZV0sFo}LZI+qSnd5JL>*xPUQyG905< +z$``^z+|vHIk9>U)SverCYD2fu)`~*V+uH%*Mu;&EF$$QM4Vagi)E7*F#>X59z|c~F +z!mxU}Y&PZjq{X87Tl(F(i1hos%0?)9=kbnD0-;=`B42u8=>ekq3Bzn=q*t6S}1y}%j2@WS?9@xherd! +zdN5Dwp!T_XPg`B%+Th(#^d^_9vHfmWd%b6E-D`Ir;^5<1Ypah0%&z+2D3l4JOyqr` +zu(U8(_%;-MU%0RboGpJk6#Yqrp>_p}L7omh!VJgaJhV7(n^5Or%P{f~2^D?@)$mYt +z1)r9Yo?PvbY+Keqh2QUh;v(G$6>dGr92k07fchVW;CKj=a%Ww2)LBq^MHB}Goxjx} +z4?%@P7x6Ao0uNNU0u_#Vh7{Gg=m6i3b5P-1)v~;63$l?9Ko1q}pgDx}i%{We0aUdW +zD%^#pjX~2swe48rNww2sYgt3`f(ma#V;s<_IiG^Q&^vV4US5Ouvg5rT;UO#HU(n|k +z0Yah1qnM}1)>a4o-ehfQf_@*L((i7zXoOB*JR*F1Qok>Re((C0e&3qV?*}gHcf0}V +z_X8vJJEDg5`+<@A9SOtwy{U(9BK^KMq2FJ=QonD#tlzsv>i4Zl{oXY~zb{0!m-Rc^ +zUy6ut==ZJO`-k=WwCEn_cjQUx_dN;yexuK@XIQ^)y{zAVi4}#{Z8dn{l1Xq2(um>*6$d4BlY{i%liEw>Gy*v +z{eEDCem`(k{r(cymD2C1^a}lcP}+1^ze|ZW%OmvrKIzTQlzvCvgnnOW*sSXJEtmEC +zmY&Ti{f?phH}(5wRlje!tluH*q<(LLe!rceFRsw<`-b&9OX>HGo1wrnOGQmw!yQKC +zPr3d0_l`;dYQ0*#zY&Q}v60Mix5rs+GG%UlU`r(wI3ng$%{7^z!z<_E2I^Z{g7{l2 +zE1jgjwU+5VjX^$_kA+q0lU_UQ#oXSHwHBS-8VMLc*JZ6m=|Ub(@xjmPH^`?EkUz@% +z*vZ!MPNrZ&D{vbsSanfB>XNxd`SKTK(EL7gi&_3fnKDT +zw+H;VDKW?s$WF{f<|oT)d|_nvA@k`Re@$84<+4xWCj1YajAUg$R1ktn7p|yIL3M5= +ze2{aaAk_`S=#?*!16lO?FId+r2q*{?Ln+5{1O`{z8By}Se$t{gfi|fD8Q6ljV +zjZeph0ydk;D))zT26u99!=ctlLAG+C?WvFReq)Aeu|Z{PY!GmU*DHzF8)b+>hhHHG +zx=k!p?{nZ&8dp$)SYA}b@oJo2RthF$h!c$y5KTU*Yec)TNDYsMe`=C(iW+u^IjWVLqPih@&k@;@? +z)}RiTz~XQd;#oQ|rVR14??4m71#tz)GlX9`^3BQ+Z#Le7n_KY{l`Ih#6Uy8~&&?2T +zG~S@b>qL~}puIg{U8Qg7B$oeHejg`%b(VBBUIe+kc)w`|*u=*ZcE|%LQZ+bQe3{>m +z5cPJcc#!WBH+ldv?mo4#adKZ5P_FzSmB)Cx>NYf?_(8y_sW_ +zhg`Y_*4fvesnJ)*TL%2*<|RHW;fOd!l@WFMSp2TV?*}N)DO!XeDMgR|)C>i%KjSxZ +zka;*0k~sX30d2dS*@*|E?pK`v)VHcL^sH- +z^1yFSSN7$Vq*`wRXwtn0v-EW?067RJ1Y +zsk$hL+f~`n81D!uPz~s8J-TzJ+M|%I8o3iX`avE=b)e3=hmaPBLCP??$Uo+S!vU}K +zgE?l)&1xJ7B*_5zRDUnp2X2kU$b^_mASRfkU0}lb`320%p=wzMe;1g}0C`=a_fG1! +z`GAgq2LMOD2e_cJXilX5B=D^4{}ViO803LrJS!wTJFVhbA>rB2IT^3i1t@bIM}ROb +zkoWOvKpDn6EPGO9;KGa?+E|=#QQjBI96mFktVixCb4?N#ngL~sNmlLwlx+c&A*|kK +z0?Nc3+yKfHK$!?Ali?c3mnX5(KDNOCD9Z^cM}%f;<1+=7}P;s9m6fHFUz +zY=eq>HNMJKw0j33_fB)fU;6R89a&oLx3_9pezh1 +z;{au?fHKTS+sA-1A7s1QyF;=qHOU#|yO)(}6DQV~sv#&@$pJPkGp#@vP*zZ@#_17S +zX%bhNWZY&z{jC(2tKn;u(V`&UsfP86LtHK1p@wzHvs6Td$ZrOetw5FlpiBXj`7{r| +zSpz70NP4p-4(Qqk=rRJzL_nE{`$6360cHJwvR!~O!oD{FWeyqn1-9ITI%Ak)TQEn| +z5pX{uXwMlgdO+EIfHEhb3>dc61SlJ1TTGDNB>_+t29)Ij%F2~B)VBC1=#smYPuH4o +z8=y50S*b8fqQmS0U;)Z-e+X+EbIj`yo~PbhA+JXB9&x^TJ??K;O+ESb(`+j4NT-qb8X!s~xwAetFFF +zp7^-wY1|`PL>(A9+i~|Se$Swe4ilSdk^x!Re>uf88L^nNiV28@il@j5#jHcVjVAG4 +zwJ$Od(F7>l1SkV`Zh(C7-1UhxJfe{rA}hb;W6W&;lyw2h-U5{61Ijo+nV=lQ`)z?f +zI(b}xe#>Xa@q*Z|BYvXlx6uj=0}(4rh%H4`XO(xc7vw+GdmY;UCs{d*wYc|q<$of_ +zpON;zppycMPFCInuH9z36%AA^Q%q@-JQM(w-3BOg0m?c7Wg0+P3!todo=L8hmAPo^ +z0+d*U-(nM1p-g26O~{sD=Kv^!JY#pEsjD2HM$z_4!YV+uC;aT@RHPf +zENfr88}4vjeb819_Xe +z)=4K*I-txsptX!jjkO;5ngC^mXpprI4MO%q^G|5qfHIXA$KV$MlnwVKpbUso9spE0 +z0Ar4l>HgqDYIU}8=ce1=3?bl^{w<(Kv&WGlkXf*<^q)Y5I2W>p(F!5f`h%#DL;tUY>ck4j>g->` +zl<9>jH=uLhk?Rr~?`YgF7Gv!KR%T;u$BON=SR$xCv=xo*SS(qE +z#gf;sSOOo~9TFBxI3e`#-aubm(`gjP4rU0k{Ju~;KV*!K-85i}8pAyLum&A(i3eh! +zep-1<*|?MJDRV;OKW&ar%Mt8vmj`BqhhV=Bf(o##3-JTiha_d5v8q2RN&#B3je3oT +zycp9V55{z@r*K7_*}hisE_X+MDJwDLmg?}Yd~H3}DTy0lxS%O-X)*QR>aB*v&2lbb +zgQ71qGmRPjx)*kY6`5%4FzO)JJ}-&ru20M}3c~u@X&IEBlAs +z{mN~$zQ&4J(Ks^3E(r`K9WZYk=3TMRD@m*EUgH-Gb{EDj#^g@o0357{r^#+2{5qhW17n{9`znt5{!uwibrtK9h@UU{B-SKw@$Ep#%efXQiUonD6yw +z_@6lGHJUsyLJjibT2!gYfIrZW20^xbCS)r~W6IDBuXM&{*K~#+#BXfKXmoIRm?YW8 +zP})9Tw!87L(8H1w(?J+w;|wI<0OyA0%Y7ttwWWe~MRAYl_BZv%d^vKn)_*n9}&4Wh?=9_}ZbLE+4x +za0)0K;;g{9A~Wb3s-V6lT{$E;Swxj1)WTN@xt>rt0lJBZ!jSU1c(MTOtykp_nIb3t +zi}4PGQ9NP7LC+_-TPfOP7OJ(I6*hp1IrPz6#Rf8{Me%*p@Sw`~F +zA%OYCP*5IhN|zO$2(LxArSRS0Uk~| +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 +zqQ +z1~}RqLO#hge=I)suFyuqcDet$eB;)X2`MeK|r?=&^-X?=4p?+QNGUC +zjLA&@d-p@UMtQOf8U~DbGqXT%fVvhtc +zx9maO$I*Wb>0~b~P1y_ms=Z)1O!h+1o>7_E*=MizzvW2Qr$hdws82}Mhgs#c2knu> +zHw;saElQPgd@1Y-yhMzZ8Sx6T;c)*D9gmQKwgSNHZ#j&e%TN)ft>p|b(&V_M^Df+< +z!M$wjvz=iFc!OhK=X%5$<YciXOwZW}PM0+R?UsS8MRhuD0MrfYyZ8NLwV>XZA;0cTL%F5bC(PoGtRswbVbUx-TiIbcDF=})u?kDx<4=BN4$tR<1+7&l#8?$ +z_LNz+0}p74QQhN$&-|wiY3y$g>Su)6C|*8b#UVTME?~tvY9!aDS&r^jj*Cyyo{xk> +ziTqMlVA}mATyofb6z@iyA8}jo6%~{U{BrnSA?G^(=o)POk--nJ!knL94?~~6^P+uM +zh|bs}jjf?J`D*?4x@^myJyLZw-Fr>4Jp%)EuVk+#%x5Tw;zL-j!{%UD0BcKzy^>-C +zZwpyl3SlpP&xb6lk%wWqZua#M0DW`!Zr;jlcnTf$Z~`FR2CBzE_2}8Tpn42cuhfbL +zCaKmO7L?;Nl#-dg8EHfl2$;v-)E`433!eCck{BC%ad0< +z1fM#5nzpJHH#5H!nP%(G; +zhS&+!4z5v6TmyTaawsjqHR?=P=NfCM9mF;64z5MpRIY)ND%YqsC4A}$uJJs$#`DAP(k1tzpo=77lO}w +z&`;nR>%cYYiEAYKNaY&U*t$l2O$!2?AivE{B-hv>t;GEIjB|l!IP1VOZU@hpKayvd5 +zSO5I~#y8ffSexJ*74t4J6MAbL1 +zD8x6&A%B@~yz|%jM#|s*U2F8IJXiYUhxrDc1(!T|Cr9#)&8{C0^9?n81>Xqw_9ywq +zm*}_m!8bN28^I?4cnQ8ym*N{0gq8Ri>dm2Z&K5e`Pew*=qtsC;Af +z_8q=Ex32-;xGTvwUP$td7ye)K4Ug#?d;U^Uv!8f3yiElug5#Mkn_{Prf;2U+O +zk$l6H;2Yk*k8iwi1>eAI-*A~& +zo#}7z4Ug&De4~GuZ4F@0T|@b +z3{zqT^s@rH>{jyEl5dcFX7ELRi0{)};l`fP5{>|XFYA#poI~RdJtE;#X$RTB(_jN* +zUnllGT`%-(=q`pHzX$HhHnPEG0OLuPYd|m`wlYU&PA);=vw7RK_Vdn&NR4MxjwukzO&EY*c~Zs?YOsxz7gNp +ztsJ8;&XnBVjzwJT6J39od;M5!ER-DD&c1G~_Z}o~L~<*=BR(gmb0G(abKpHHd$Ra9 +zaCDfJ)<{=)Zf4aGY6B92Q{8;zOTo%DgscsraW45f+7>v&(2c#ysrqQm7@y8RitqWO +z=!dA*Vswo&$k#XYEZ$1rzBKe?(w^IJ3FJ?HPN}524xh_Q4LzH@C$&Pj8~%&;aZrE< +z2L;qZz!LAD0B$Gp@(uJs)4nXoJ&MYW_}IRu)*nTcjonqObolvkhj0vM5^U^_R;6UH +z-pIPfLI_fxdwm~-a2RO1Q8)E37bu2VnB0dK=Ro82LH`Zyvx!`0pIUES| +z++If~iZtRSwn+~#>-@2y2MqEIHz?CMT64jY42{W|y>L!RE4JprPufz1vl?)0D|-`1 +zs}4jTkfLKJ&hSZvewDL9Fp$Lv&NdU?BE=}xYz>i2B-IVhZ*+W(60A(h!#tl#)_8V +zy6}+(>VnxjsP5@QU8%jf_@)x8b}|E%Q$Xd)LFE{ht+21)l8{_?nuIxR6`$v2@=20> +zK}qxTPUr3rAy}{RhhFbPQsoz3BX<&=fu*AShMrNZ9OH-1Lcx|8KaCZ=?pR#w=X?#_ +z8XsgfzzJ0xId_xiP=smx^?bj&4%_=0(04)xvu62~LQ#1{)X|)8=vh>YsTcPB0DIiC +zEe~PuIL`c-;DZiXO&@_^OEb<4h}L3!J`{xSp=Bm~>tlUZ5%#XvIKzuq`gMv=Z$;zr +zIx9d+AQyG*>wRl5dY=^&>axGTa0QmJqFC0j)0qx9UrvZz6091~SCyyXgDDF-hL<1I +zlKTkUlC%@LjqfuiI +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 +TC6JatS^{Yaq$TiwrUd>s+;lNd + +literal 0 +HcmV?d00001 + +diff --git a/firmware/lantiq/vr9_phy22f_a2x.bin b/firmware/lantiq/vr9_phy22f_a2x.bin +new file mode 100644 +index 0000000000000000000000000000000000000000..1fed6ad65e2b6895f21e1ad81b1ef26b13edabff +GIT binary patch +literal 65536 +zcmcG%3w%>mx;LDiq}e-#&@?Sj#nCjjTw6#fAO(Z~;cB5RZVTRnv?z)YWbaNCXhLge +zbdJ&u)C)R}#}*wxpyJ?}Ii901v~$kPeCG%Q=X~G1?_5};Nxac!&1fo@w)y_gPErcu +z=y|{2hpl_>wbx$PXFa$7vsOcMO~%QSCmTZeUr>%+2)Xve4F<#CLiiv4eZD<;-5N?> +z|CoA?8TIGKGn3a_hbFI;!;;s1scSrSotKqNZ_c7}WG(XCaZ_j3akIjXHymdMv#~J? +zi42JhiH%5%NM>Uv5|P7n9EnCGY7x5`pUJ7u^hA7?H@Gb4waOZMt+L$LDr@@SvOL!+ +zYw@+ps=QWNs|J_lyH;5bU8^kr*UB<=#sZrSW=+`Q(hN@RshhQz1^k>iEEt@^4WlXT +z@M!Iq0)9sQ7p1+QPig;^uYD}w=lzS6);osME{@S``S|H|<{O;(n~NyTIZ-Rk$IqyR +zl&0uu3nyxK=HqA7dP@61Pg`H41@rOq!7fTWHCiijYP<5arv&_LdRh4An}we_wO5_m +z%V-X@W&baQe{c!1Q^U`HIjJod1=W_XI`cnvYL)q#(@FI%%>Ve#{L1|NPn{Zm);m#w +z0j2Kc&&HlP$@hIQb}yfZ2gAR*`o|gGAMzy9UHni&J^vr!Nim7{mv$$*drQxGUOe~8 +zE8V>r{J*pM`#uPN8b9CVaz5G35B1-0TH-42N=%B0p1#umwp6~@;rbi0^}YDH>ofit +zkH7jUpZAkd8%Pil~{rT=t3@&*@As<`m_>h72ZU +zvZx^`abgoMWumCf=XURj4S +zsq2@iYdm%BNnO86UC*bk7gE=Ysq3ZGwKsL`OI`al7wY3!*bT=UkDpRc9T!^H80emE +zjtj>PSoOx^8OP0T@wjEriGn@XzjIwAZ=~CFUB-3o4T_<@yCFWQ5#wOXPB3F=Quui0mU^ooH})iV^WV#W~Yv)DmA1kU4u#usMLr`&8XCZ +zN-;~vpNKP~9DAZYC#Tc1C2o@UdbrvUHx#s6ri7av@rFXDlCvd}*dk~de$5p6MK60- +z82Joqd*X0j9N%=i1vw}37bBCqpE#T`eA7*`#qa|$U@=>)+b#cQvF*z>i^Ig>Yej=$ +zJ1~E#}^FqDR-zmu5{Y);Um!Y2JXYt*Q^j#b8 +zlV#S{7F77zy7OBK@!HVT0gGMPS!?`C~Nt?elEvQSWZ*Va=qP> +z%=+iZIm`RxXh<^lP~9e9^gOy{q_X5r)@WkRtbKSkd!r)d!DSyVQbA56gnl|2Cvvnx^HIB#{-P$3qd(RAz=QY0@!$}sx#swCscq5drqHrAqP3A3CT +zddR{aO5V#zH1di|RE9+xjyJYq!5y>KwAC46GHr^y0-NODTYUoM=U70}r +z%!%+AZVPZ5j_`0}B-g(sKE5Caj}0<9E8_czBeeVKwDTxjLIXg?p^F7K$>$5k`z{nL +z^Ia;a^<7us_k{$bkLaqctfwdwkbc<)@BdIA}d%!+@`mq4;5$6 +z4ROhM>=$_$%#j#OU+fpz1zRGMvN7P1!qESPChKiPljAv&5ut|o6#d&aB!K;K6w5L& +z_Mu-0+Pi{gqWL_9`NT37v5YgYj7?a^0+w+rmN8Z_mUiQ*1}tNK{h;;{Sw>t5Qyc_9 +zV?y;ED&cLT2IaYc{(oAa`ku-+`tg(NyP@EP_JjFGUv4bV*Htjg*I$t78&P0sLfY`A +zh7oPOtT89jhyLD>YV*0qni%hDdfYG2d$6j;wtm)tX&T5g6l2o&Ww^Jy_setl#`^C8 +z|4Bq{3N`vV3i3P<>-ek*%W612X&Y#3cVpbeV{NPmG{Ze!FI#VEa&gbdriMJ^Ym9#{ +z)u&ivd{<7ZE1cV!E1&NyO+57LEG~D38bZXONRIO?8EV-#6yJ>g0(e6Hw8#g&h_BV*y%uKLil&W~y6br~a +zgvbBDVEr`5?d}n>+kR@#*!xjzA&@S)#}?AzkMUA=5m$ +zb6X?_1@@yr#pa7iD5MmHm{5qh=CjN%w;O$|T>vlncgUY30+!&vgZ`(jF|D}R#uCQ6 +z@ZkO2G*5z15)Zogxh(VTt>Zp&7nX)cl!g;*(qW}v0D+-9vqkw_0`>Q!ehc;ad!fGj +zk*yWk3fqp1st60+D+YQH|P6?fCt;pP2vVoWb +z^cm(!t_80%@=3%Q7vh39A4@&|6t^pwdT}rffZsQg+SaPJ9tXcLYE9Cqd7`!e{Ead9 +zO{8aMh)}|4oonjC`2JZ3GO@gGZPFGtVaaD>$v0{XnsQq~P7ocZP}F1;g&Silic&>w +zg@&T$p{N`b#ZZ(NMTzR|@vJ2WhH56q(*4@)O*7MLeMM7l97Sm;sue{s6m=X$sVM4{ +zwm@q_Q6kX-igKeUk$B2N@QMSOn(COdfA+$rnI^Ce4Q!(cMeRmW3sBU6eu|=6Nj+VG +zj-n{=qnsQRbrXuxP?QNpv7wrJd-?v`4xlIpin=|`+fHdKno!g!l9BW+KJBM5niNvZ +zqenUur!(@LHiI$m`ojlLZ0){2Bi>M8Fibhuz#(6*tGzxl^0d)k?>cxo{9e#0g?jr? +zHv^v${LLG8JofCSXZ%gsCmJ_4HTV;|!_KmNW8u-8vh%Y~n=_BbCOK?DYi96|S^lP? +ze9DuLJjh-|*)L%ZDt!N1P~&3-M&G%D&FZ-P&5@3wp_Wk1q +zzt8y~2YoyrsyXwx +zJ3mp`bok$5hOLja8y%V3nm6q@5IGroY+~*;On@>e!_LeXsTB2r^}6W6&&Tp +z$#*WUtiFHYZ?5S<0L +z6Wi=Ydr;dUj76y;%I9HjR!q2>@e78*=@xCaD1xYYA(!lsQ4V8rzUV4y-G2RE(DIgo{gg$w>sD+p5?damjR_WpuB0MMIHr~bq_=Vv5bx&uYr$_3M +z1g%HnrxfTF;WoGOpwR@-jE0;`1!=FwTZ;>`H54UukOOknhyAkObZ0?ArQoNuCcP?0-v=Qh-v#g2?XjP%F|^vJkWuxcd+0;Wt{ +zW^ly9=sDMSlU&i&zx)(z7r2)iS2aO0tZZuFPA@wKMIU3uodtcRtr+Y}r5Ej=fuNx> +z4Cn$h1_49;=O19gEP_ +zO>;whY(ad@)bK7SR0TDWY}gG&jYxor$8z9+NQ51`e0>F<`%MKy?v00Q9}nFZkKLW^ +z_GGo3VTG|f8seJ^;?5~ylKHy@W%WIg2jaTl?C!`tcc=VjtMAwy7L@zr@dxhM-Edcv +zFVc8tQ>5?92Uzcyt1$-m+#l(?H*#;h{J!`y-#w90p{u{Y7lp$esPx?xx$iE#oYH$| +zsph6tQ;kv4vBEQ{Y~^f_y?-JNsMhb;7AI`x6Z#&4?myfnKN<1fhQY7M%o+wiU6 +z8**vbQCiw5Btmj&5k5@O=o(%TlqZ1GQk@2ut2(*llK-dm93C2RlYei_{?P{_Gfl>D +zhHoOV^%?F;*M@bgm$@qqnd>$znPJFuD@%}AqO99MY08?pGcs%I)*;C-canF$eEX7l +znYS-@((~q8*_!FCROr6;cB?hhuj=>SO5AVw{zgilS8lc9l4-SCXWUv|G2f6`Q)tBt +zWnEwarAG{zGwSWWiH`3Doki*>`nP8$4M&|F0<+F4qmk>h-=!&;gwFsMUtXph5EBg*-aN&ft}-CJ5)tr!)v-{_lM +zd$;oHT(kd{+G({$+!=kSQY}z-jVmowjSlF&@F9l(akHR?HG0u0Wt|E1k`bn`H)KH{am{6>a21!If5`K_GEl8pXBvA~K5I_(RxFlipC1^Z0VmzGnqkWyMAxLvidb~E)?vR;a!TS)%B8P?W6<80^ +z3G#G~VNQK$Z^F=`2;Juk4}eofMQCF0C7GE<)7WCtFjip$#*6Vhg*$DUl4;ZMxDLPL +z`BufkM$#)j%;nJgd8>DJu2k@rNai(vb~1*&iCZDQ=SiBNaMOIe!$rYGz-2bI-=xUW9r`y} +z_@-LQR_>PW)t^lYdM9l`V`%!laNOcz!Y8kC&;iHfbDDh2xAJ41E=Y}i1-k2AED0Bu +z1R-u=6#^!;KC4{+Qm8^gxX4eX%ZHruxcHH~v{`(ufmAKzP_l8VJ3uLXBZ?)DG-daxO +z2NqZ%lazItNQZNcOb6l*-&38_)tTy_%4%2N2-;5@I0!UnE0uYJgYhWYS5A_*iM}k84kivJuV!=CP-h6Vx5-MqS?@%xv +z5CQ#VYW{Q-Gul~yz0dA&2Vv)oQfsHGB~XHq<$ocIf-K-6x0Th~d@~(0g3kQND0_Y! +zOv0TXX}@o-5o{qpY-aBE$)SYY8OxsxW(X;v>s_MdSVA>U2cNvVtdp&8h*iPi3kT<>I<1LM~|%8o9^!1|r|e7Y8P{Ym3*jo%MU26O1b`=jpy|+edh58kY|CO=7g$TtMSz~;@RLNm#{`8u#`SC +z)|j_hW>$^6iI;u{Uh;;{OZHg5GcU0wdC804%u8Od3d&2??Px@jmxNOH#7jcfhxByf +zB^#|n6u(vHC7Y}|FL?~_h?n3(yyVd&FKM(sseePfq{*7C{K)!@{!F~2(fTWN{@?IB +zh?jiI|BQsQ`27&SAAy%d2YAWH;3eHygQN@58oXaEpf$L@3?wd0$3z**)X}scr|B|! +zdTkRH^BY +zSJwXd)qa%4wU(70nv^XTxn=COh{F-|%*^f?zCNErs# +z7>XB4v>FZ??kL}*djU+2$-(STPJj4-s2$g3Iwp|Q1d@`KnnXj;R&T3sD`_in1{DeO +z$oPJZkHy*@k1(0#I-oX&xjWbx +ziw#PHr|Ts>ma)}Xq|5|7~aU9jqH!|*c-y5DA#^EEA&LkBSAcBc;tBZ +z2#@_#U?>RW@a30KrBII9{3ZLT{j`f)w8|hv)M)rP`^}yow)9WriaRl>+x(@;J{q(^ +z>V6P5d)Vbdh)!2uR`2fle#?PaJa?FIuJK0=+3(pnY{5@-`A+kflJENbmA+FundZ-p +z`GcQsM>XcJw0UxPYV#C9n?dt;l-BxWef~+^T;%K1Auc>WFv3Wwbz=zwf~d(gR=7X +z?Du)@64JBe+hJJ41g|B#dL1;WJBlCKfLqeD+>dNnSH5{;c`=?h0A +zu*$H+i(3QKEjQGy0M_mxU5jk7?qY}igm7bJMfU#Sn0o(&+SvHoZqxgU-Dk$b_}Vc_ +z(GIh3LZrCa4CB2B#(NQr_X#lG55RbLz<7r`W{N%)nA8EILQ-B0XUK+@j%Z$QRO-)J +z-<`;jVm*0KReS7?F~RWtn?`+TawJM_wrB@z+G3)0OpZ?T(7`A5846DJny19r5a{<# +z1aB=km+0#GVIbTiRY*PC0|)A$n@8&;n{r8nDal=T6{|Tp3AQU|jy%Sq*|BVxcD>#0 +z1V6B7PY}{K`U7LqHe=2Uzt`+19?Vh%8|)6!HO#NV`hnsBxty;oP=?vEa8JHsUW}WG +z_?-l;1on;7`>H%t&o@nRC`GmlV9OjzmiG%?UpM3SA}mO+#=nqjLwg0AW699UK!Q!i-YPUeganb +zI$UxK1UQdaAuGg?CMv*VVV-hl{msy6D}r@Rfl&o@(w7W&1jLos%l1(Cg@C2K(*T)9 +z<9}upbGJmF475b^43gDj@B`GaghmMJm?Blw_7}qle#7b$YS>Q5Gx$Ytxe*t(Jz!;Z +zQchji5_*c}2DTxB!;00kPXO)7fW8@Gn2f;Ul1%B#x+b6`buG~!B1_1bT#Iuaj{AbhG75PZoSt(YguJVV +zymPDzI>WGx$e_pUg8M+C@I3GTUv?^n^*s9zl2T;E8bJdTTXk)Fl}8fAz2c!+CnXIq +zhHk1dn5rDvwM&?c&oA=GgmafFPR`4@cVQ0@!2b@A>+HfB5lvkRh)Tc~PvcJKI8(3RF161zf^ppA#v; +zm7@i~S#ml)FkJv(2{)PyH(Dvv{Ws835et^nd0d9Rn8UW=BlzOdwmX>1D=_z|k0$Vk +zJKze?*h&d8qfB0fL|hmy4qWck_(=Fc?vnUu?{RiFd<)dEvWVWi +zh|IvUx7RO+uWRyv9^Gz2XW{N+GJK4soxK7>Fd$!kRwh5-IT}Rp4a9!vd_x~s=xJaP +zAfbL)>S+wTA!)leP1# +z90Tapm5|}%7PJbMV7#693B5!3MnA-#3()x +z?@8Bg=MN}58QLSjFDtXzKA)*ZYzUmWCC|_j_23uqtOR&A53<5l4_V=ulN=j#GdVWk +zmoyeG8Vi(jEFsF<1CojOS|IFt3i#W042S-j?G-f@gINs52p;=Be7WGUj|5*4wNXj> +z`>_bV+)rg2G48C5Gg5lPP@2@9VBml1pBW_oOGy5c-{dO!UvicFFGft+Ss%K`A`1rG0Jqv|63wukpE@1aK}X8j`5}3F{$zdtbZ +zyUPDAlK*SIMg9*REdTeVGGedU5?)h$p1$b)P;MJ{~J?&4wC=tN&Y*%C*|W)d@Na +zL6v?J@?S~Ie+;R}KE&u-jlX8&!fBhQre>%zkGIE0J^8XJ> +z`Hzb(|68w+|E&Soosj=!kpCVX6F_^?^8dk<{C^`U|9^Td`H$77%YVKBg;3-e~dlD;19@ujJ&c`A7}DYKMX(hLcSJhm;Kb`d_A7$>3Q79^8ozR3wW(f +z1HB~u)G24gWk0o9f_>D?O32Cq5BZMre~R_H%l}gAwdMb0q)*lTJ|zE}t*CsE{MW29 +z+iBJ1|1K-$p5*`YR`ASkm;cxDeSMSse-ZuuM*08U{9ZC!XGJ$i{%?f)OT#_M|4kgu +zFZL+4o#g+o((?b$AmyZNho_w6|6lN&MBgy|K +z;l9(@53IWU{~;1d{^LUO|EZ+>f7+_c{~y60n3DhG>l={&DC;WuKXZ`$KQezn{)euV +z|8N+mbOXuSov?g&bcrKPKfb;=uPu1Pwgm +z+vNWdhD_fg|I>NCN&X*!{Qm<(dj`w@wrk0MIIQa-D+bB`$5`kB9_5hyZ@p6f +zw_-Sw^1nMN|I5@dgc}UV|Bnace>5%skIeg;{0{}-ewzZnOf}qZNqpv8?0+}x|IAhL +z1Ax^HfL5#q0P!&6fw8Y%3Gogzeuf+7{W6&Mt0+Bl{-U{*chOuOXPJ*oPT(yo$QN*1 +zg>?olZu-VKV}5xWug@&Gt%AOpj0}VFl_EO|oN}99M@^xD9@ngXOhy@xkUt`G&4$P6 +zJGdIGg)~qo^r$i)YOl^90$-0+&v7x0qK?jC|6GfdVb_cnCi9% +zK7`J7pf1`Nh?a~He6US7$WYP|vYZ8YljK0c23#`Ru|23-DrXd{6N=!s7-oUr0=Mdf +z$gtwS=bz95u1RE5!adOe_k;xZL`R_M;-g3gwmqwGcM>m{p- +zRoBfRjc@iZEV-6{VOrY1fHgvXfeU22;}*L7MG;G-m-P0OjXxc7yy*0l&>Q)17(f~; +zKyhu~Wb;EFnfY|3h{Hd?eu^&_NE$+)S`iWeypt8mFqmD~vtk55?W|{W?T#rygH0Qy +z^DL)L3*ZKJ_Omv~S-F$J{i3^F-h|sF3vQQExLx|-cIk!Ng~9Ezw+mrsR=U8L1jjZ9Mupbfqf`_>}JBY +zePO{5wOypPK&;ytAb$e9{E$aZTO+t* +z-Ex04x-*aoTjNZHq}`l}P^9RH>pgH|!yYl%q5un$_f8w;<~$&<`aXPMcNn(fb}YVm +zfMWSoVDkutD1x8t&vwTz^)^Re3H%n>`n2#^&_+9CyU7v=^GjGZPm+yBd{_6i05k+1 +zI0@SpWca(rq;U@Nca>PQkwm`~Em65;(-7D}*)9p9A5<$;tfeNkDXJeb7aQ7A_>4Asa`^Q2TYn2#(G)nvhCQ9=F`;x7+LTGk^d +zlS&_=tdOeody|qmaBoB(%Or00NGab$2C~dUljhSmfcMciVE?x!+kx9zTTnJ!1hD`5 +z8C(PbifM+6;DQ9hmb4MP+Ik#b0+4?v`cGJ`?j_*Em7nr`x@-+&sXwP7005As_yWo! +ziHr-<%zKgn0Waa*%g}eigpKS$dvcjW!c)JTD`|b?u50P&$~2fO6ZPF8+t&XTbb`bT +z$_;uuDMaB${5tXL!namF1b0L6Ym+pap)agm3cWWEnFliAl@-6JALDU3vlo>jS>O(( +z04`Y#ZM50pj7a(*hrvF6IEbY)>dpy{3=ssV78I%Q_Z3g_M;nzXlIE=E9?S8~K}i#j +zYyIq&J~mxC)z#bWu$&T$uqbxJ*cR%35O952CoRT;+=WFGz=G6j>Kbjz80j`tj^)VM +zQu2eCyhdf5G#+W*k93}ti#W`Q%4)sOBKr9N`NE(IhX=RCg0`q*DOTugas4XDfKbqh +zCfg+~vyP(@6iNOXGm0EffQSYrD6?Yf_a&4$kxfeas05`%nv6vLp9)LWX$JK^5Jsh& +zP-#%~Y?+bm_yN{j<%nXn0J6>n^7FTJu;q|xBa6`pjN1rjeG$g(X?@(#ms~wArkLuu +z1R5@(di%JTr2Xb>Zt`ro+2Qy>P!z#8)8ji4ePYWct1sx%Fm!+d3g$~~6(9u=d(1kI?QiLrb+IhHU5>%jZR5q<`KQc0LRScV%H7cLmj +z44ZC8fw{n(l1U>2+ZsoFd?^5*zS7?Cewup_u?>A&ihGLfD^|x9Wd>abEZc1L_S3el +z*w)hw_5lmD^f-xj@6AuDD +zz=2SqX`>-zGi|MWybV0rZg(6Ef>)2~`B8v@9KwTWOd=WsIq36)X_3H^IHJrIns*20 +z9kg~c&5H`UFILAFArfmr!VmdK#9M(8K!TPSin#?5^NKGtBrtcm1QuptmJo&|OQyKs +z#bA$2{EJ(H+{LhQs1#r5v1sS!8XZ3mIs*WEEn3f0kOiarVchuP+zdi@P1;5;QGCex +zw9iz7aU*~t!!T|*GATdR*X|rXUfGxS{}C^uwoz=208M=ubN5S7GjR_%* +zFN2?XG5DkRi{u)lww04joJIX(-0!7LKNtcBhCsv+sL9yK$HxhXjihmzsE#Pc7z=jC +z$skw=d})F*N7*Mr#BsEr8#+ +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 +z{?-INC +zog%+S)b7T*q;Em)%D^}(LqGZm1L~dEbhD`4MD(~GuSdQRm^a)HO=DifBD@l?`1wv_ +z-4%3&hJtQ%x{6w!(`k28<~$Y6Nb)qyIoJc}V2MO^!s`UwGK0p+PQDo80Y)#EY5!~| +zWO!2c-YV%T1~dl-NlrGAPDAiP@5ACPCK~wN92MPGQO+EqV~`(q;bM^eUosy8bU(-D +zF+UEPL7L?Ru3Gnrb5P&(G1KiZ`0Vkm8N3E|m=pB4l-l%#A3kq1nPZxXgj{qK7tsNx +znd8Kh{6q_Tnmr|GLn{+4D7Fp7Q#DVSK?B|h-=4e3muoEWf(oV!pPD&IujyuID3s3X|xD|8EdJ^&t3Z3ZGgt +z2%mZo_|)MfK6N;aPaRI-Q}xy)K6NCGPpwJgQ-GCI_|%cB@ToQ5j8BcF{bJM060F|Z +zav7gG@O6CZKmaN}?f?7bZ*kKXeP73v;3~sucKirbk@>H~uYjW?db|Mikp>f+c_v{W +z8G%LEG9e~yW}F2D18*e2BH923`Jcd++Bwqs=(jwncGE)A8%m_h?-sb|$rt>&M8mmOq``+XZ?`#?zpoG+zq9BCsf-8Mbkf +zzuI-I25~ZGJ7|o2y1;A_Fe@2C|77f5vr>vx%aSp-lfl2HD&uhnu*i&996WQSIk|_5 +zf-P_wC$O5GR)oUf{vz(5rx+7G?v`v-RKrot9#n(s24PaE_!e>#^B1fdwsBE$0+{o5 +zY~7-J6VDvDH{$srEUS$f2xP$)E(&3}#{RAK)<=*|^>b|2x>h`i7hn;>Nd$zFaDSHc +zODpgO>L#zooUKgFS(Z|*tV*^EPTzab6cPU2dvouzYWL#-7mN**80AjuUHbiS_=|nk +z2lRUnXy$G!5gX;PDb-dg0PbIf`>Yz=zgzw)=>FWd;r>1McHr&5!u=bC`*$zgzYOl* +z=ivTT;r>m){hNUMHwO1_*s$g8zry|d#M^&`u68HI^7PBtHlxRKp?HcP_&(}`zOls$ +zeU-B(tQtbPWh@!~I5Lob7#9J{s1EL54)<>sTyR}ht-_c1jIW8+ShZSY(8Bz47hhvt +zjKLm+q3xx9pvWYKh5||dFk+C<6dgmug)p>x70f2?2}9e-Xb}NJBcJ}w#1k-4eoWLj +zY`$|{EK|RSI?6Fugwq8w>-K75-P>LS;$i)Zfa-k6$}{Vh`_Dw*4%`gj9IQ_B063Qc +zoFgWI_J4U~0OzTDUp;{H0D$wYebKiu*MG(Ebpv?DH(KsatCs2}LpS+P0p0;#yQ06_ +zzEO3_34z94O&{1aHoE{4@x1 +zp8;~84%&T919;cLwpk^BcL+67EusG^Bl_0Dhsqe{ko;&^0$c@nhpuC#xz<59u9gAb +ztwEdUod)nO&K|TT0N(Y(arP>}JHqjh{u6Aer1sc(b)+T&;s9-|w4q!A?=moO$@tNk +zz#v}^q8p>u0lXu-uo6Ze1Alh|pL+n4i3`JpFuVmxo?WYBc*w8E%#e*3uC21w1H5wp +zyxR-#t{&i>1K=I$TE8k&$JZ|ny0$LdW@`p`M`!Y#0i&G;V;hKbk#zmN+zLpx}lPUaP;B%C}C7U65`4Q~CgMOTjgV%aOfe_5# +z1i(856{-O5u8xZ%+k^gnoBc5epDOuo_*BW&_*98Ag-^jGxB{P|=da^aIK$xU_*BVN +z_*BUZ!lzKc06vBLuj5lCmDj+hN(SRo)P}F&Q%D@Zr&9N+rj~p=J~d+ipTd-+@TqT) +z3q6>^r%nWV4iqf`KDB+RTCR?{ZKJ5I)bXhe1NfBx3VbR?_|!XJ!>9ZfZN^}{CW%kI +zcNIQ`TCc*Vx^#T%y#RxblftLY4vziB`XhYmh>lPF;hXU(kYoy<`olNkQ|UY%2vQ{s +z>uP+8-lGbvIl`y>SKw1t;8Vu}Y#Y{_j!&V#U&p8J06rB1K6OVDpCX=3_|zTO!lzRD +z&Nc9<(NGy(tpO9&@hSZs;Zv(^Xd>ZL+oAR*@hLER(pO~GG8mugA$Jw&^mNvzf|{Vh5l>Y)7VT$vadq6Sr$fNtcdK9ZKO-hyK6gQ^!*H)N{W};ZweE!lzb)Ujm=H +zELR8NQ?FkOpMvU1_|$_};8VxHBR++W)YV{> +z>gR*7sW3jinlB`AsV@g`DQgm!s!!ok2hzCI0pLbOV)UX4qp1Mk +z6!u&iM1WHmmtKTCDJolQZ2~y865tf+7dWhiB>+xU1DrAfoN5L*6$3c68{pI#fKyoj +zr?6G`7>}*CJPU9t4sfaiun_F?SPub~jf|99sAoazHO^)A4v +zmjOa +za4OC=z_yOanU%34_PX~2#_92ugIbV39;1rxI;hEBNU2!9>m6oZCG=U_sL +zh-ZQKK!2vA%Ig4i5IY1>Yjl4aAQ1ih +zW-qI;Rj51!2*ihT>AnQEz@gGI1xhk*$76aos1t63pDn-q>ThLy`%eKLBn+cMwFug0 +zRhficp(-b;uM6mP3W^m2t;@N~?Y9vek<`;KV2@QNUr&4-Rsh9bK_pt{B)1Ae*9PH+ +z`By4*^m4W6HefNgT*hL~(iT~Y=f@h+&y+pO??bEa6*XV#Oj$hLk73?`sdfR69uIRSbEe;Du(&}78XZ2%wmKKUTNfGa{H4DNA^g^t0&6iQ-n`iu%m +zS{Sd>LPx&8g6$c=E?#79Ln|$QARsRa+WP@+u3l}f^W|1WyuRE;*?waI!=PbnvwR=Q +zrU*s!aZ&`%wz#FB0(c5g6t;)Hb>L2FsfY|N4@=*yxvDa#en~6(h{l@`ke6ZXJg?7N +z2F^!nwpJkygyvt=eqIi`#$>oSV4Dp}#qcc9r;wyiQyuz_@kQVBKXlW2J+u=zDToRD +z0c%HRYtj;x8zW2ABEZKkz{eegbpt;B>hjr&`W$}6t1)YLh}x>lbGQ=SdpKDKV$8EX +zbkeuT^%bRy+62PX9>#r!<{EM&&J^o59$p?aZ~ajF*-k90MI!2!mMv9_wVoruc6-v; +zZi`Ou=opoPJOb49qJes(51q>+u)Ka7;wT<;lEGZ-k8ms@O+QNq;IAoEV`E;@2}52< +z*>2V89OY@1%T)Rn>xu(fq;>T(((}~fC0I-COO2?IuyD(}flP!MMYVCSS(0^3V3vOk +za7r7#tla{bdfB0!Jx6FP(FHG>w*=or=sjEqIQxjLA>RRXP#x36y7MN_&dSuWKp4-G +zZXeFaGWt8`pW`hv=0`^gW;sy&>$<{){j2e2z*>&#C>%0Ly4iN{ZJo3=|V!Edo +z#nlUoy_w2-mE!+yn?D!(uDrz*n^;`2+&W*LyA0VD&qYG!=GqmMXEx#x4ek=>!eWDa +zak0U>V3NUGY*?_MaK`PG$XhcXc~OpDKG}22ZeO?xv4*$Ox5bsqt%R51HWML?SG-&I +zz+9ZeTD+dlY!MM4q5h*sou`U(5xLh&@q+kydXoD8RK;#>7jBKi&xD&3x5oP`5V&U1 +zhIrs3F_61IIHBHATpK%qZQXusdaO2S%OVU{-s!5I1{c +zb^j2Ug>3~wI~Ey26lc*M8C0E3@(st+-IUBLnlMZ;q|c)CS%-nr;ZHprPE-ikJvK|O +z5M)Zj5nbK4w$F+qz&*wg3_*J`Ve(K+LjO%NdvzdZQg4l~j#E_Tu^53v6o$@k= +zF_yX8ajxy?fjzWFiqksr7;}cRTkUqavlQ@)w-bSp5Q8}tPPsH6wkY|#1o*7 +zM7j>R@mRWkFX~72cGP81oHnv$^KkUdI5+m-Wh+*OYT(BTVeb>p>lyy7=k>I~Cwa~D +zdJg_h^lidGgU)_}6Ky+i&7|=Q0;mHWZ*K~WsjQQXbx2JFQ*lg=w$DA> +zgDn&DVBa1bx>=X7A&j$8y +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 +z$`-lq1;?3PKKFc10*gk78 +zeg?~4<5l^&KludKgd#>Xeq!;6D=Kzl4a8;a^kLz|M&aXynAS&mv5*iRmaug-SQ0TC +z>&h$?u@%1ab$tn)4hDW~QxF?zY>%1fz&@LkhwlJNk5F?@TOsf|1*M{Fn;LDC+CPP2 +z-F?($urWUa1bxc)XCvH0tx7jv7}vrpPsj^Cz#@Hwwu+}6@n!M9H0YCdI34jFvFGYl$&5dxv2uRyhsE4Y +zagjWVpW?;4=TosH +zG9p{giOyhkpe$Sys)8@k7<)aqX1A)-%k|PvVvVjSQr%t^N^6#vU#kv267PqrLe)~W +z+Enb1A)di!c&PGqePo)PsvO7n9A@(-yIJfC<{)kRq_#KS&Gm78N0nnrT71B!4=i>=^NY@-**ea1PE>h#s}y^Ub#%nGT19qTg-P@m-#aEr +z;0(-m-n-gr#=(+XDRvg$JBr~%9P0G&Cgh|{swUjQDdnB1N@sn_omu9R*!yYtbS&H~ +z9qtXJn+%DTtM2~rRHn($EL}YuNQX^cmcpN~2WT?eeTV-h`N7dId&@5CCe-DR(p8OyO(8@90(VR`Z=UT;XMjMKkbK +z+(B|$=9beZ-?=KM0$LY3WXfK=qS+wBgY0xK{Kw*G(xM5@MH4cbTxiOmiN&Y!^`bqY +zzFKQnY|0YEX-MmG`TBUd;~%6xxKgLQEdi%2S;E&P_wAxbNwe;oWIX*R2dRws@N^KQMyQsA(ryV`tNxr%s +zl!+yDX+J4D(cAn`{As>kfIU-6ouOOHf=2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CI}U +z1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CI}U +z1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CI}U +o1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp|33o%0>w{+uK)l5 + +literal 0 +HcmV?d00001 + +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0119-owrt-ATM-adds-lantiq-specific-hacks.patch b/target/linux/lantiq/patches-3.7/0119-owrt-ATM-adds-lantiq-specific-hacks.patch new file mode 100644 index 0000000000..6dbbb21ec1 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0119-owrt-ATM-adds-lantiq-specific-hacks.patch @@ -0,0 +1,508 @@ +From 8d2a7d1fb561c9cb098c2b13ded34fe0f49dcca5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 3 Aug 2012 10:27:25 +0200 +Subject: [PATCH 20/25] owrt atm + +--- + arch/mips/lantiq/irq.c | 2 ++ + arch/mips/mm/cache.c | 2 ++ + net/atm/common.c | 6 ++++++ + net/atm/proc.c | 2 +- + 4 files changed, 11 insertions(+), 1 deletions(-) + +Index: linux-3.7-rc6/arch/mips/lantiq/irq.c +=================================================================== +--- linux-3.7-rc6.orig/arch/mips/lantiq/irq.c 2012-12-06 21:02:42.000000000 +0100 ++++ linux-3.7-rc6/arch/mips/lantiq/irq.c 2012-12-06 21:03:13.850060387 +0100 +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -99,6 +100,7 @@ + ltq_icu_w32(im, ltq_icu_r32(im, ier) & ~BIT(offset), ier); + ltq_icu_w32(im, BIT(offset), isr); + } ++EXPORT_SYMBOL(ltq_mask_and_ack_irq); + + static void ltq_ack_irq(struct irq_data *d) + { +Index: linux-3.7-rc6/arch/mips/mm/cache.c +=================================================================== +--- linux-3.7-rc6.orig/arch/mips/mm/cache.c 2012-12-06 21:02:40.000000000 +0100 ++++ linux-3.7-rc6/arch/mips/mm/cache.c 2012-12-06 21:03:13.850060387 +0100 +@@ -58,6 +58,8 @@ + void (*_dma_cache_inv)(unsigned long start, unsigned long size); + + EXPORT_SYMBOL(_dma_cache_wback_inv); ++EXPORT_SYMBOL(_dma_cache_wback); ++EXPORT_SYMBOL(_dma_cache_inv); + + #endif /* CONFIG_DMA_NONCOHERENT */ + +Index: linux-3.7-rc6/net/atm/common.c +=================================================================== +--- linux-3.7-rc6.orig/net/atm/common.c 2012-11-17 02:42:40.000000000 +0100 ++++ linux-3.7-rc6/net/atm/common.c 2012-12-06 21:03:13.850060387 +0100 +@@ -62,11 +62,17 @@ + write_unlock_irq(&vcc_sklist_lock); + } + ++struct sk_buff* (*ifx_atm_alloc_tx)(struct atm_vcc *, unsigned int) = NULL; ++EXPORT_SYMBOL(ifx_atm_alloc_tx); ++ + static struct sk_buff *alloc_tx(struct atm_vcc *vcc, unsigned int size) + { + struct sk_buff *skb; + struct sock *sk = sk_atm(vcc); + ++ if (ifx_atm_alloc_tx != NULL) ++ return ifx_atm_alloc_tx(vcc, size); ++ + if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) { + pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n", + sk_wmem_alloc_get(sk), size, sk->sk_sndbuf); +Index: linux-3.7-rc6/net/atm/proc.c +=================================================================== +--- linux-3.7-rc6.orig/net/atm/proc.c 2012-11-17 02:42:40.000000000 +0100 ++++ linux-3.7-rc6/net/atm/proc.c 2012-12-06 21:03:13.850060387 +0100 +@@ -154,7 +154,7 @@ + static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) + { + static const char *const class_name[] = { +- "off", "UBR", "CBR", "VBR", "ABR"}; ++ "off","UBR","CBR","NTR-VBR","ABR","ANY","RT-VBR","UBR+","GFR"}; + static const char *const aal_name[] = { + "---", "1", "2", "3/4", /* 0- 3 */ + "???", "5", "???", "???", /* 4- 7 */ +Index: linux-3.7-rc6/arch/mips/include/asm/mach-lantiq/lantiq_atm.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc6/arch/mips/include/asm/mach-lantiq/lantiq_atm.h 2012-12-06 21:03:13.850060387 +0100 +@@ -0,0 +1,196 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifx_atm.h ++** PROJECT : UEIP ++** MODULES : ATM ++** ++** DATE : 17 Jun 2009 ++** AUTHOR : Xu Liang ++** DESCRIPTION : Global ATM driver header file ++** COPYRIGHT : Copyright (c) 2006 ++** 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 ++** $Date $Author $Comment ++** 07 JUL 2009 Xu Liang Init Version ++*******************************************************************************/ ++ ++#ifndef IFX_ATM_H ++#define IFX_ATM_H ++ ++ ++ ++/*! ++ \defgroup IFX_ATM UEIP Project - ATM driver module ++ \brief UEIP Project - ATM driver module, support Danube, Amazon-SE, AR9, VR9. ++ */ ++ ++/*! ++ \defgroup IFX_ATM_IOCTL IOCTL Commands ++ \ingroup IFX_ATM ++ \brief IOCTL Commands used by user application. ++ */ ++ ++/*! ++ \defgroup IFX_ATM_STRUCT Structures ++ \ingroup IFX_ATM ++ \brief Structures used by user application. ++ */ ++ ++/*! ++ \file ifx_atm.h ++ \ingroup IFX_ATM ++ \brief ATM driver header file ++ */ ++ ++ ++ ++/* ++ * #################################### ++ * Definition ++ * #################################### ++ */ ++ ++/*! ++ \addtogroup IFX_ATM_STRUCT ++ */ ++/*@{*/ ++ ++/* ++ * ATM MIB ++ */ ++ ++/*! ++ \struct atm_cell_ifEntry_t ++ \brief Structure used for Cell Level MIB Counters. ++ ++ User application use this structure to call IOCTL command "PPE_ATM_MIB_CELL". ++ */ ++typedef struct { ++ __u32 ifHCInOctets_h; /*!< byte counter of ingress cells (upper 32 bits, total 64 bits) */ ++ __u32 ifHCInOctets_l; /*!< byte counter of ingress cells (lower 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_h; /*!< byte counter of egress cells (upper 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_l; /*!< byte counter of egress cells (lower 32 bits, total 64 bits) */ ++ __u32 ifInErrors; /*!< counter of error ingress cells */ ++ __u32 ifInUnknownProtos; /*!< counter of unknown ingress cells */ ++ __u32 ifOutErrors; /*!< counter of error egress cells */ ++} atm_cell_ifEntry_t; ++ ++/*! ++ \struct atm_aal5_ifEntry_t ++ \brief Structure used for AAL5 Frame Level MIB Counters. ++ ++ User application use this structure to call IOCTL command "PPE_ATM_MIB_AAL5". ++ */ ++typedef struct { ++ __u32 ifHCInOctets_h; /*!< byte counter of ingress packets (upper 32 bits, total 64 bits) */ ++ __u32 ifHCInOctets_l; /*!< byte counter of ingress packets (lower 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_h; /*!< byte counter of egress packets (upper 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_l; /*!< byte counter of egress packets (lower 32 bits, total 64 bits) */ ++ __u32 ifInUcastPkts; /*!< counter of ingress packets */ ++ __u32 ifOutUcastPkts; /*!< counter of egress packets */ ++ __u32 ifInErrors; /*!< counter of error ingress packets */ ++ __u32 ifInDiscards; /*!< counter of dropped ingress packets */ ++ __u32 ifOutErros; /*!< counter of error egress packets */ ++ __u32 ifOutDiscards; /*!< counter of dropped egress packets */ ++} atm_aal5_ifEntry_t; ++ ++/*! ++ \struct atm_aal5_vcc_t ++ \brief Structure used for per PVC AAL5 Frame Level MIB Counters. ++ ++ This structure is a part of structure "atm_aal5_vcc_x_t". ++ */ ++typedef struct { ++ __u32 aal5VccCrcErrors; /*!< counter of ingress packets with CRC error */ ++ __u32 aal5VccSarTimeOuts; /*!< counter of ingress packets with Re-assemble timeout */ //no timer support yet ++ __u32 aal5VccOverSizedSDUs; /*!< counter of oversized ingress packets */ ++} atm_aal5_vcc_t; ++ ++/*! ++ \struct atm_aal5_vcc_x_t ++ \brief Structure used for per PVC AAL5 Frame Level MIB Counters. ++ ++ User application use this structure to call IOCTL command "PPE_ATM_MIB_VCC". ++ */ ++typedef struct { ++ int vpi; /*!< VPI of the VCC to get MIB counters */ ++ int vci; /*!< VCI of the VCC to get MIB counters */ ++ atm_aal5_vcc_t mib_vcc; /*!< structure to get MIB counters */ ++} atm_aal5_vcc_x_t; ++ ++/*@}*/ ++ ++ ++ ++/* ++ * #################################### ++ * IOCTL ++ * #################################### ++ */ ++ ++/*! ++ \addtogroup IFX_ATM_IOCTL ++ */ ++/*@{*/ ++ ++/* ++ * ioctl Command ++ */ ++/*! ++ \brief ATM IOCTL Magic Number ++ */ ++#define PPE_ATM_IOC_MAGIC 'o' ++/*! ++ \brief ATM IOCTL Command - Get Cell Level MIB Counters ++ ++ This command is obsolete. User can get cell level MIB from DSL API. ++ This command uses structure "atm_cell_ifEntry_t" as parameter for output of MIB counters. ++ */ ++#define PPE_ATM_MIB_CELL _IOW(PPE_ATM_IOC_MAGIC, 0, atm_cell_ifEntry_t) ++/*! ++ \brief ATM IOCTL Command - Get AAL5 Level MIB Counters ++ ++ Get AAL5 packet counters. ++ This command uses structure "atm_aal5_ifEntry_t" as parameter for output of MIB counters. ++ */ ++#define PPE_ATM_MIB_AAL5 _IOW(PPE_ATM_IOC_MAGIC, 1, atm_aal5_ifEntry_t) ++/*! ++ \brief ATM IOCTL Command - Get Per PVC MIB Counters ++ ++ Get AAL5 packet counters for each PVC. ++ This command uses structure "atm_aal5_vcc_x_t" as parameter for input of VPI/VCI information and output of MIB counters. ++ */ ++#define PPE_ATM_MIB_VCC _IOWR(PPE_ATM_IOC_MAGIC, 2, atm_aal5_vcc_x_t) ++/*! ++ \brief Total Number of ATM IOCTL Commands ++ */ ++#define PPE_ATM_IOC_MAXNR 3 ++ ++/*@}*/ ++ ++ ++ ++/* ++ * #################################### ++ * API ++ * #################################### ++ */ ++ ++#ifdef __KERNEL__ ++struct port_cell_info { ++ unsigned int port_num; ++ unsigned int tx_link_rate[2]; ++}; ++#endif ++ ++ ++ ++#endif // IFX_ATM_H ++ +Index: linux-3.7-rc6/arch/mips/include/asm/mach-lantiq/lantiq_ptm.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc6/arch/mips/include/asm/mach-lantiq/lantiq_ptm.h 2012-12-06 21:03:13.854060387 +0100 +@@ -0,0 +1,203 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifx_ptm.h ++** PROJECT : UEIP ++** MODULES : PTM ++** ++** DATE : 17 Jun 2009 ++** AUTHOR : Xu Liang ++** DESCRIPTION : Global PTM driver header file ++** COPYRIGHT : Copyright (c) 2006 ++** 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 ++** $Date $Author $Comment ++** 07 JUL 2009 Xu Liang Init Version ++*******************************************************************************/ ++ ++#ifndef IFX_PTM_H ++#define IFX_PTM_H ++ ++ ++ ++/*! ++ \defgroup IFX_PTM UEIP Project - PTM driver module ++ \brief UEIP Project - PTM driver module, support Danube, Amazon-SE, AR9, VR9. ++ */ ++ ++/*! ++ \defgroup IFX_PTM_IOCTL IOCTL Commands ++ \ingroup IFX_PTM ++ \brief IOCTL Commands used by user application. ++ */ ++ ++/*! ++ \defgroup IFX_PTM_STRUCT Structures ++ \ingroup IFX_PTM ++ \brief Structures used by user application. ++ */ ++ ++/*! ++ \file ifx_ptm.h ++ \ingroup IFX_PTM ++ \brief PTM driver header file ++ */ ++ ++ ++ ++/* ++ * #################################### ++ * Definition ++ * #################################### ++ */ ++ ++ ++ ++/* ++ * #################################### ++ * IOCTL ++ * #################################### ++ */ ++ ++/*! ++ \addtogroup IFX_PTM_IOCTL ++ */ ++/*@{*/ ++ ++/* ++ * ioctl Command ++ */ ++/*! ++ \brief PTM IOCTL Command - Get codeword MIB counters. ++ ++ This command uses structure "PTM_CW_IF_ENTRY_T" to get codeword level MIB counters. ++ */ ++#define IFX_PTM_MIB_CW_GET SIOCDEVPRIVATE + 1 ++/*! ++ \brief PTM IOCTL Command - Get packet MIB counters. ++ ++ This command uses structure "PTM_FRAME_MIB_T" to get packet level MIB counters. ++ */ ++#define IFX_PTM_MIB_FRAME_GET SIOCDEVPRIVATE + 2 ++/*! ++ \brief PTM IOCTL Command - Get firmware configuration (CRC). ++ ++ This command uses structure "IFX_PTM_CFG_T" to get firmware configuration (CRC). ++ */ ++#define IFX_PTM_CFG_GET SIOCDEVPRIVATE + 3 ++/*! ++ \brief PTM IOCTL Command - Set firmware configuration (CRC). ++ ++ This command uses structure "IFX_PTM_CFG_T" to set firmware configuration (CRC). ++ */ ++#define IFX_PTM_CFG_SET SIOCDEVPRIVATE + 4 ++/*! ++ \brief PTM IOCTL Command - Program priority value to TX queue mapping. ++ ++ This command uses structure "IFX_PTM_PRIO_Q_MAP_T" to program priority value to TX queue mapping. ++ */ ++#define IFX_PTM_MAP_PKT_PRIO_TO_Q SIOCDEVPRIVATE + 14 ++ ++/*@}*/ ++ ++ ++/*! ++ \addtogroup IFX_PTM_STRUCT ++ */ ++/*@{*/ ++ ++/* ++ * ioctl Data Type ++ */ ++ ++/*! ++ \typedef PTM_CW_IF_ENTRY_T ++ \brief Wrapping of structure "ptm_cw_ifEntry_t". ++ */ ++/*! ++ \struct ptm_cw_ifEntry_t ++ \brief Structure used for CodeWord level MIB counters. ++ */ ++typedef struct ptm_cw_ifEntry_t { ++ uint32_t ifRxNoIdleCodewords; /*!< output, number of ingress user codeword */ ++ uint32_t ifRxIdleCodewords; /*!< output, number of ingress idle codeword */ ++ uint32_t ifRxCodingViolation; /*!< output, number of error ingress codeword */ ++ uint32_t ifTxNoIdleCodewords; /*!< output, number of egress user codeword */ ++ uint32_t ifTxIdleCodewords; /*!< output, number of egress idle codeword */ ++} PTM_CW_IF_ENTRY_T; ++ ++/*! ++ \typedef PTM_FRAME_MIB_T ++ \brief Wrapping of structure "ptm_frame_mib_t". ++ */ ++/*! ++ \struct ptm_frame_mib_t ++ \brief Structure used for packet level MIB counters. ++ */ ++typedef struct ptm_frame_mib_t { ++ uint32_t RxCorrect; /*!< output, number of ingress packet */ ++ uint32_t TC_CrcError; /*!< output, number of egress packet with CRC error */ ++ uint32_t RxDropped; /*!< output, number of dropped ingress packet */ ++ uint32_t TxSend; /*!< output, number of egress packet */ ++} PTM_FRAME_MIB_T; ++ ++/*! ++ \typedef IFX_PTM_CFG_T ++ \brief Wrapping of structure "ptm_cfg_t". ++ */ ++/*! ++ \struct ptm_cfg_t ++ \brief Structure used for ETH/TC CRC configuration. ++ */ ++typedef struct ptm_cfg_t { ++ uint32_t RxEthCrcPresent; /*!< input/output, ingress packet has ETH CRC */ ++ uint32_t RxEthCrcCheck; /*!< input/output, check ETH CRC of ingress packet */ ++ uint32_t RxTcCrcCheck; /*!< input/output, check TC CRC of ingress codeword */ ++ uint32_t RxTcCrcLen; /*!< input/output, length of TC CRC of ingress codeword */ ++ uint32_t TxEthCrcGen; /*!< input/output, generate ETH CRC for egress packet */ ++ uint32_t TxTcCrcGen; /*!< input/output, generate TC CRC for egress codeword */ ++ uint32_t TxTcCrcLen; /*!< input/output, length of TC CRC of egress codeword */ ++} IFX_PTM_CFG_T; ++ ++/*! ++ \typedef IFX_PTM_PRIO_Q_MAP_T ++ \brief Wrapping of structure "ppe_prio_q_map". ++ */ ++/*! ++ \struct ppe_prio_q_map ++ \brief Structure used for Priority Value to TX Queue mapping. ++ */ ++typedef struct ppe_prio_q_map { ++ int pkt_prio; ++ int qid; ++ int vpi; // ignored in eth interface ++ int vci; // ignored in eth interface ++} IFX_PTM_PRIO_Q_MAP_T; ++ ++/*@}*/ ++ ++ ++ ++/* ++ * #################################### ++ * API ++ * #################################### ++ */ ++ ++#ifdef __KERNEL__ ++struct port_cell_info { ++ unsigned int port_num; ++ unsigned int tx_link_rate[2]; ++}; ++#endif ++ ++ ++ ++#endif // IFX_PTM_H ++ +Index: linux-3.7-rc6/include/uapi/linux/atm.h +=================================================================== +--- linux-3.7-rc6.orig/include/uapi/linux/atm.h 2012-12-06 21:02:41.458059575 +0100 ++++ linux-3.7-rc6/include/uapi/linux/atm.h 2012-12-06 21:04:21.282062078 +0100 +@@ -130,8 +130,14 @@ + #define ATM_ABR 4 + #define ATM_ANYCLASS 5 /* compatible with everything */ + ++#define ATM_VBR_NRT ATM_VBR ++#define ATM_VBR_RT 6 ++#define ATM_UBR_PLUS 7 ++#define ATM_GFR 8 ++ + #define ATM_MAX_PCR -1 /* maximum available PCR */ + ++ + struct atm_trafprm { + unsigned char traffic_class; /* traffic class (ATM_UBR, ...) */ + int max_pcr; /* maximum PCR in cells per second */ diff --git a/target/linux/lantiq/patches-3.7/0120-owrt-generic-dtb-image-hack.patch b/target/linux/lantiq/patches-3.7/0120-owrt-generic-dtb-image-hack.patch new file mode 100644 index 0000000000..aed0009136 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0120-owrt-generic-dtb-image-hack.patch @@ -0,0 +1,26 @@ +From ac676d9516d9d14b98eef3dec05badae1d1a331a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 2 Nov 2012 15:40:08 +0100 +Subject: [PATCH 120/123] owrt: generic dtb image hack + +--- + arch/mips/kernel/head.S | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S +index ea695d9..be87456 100644 +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -141,6 +141,9 @@ FEXPORT(__kernel_entry) + j kernel_entry + #endif + ++ .ascii "OWRTDTB:" ++ EXPORT(__image_dtb) ++ .fill 0x4000 + __REF + + NESTED(kernel_entry, 16, sp) # kernel entry point +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0121-owrt-lantiq-dtb-image-hack.patch b/target/linux/lantiq/patches-3.7/0121-owrt-lantiq-dtb-image-hack.patch new file mode 100644 index 0000000000..eef7da47b4 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0121-owrt-lantiq-dtb-image-hack.patch @@ -0,0 +1,44 @@ +From d8f83a608bc854dbbe6b2ea5436e9b34516af8e4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 6 Dec 2012 16:09:08 +0100 +Subject: [PATCH 121/123] owrt: lantiq dtb image hack + +--- + arch/mips/lantiq/prom.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +Index: linux-3.7-rc8/arch/mips/lantiq/prom.c +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/lantiq/prom.c 2012-12-14 23:08:36.451576994 +0100 ++++ linux-3.7-rc8/arch/mips/lantiq/prom.c 2012-12-14 23:08:36.607576999 +0100 +@@ -72,6 +72,8 @@ + return 0; + } + ++extern struct boot_param_header __image_dtb; ++ + void __init plat_mem_setup(void) + { + ioport_resource.start = IOPORT_RESOURCE_START; +@@ -85,7 +87,7 @@ + * Load the builtin devicetree. This causes the chosen node to be + * parsed resulting in our memory appearing + */ +- __dt_setup_arch(&__dtb_start); ++ __dt_setup_arch(&__image_dtb); + + of_scan_flat_dt(early_init_dt_scan_model, NULL); + } +Index: linux-3.7-rc8/arch/mips/lantiq/Makefile +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/lantiq/Makefile 2012-12-14 23:08:36.459576996 +0100 ++++ linux-3.7-rc8/arch/mips/lantiq/Makefile 2012-12-15 01:40:16.519805129 +0100 +@@ -6,8 +6,6 @@ + + obj-y := irq.o clk.o prom.o + +-obj-y += dts/ +- + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + + obj-$(CONFIG_SOC_TYPE_XWAY) += xway/ diff --git a/target/linux/lantiq/patches-3.7/0122-MIPS-lantiq-adds-pcie-driver.patch b/target/linux/lantiq/patches-3.7/0122-MIPS-lantiq-adds-pcie-driver.patch new file mode 100644 index 0000000000..9319858f5e --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0122-MIPS-lantiq-adds-pcie-driver.patch @@ -0,0 +1,4786 @@ +From b0b68cd5b5da72950863af882c368f28f65690e8 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 6 Dec 2012 11:43:53 +0100 +Subject: [PATCH 122/123] MIPS: lantiq: adds 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 100755 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 edeb58c..116765a 100644 +--- a/arch/mips/lantiq/Kconfig ++++ b/arch/mips/lantiq/Kconfig +@@ -17,6 +17,7 @@ config SOC_XWAY + bool "XWAY" + select SOC_TYPE_XWAY + select HW_HAS_PCI ++ select ARCH_SUPPORTS_MSI + + config SOC_FALCON + bool "FALCON" +@@ -40,6 +41,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 75e1b7d..b077d49 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 e13a71c..46d7096 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -44,6 +44,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 ++#include ++#include ++ ++#include ++ ++#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 100755 +index 0000000..5f6ab83 +--- /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 ++/*! ++ \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..de6e2fa +--- /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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 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 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..49a4d2f +--- /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 ++#include ++#include ++#include ++#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 ++#endif /* AUTOCONF_INCLUDED */ ++#include ++#include ++ ++/* Project header file */ ++#include ++#include ++#include ++#include ++ ++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) { ++ /* Config AHB->PCIe and PDI endianness */ ++ reg |= IFX_RCU_BE_PCIE0_PDI; ++ } ++ else { ++ /* Config AHB->PCIe and PDI endianness */ ++ reg |= IFX_RCU_BE_PCIE1_PDI; ++ } ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++} ++ ++static inline void pcie_pdi_pmu_enable(int pcie_port) ++{ ++ if (pcie_port == 0) { ++ /* Enable PDI to access PCIe PHY register */ ++ PDI0_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++ else { ++ PDI1_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++} ++ ++static inline void pcie_core_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ ++ /* Reset Core, bit 22 */ ++ if (pcie_port == 0) { ++ reg |= 0x00400000; ++ } ++ else { ++ reg |= 0x08000000; /* Bit 27 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_core_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ if (pcie_port == 0) { ++ reg &= ~0x00400000; /* bit 22 */ ++ } ++ else { ++ reg &= ~0x08000000; /* Bit 27 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ if (pcie_port == 0) { ++ reg |= 0x00001000; /* Bit 12 */ ++ } ++ else { ++ reg |= 0x00002000; /* Bit 13 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ if (pcie_port == 0) { ++ reg &= ~0x00001000; /* Bit 12 */ ++ } ++ else { ++ reg &= ~0x00002000; /* Bit 13 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_device_rst_assert(int pcie_port) ++{ ++ if (pcie_port == 0) { ++ ifx_ebu_led_set_data(11, 0); ++ } ++ else { ++ ifx_ebu_led_set_data(12, 0); ++ } ++} ++ ++static inline void pcie_device_rst_deassert(int pcie_port) ++{ ++ mdelay(100); ++ if (pcie_port == 0) { ++ ifx_ebu_led_set_data(11, 1); ++ } ++ else { ++ ifx_ebu_led_set_data(12, 1); ++ } ++ ifx_ebu_led_disable(); ++} ++ ++static inline void pcie_core_pmu_setup(int pcie_port) ++{ ++ if (pcie_port == 0) { ++ PCIE0_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++ else { ++ PCIE1_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++} ++ ++static inline void pcie_msi_init(int pcie_port) ++{ ++ pcie_msi_pic_init(pcie_port); ++ if (pcie_port == 0) { ++ MSI0_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++ else { ++ MSI1_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++} ++ ++static inline u32 ++ifx_pcie_bus_nr_deduct(u32 bus_number, int pcie_port) ++{ ++ u32 tbus_number = bus_number; ++ ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ if (pcie_port == IFX_PCIE_PORT1) { /* Port 1 must check if there are two cores enabled */ ++ if (pcibios_host_nr() > 1) { ++ tbus_number -= pcibios_1st_host_bus_nr(); ++ } ++ } ++#endif /* CONFIG_IFX_PCI */ ++ return tbus_number; ++} ++ ++static inline u32 ++ifx_pcie_bus_enum_hack(struct pci_bus *bus, u32 devfn, int where, u32 value, int pcie_port, int read) ++{ ++ struct pci_dev *pdev; ++ u32 tvalue = value; ++ ++ /* Sanity check */ ++ pdev = pci_get_slot(bus, devfn); ++ if (pdev == NULL) { ++ return tvalue; ++ } ++ ++ /* Only care about PCI bridge */ ++ if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { ++ return tvalue; ++ } ++ ++ if (read) { /* Read hack */ ++ #ifdef CONFIG_IFX_PCIE_2ND_CORE ++ if (pcie_port == IFX_PCIE_PORT1) { /* Port 1 must check if there are two cores enabled */ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_read_hack(where, tvalue); ++ } ++ } ++ #endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++ } ++ else { /* Write hack */ ++ #ifdef CONFIG_IFX_PCIE_2ND_CORE ++ if (pcie_port == IFX_PCIE_PORT1) { /* Port 1 must check if there are two cores enabled */ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_write_hack(where, tvalue); ++ } ++ } ++ #endif ++ } ++ return tvalue; ++} ++ ++#endif /* IFXMIPS_PCIE_AR10_H */ +diff --git a/arch/mips/pci/ifxmips_pcie_msi.c b/arch/mips/pci/ifxmips_pcie_msi.c +new file mode 100644 +index 0000000..5bee7f8 +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_msi.c +@@ -0,0 +1,392 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_msi.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCI MSI sub module ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe MSI 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 ++** $Date $Author $Comment ++** 02 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \defgroup IFX_PCIE_MSI MSI OS APIs ++ \ingroup IFX_PCIE ++ \brief PCIe bus driver OS interface functions ++*/ ++ ++/*! ++ \file ifxmips_pcie_msi.c ++ \ingroup IFX_PCIE ++ \brief PCIe MSI OS interface file ++*/ ++ ++#ifndef AUTOCONF_INCLUDED ++#include ++#endif /* AUTOCONF_INCLUDED */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "ifxmips_pcie_reg.h" ++#include "ifxmips_pcie.h" ++ ++#define IFX_MSI_IRQ_NUM 16 ++ ++enum { ++ IFX_PCIE_MSI_IDX0 = 0, ++ IFX_PCIE_MSI_IDX1, ++ IFX_PCIE_MSI_IDX2, ++ IFX_PCIE_MSI_IDX3, ++}; ++ ++typedef struct ifx_msi_irq_idx { ++ const int irq; ++ const int idx; ++}ifx_msi_irq_idx_t; ++ ++struct ifx_msi_pic { ++ volatile u32 pic_table[IFX_MSI_IRQ_NUM]; ++ volatile u32 pic_endian; /* 0x40 */ ++}; ++typedef struct ifx_msi_pic *ifx_msi_pic_t; ++ ++typedef struct ifx_msi_irq { ++ const volatile ifx_msi_pic_t msi_pic_p; ++ const u32 msi_phy_base; ++ const ifx_msi_irq_idx_t msi_irq_idx[IFX_MSI_IRQ_NUM]; ++ /* ++ * Each bit in msi_free_irq_bitmask represents a MSI interrupt that is ++ * in use. ++ */ ++ u16 msi_free_irq_bitmask; ++ ++ /* ++ * Each bit in msi_multiple_irq_bitmask tells that the device using ++ * this bit in msi_free_irq_bitmask is also using the next bit. This ++ * is used so we can disable all of the MSI interrupts when a device ++ * uses multiple. ++ */ ++ u16 msi_multiple_irq_bitmask; ++}ifx_msi_irq_t; ++ ++static ifx_msi_irq_t msi_irqs[IFX_PCIE_CORE_NR] = { ++ { ++ .msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI_PIC_REG_BASE, ++ .msi_phy_base = PCIE_MSI_PHY_BASE, ++ .msi_irq_idx = { ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ }, ++ .msi_free_irq_bitmask = 0, ++ .msi_multiple_irq_bitmask= 0, ++ }, ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ { ++ .msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI1_PIC_REG_BASE, ++ .msi_phy_base = PCIE1_MSI_PHY_BASE, ++ .msi_irq_idx = { ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ }, ++ .msi_free_irq_bitmask = 0, ++ .msi_multiple_irq_bitmask= 0, ++ ++ }, ++#endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++}; ++ ++/* ++ * This lock controls updates to msi_free_irq_bitmask, ++ * msi_multiple_irq_bitmask and pic register settting ++ */ ++static DEFINE_SPINLOCK(ifx_pcie_msi_lock); ++ ++void pcie_msi_pic_init(int pcie_port) ++{ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_pic_p->pic_endian = IFX_MSI_PIC_BIG_ENDIAN; ++ spin_unlock(&ifx_pcie_msi_lock); ++} ++ ++/** ++ * \fn int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) ++ * \brief Called when a driver request MSI interrupts instead of the ++ * legacy INT A-D. This routine will allocate multiple interrupts ++ * for MSI devices that support them. A device can override this by ++ * programming the MSI control bits [6:4] before calling ++ * pci_enable_msi(). ++ * ++ * \param[in] pdev Device requesting MSI interrupts ++ * \param[in] desc MSI descriptor ++ * ++ * \return -EINVAL Invalid pcie root port or invalid msi bit ++ * \return 0 OK ++ * \ingroup IFX_PCIE_MSI ++ */ ++int ++arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) ++{ ++ int irq, pos; ++ u16 control; ++ int irq_idx; ++ int irq_step; ++ int configured_private_bits; ++ int request_private_bits; ++ struct msi_msg msg; ++ u16 search_mask; ++ struct ifx_pci_controller *ctrl = pdev->bus->sysdata; ++ int pcie_port = ctrl->port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s %s enter\n", __func__, pci_name(pdev)); ++ ++ /* XXX, skip RC MSI itself */ ++ if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) { ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s RC itself doesn't use MSI interrupt\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * Read the MSI config to figure out how many IRQs this device ++ * wants. Most devices only want 1, which will give ++ * configured_private_bits and request_private_bits equal 0. ++ */ ++ pci_read_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &control); ++ ++ /* ++ * If the number of private bits has been configured then use ++ * that value instead of the requested number. This gives the ++ * driver the chance to override the number of interrupts ++ * before calling pci_enable_msi(). ++ */ ++ configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4; ++ if (configured_private_bits == 0) { ++ /* Nothing is configured, so use the hardware requested size */ ++ request_private_bits = (control & PCI_MSI_FLAGS_QMASK) >> 1; ++ } ++ else { ++ /* ++ * Use the number of configured bits, assuming the ++ * driver wanted to override the hardware request ++ * value. ++ */ ++ request_private_bits = configured_private_bits; ++ } ++ ++ /* ++ * The PCI 2.3 spec mandates that there are at most 32 ++ * interrupts. If this device asks for more, only give it one. ++ */ ++ if (request_private_bits > 5) { ++ request_private_bits = 0; ++ } ++again: ++ /* ++ * The IRQs have to be aligned on a power of two based on the ++ * number being requested. ++ */ ++ irq_step = (1 << request_private_bits); ++ ++ /* Mask with one bit for each IRQ */ ++ search_mask = (1 << irq_step) - 1; ++ ++ /* ++ * We're going to search msi_free_irq_bitmask_lock for zero ++ * bits. This represents an MSI interrupt number that isn't in ++ * use. ++ */ ++ spin_lock(&ifx_pcie_msi_lock); ++ for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos += irq_step) { ++ if ((msi_irqs[pcie_port].msi_free_irq_bitmask & (search_mask << pos)) == 0) { ++ msi_irqs[pcie_port].msi_free_irq_bitmask |= search_mask << pos; ++ msi_irqs[pcie_port].msi_multiple_irq_bitmask |= (search_mask >> 1) << pos; ++ break; ++ } ++ } ++ spin_unlock(&ifx_pcie_msi_lock); ++ ++ /* Make sure the search for available interrupts didn't fail */ ++ if (pos >= IFX_MSI_IRQ_NUM) { ++ if (request_private_bits) { ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s: Unable to find %d free " ++ "interrupts, trying just one", __func__, 1 << request_private_bits); ++ request_private_bits = 0; ++ goto again; ++ } ++ else { ++ printk(KERN_ERR "%s: Unable to find a free MSI interrupt\n", __func__); ++ return -EINVAL; ++ } ++ } ++ irq = msi_irqs[pcie_port].msi_irq_idx[pos].irq; ++ irq_idx = msi_irqs[pcie_port].msi_irq_idx[pos].idx; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "pos %d, irq %d irq_idx %d\n", pos, irq, irq_idx); ++ ++ /* ++ * Initialize MSI. This has to match the memory-write endianess from the device ++ * Address bits [23:12] ++ */ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] = SM(irq_idx, IFX_MSI_PIC_INT_LINE) | ++ SM((msi_irqs[pcie_port].msi_phy_base >> 12), IFX_MSI_PIC_MSG_ADDR) | ++ SM((1 << pos), IFX_MSI_PIC_MSG_DATA); ++ ++ /* Enable this entry */ ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~IFX_MSI_PCI_INT_DISABLE; ++ spin_unlock(&ifx_pcie_msi_lock); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "pic_table[%d]: 0x%08x\n", ++ pos, msi_irqs[pcie_port].msi_pic_p->pic_table[pos]); ++ ++ /* Update the number of IRQs the device has available to it */ ++ control &= ~PCI_MSI_FLAGS_QSIZE; ++ control |= (request_private_bits << 4); ++ pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, control); ++ ++ set_irq_msi(irq, desc); ++ msg.address_hi = 0x0; ++ msg.address_lo = msi_irqs[pcie_port].msi_phy_base; ++ msg.data = SM((1 << pos), IFX_MSI_PIC_MSG_DATA); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "msi_data: pos %d 0x%08x\n", pos, msg.data); ++ ++ write_msi_msg(irq, &msg); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__); ++ return 0; ++} ++ ++static int ++pcie_msi_irq_to_port(unsigned int irq, int *port) ++{ ++ int ret = 0; ++ ++ if (irq == IFX_PCIE_MSI_IR0 || irq == IFX_PCIE_MSI_IR1 || ++ irq == IFX_PCIE_MSI_IR2 || irq == IFX_PCIE_MSI_IR3) { ++ *port = IFX_PCIE_PORT0; ++ } ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ else if (irq == IFX_PCIE1_MSI_IR0 || irq == IFX_PCIE1_MSI_IR1 || ++ irq == IFX_PCIE1_MSI_IR2 || irq == IFX_PCIE1_MSI_IR3) { ++ *port = IFX_PCIE_PORT1; ++ } ++#endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++ else { ++ printk(KERN_ERR "%s: Attempted to teardown illegal " ++ "MSI interrupt (%d)\n", __func__, irq); ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++/** ++ * \fn void arch_teardown_msi_irq(unsigned int irq) ++ * \brief Called when a device no longer needs its MSI interrupts. All ++ * MSI interrupts for the device are freed. ++ * ++ * \param irq The devices first irq number. There may be multple in sequence. ++ * \return none ++ * \ingroup IFX_PCIE_MSI ++ */ ++void ++arch_teardown_msi_irq(unsigned int irq) ++{ ++ int pos; ++ int number_irqs; ++ u16 bitmask; ++ int pcie_port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s enter\n", __func__); ++ ++ BUG_ON(irq > INT_NUM_IM4_IRL31); ++ ++ if (pcie_msi_irq_to_port(irq, &pcie_port) != 0) { ++ return; ++ } ++ ++ /* Shift the mask to the correct bit location, not always correct ++ * Probally, the first match will be chosen. ++ */ ++ for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos++) { ++ if ((msi_irqs[pcie_port].msi_irq_idx[pos].irq == irq) ++ && (msi_irqs[pcie_port].msi_free_irq_bitmask & ( 1 << pos))) { ++ break; ++ } ++ } ++ if (pos >= IFX_MSI_IRQ_NUM) { ++ printk(KERN_ERR "%s: Unable to find a matched MSI interrupt\n", __func__); ++ return; ++ } ++ spin_lock(&ifx_pcie_msi_lock); ++ /* Disable this entry */ ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] |= IFX_MSI_PCI_INT_DISABLE; ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~(IFX_MSI_PIC_INT_LINE | IFX_MSI_PIC_MSG_ADDR | IFX_MSI_PIC_MSG_DATA); ++ spin_unlock(&ifx_pcie_msi_lock); ++ /* ++ * Count the number of IRQs we need to free by looking at the ++ * msi_multiple_irq_bitmask. Each bit set means that the next ++ * IRQ is also owned by this device. ++ */ ++ number_irqs = 0; ++ while (((pos + number_irqs) < IFX_MSI_IRQ_NUM) && ++ (msi_irqs[pcie_port].msi_multiple_irq_bitmask & (1 << (pos + number_irqs)))) { ++ number_irqs++; ++ } ++ number_irqs++; ++ ++ /* Mask with one bit for each IRQ */ ++ bitmask = (1 << number_irqs) - 1; ++ ++ bitmask <<= pos; ++ if ((msi_irqs[pcie_port].msi_free_irq_bitmask & bitmask) != bitmask) { ++ printk(KERN_ERR "%s: Attempted to teardown MSI " ++ "interrupt (%d) not in use\n", __func__, irq); ++ return; ++ } ++ /* Checks are done, update the in use bitmask */ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_free_irq_bitmask &= ~bitmask; ++ msi_irqs[pcie_port].msi_multiple_irq_bitmask &= ~(bitmask >> 1); ++ spin_unlock(&ifx_pcie_msi_lock); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Chuanhua.Lei@infineon.com"); ++MODULE_SUPPORTED_DEVICE("Infineon PCIe IP builtin MSI PIC module"); ++MODULE_DESCRIPTION("Infineon PCIe IP builtin MSI PIC driver"); ++ +diff --git a/arch/mips/pci/ifxmips_pcie_phy.c b/arch/mips/pci/ifxmips_pcie_phy.c +new file mode 100644 +index 0000000..a4171a7 +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_phy.c +@@ -0,0 +1,478 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_phy.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe PHY sub module ++** ++** DATE : 14 May 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 14 May,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \file ifxmips_pcie_phy.c ++ \ingroup IFX_PCIE ++ \brief PCIe PHY PLL register programming source file ++*/ ++#include ++#include ++#include ++#include ++ ++#include "ifxmips_pcie_reg.h" ++#include "ifxmips_pcie.h" ++ ++/* PCIe PDI only supports 16 bit operation */ ++ ++#define IFX_PCIE_PHY_REG_WRITE16(__addr, __data) \ ++ ((*(volatile u16 *) (__addr)) = (__data)) ++ ++#define IFX_PCIE_PHY_REG_READ16(__addr) \ ++ (*(volatile u16 *) (__addr)) ++ ++#define IFX_PCIE_PHY_REG16(__addr) \ ++ (*(volatile u16 *) (__addr)) ++ ++#define IFX_PCIE_PHY_REG(__reg, __value, __mask) do { \ ++ u16 read_data; \ ++ u16 write_data; \ ++ read_data = IFX_PCIE_PHY_REG_READ16((__reg)); \ ++ write_data = (read_data & ((u16)~(__mask))) | (((u16)(__value)) & ((u16)(__mask)));\ ++ IFX_PCIE_PHY_REG_WRITE16((__reg), write_data); \ ++} while (0) ++ ++#define IFX_PCIE_PLL_TIMEOUT 1000 /* Tunnable */ ++ ++//#define IFX_PCI_PHY_REG_DUMP ++ ++#ifdef IFX_PCI_PHY_REG_DUMP ++static void ++pcie_phy_reg_dump(int pcie_port) ++{ ++ printk("PLL REGFILE\n"); ++ printk("PCIE_PHY_PLL_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL1(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL2(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL3(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL4 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL4(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL5 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL5(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL6 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL6(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL7 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL7(pcie_port))); ++ printk("PCIE_PHY_PLL_A_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_A_CTRL1(pcie_port))); ++ printk("PCIE_PHY_PLL_A_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_A_CTRL2(pcie_port))); ++ printk("PCIE_PHY_PLL_A_CTRL3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_A_CTRL3(pcie_port))); ++ printk("PCIE_PHY_PLL_STATUS 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_STATUS(pcie_port))); ++ ++ printk("TX1 REGFILE\n"); ++ printk("PCIE_PHY_TX1_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX1_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX1_CTRL3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_CTRL3(pcie_port))); ++ printk("PCIE_PHY_TX1_A_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_A_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX1_A_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_A_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX1_MOD1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_MOD1(pcie_port))); ++ printk("PCIE_PHY_TX1_MOD2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_MOD2(pcie_port))); ++ printk("PCIE_PHY_TX1_MOD3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_MOD3(pcie_port))); ++ ++ printk("TX2 REGFILE\n"); ++ printk("PCIE_PHY_TX2_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX2_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX2_A_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_A_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX2_A_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_A_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX2_MOD1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_MOD1(pcie_port))); ++ printk("PCIE_PHY_TX2_MOD2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_MOD2(pcie_port))); ++ printk("PCIE_PHY_TX2_MOD3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_MOD3(pcie_port))); ++ ++ printk("RX1 REGFILE\n"); ++ printk("PCIE_PHY_RX1_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_CTRL1(pcie_port))); ++ printk("PCIE_PHY_RX1_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_CTRL2(pcie_port))); ++ printk("PCIE_PHY_RX1_CDR 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_CDR(pcie_port))); ++ printk("PCIE_PHY_RX1_EI 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_EI(pcie_port))); ++ printk("PCIE_PHY_RX1_A_CTRL 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_A_CTRL(pcie_port))); ++} ++#endif /* IFX_PCI_PHY_REG_DUMP */ ++ ++static void ++pcie_phy_comm_setup(int pcie_port) ++{ ++ /* PLL Setting */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF); ++ ++ /* increase the bias reference voltage */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF); ++ ++ /* Endcnt */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF); ++ ++ /* force */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF); ++ ++ /* ctrl_lim */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF); ++ ++ /* ctrl */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00); ++ ++ /* RTERM*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF); ++ ++ /* Improved 100MHz clock output */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF); ++ ++ /* Reduced CDR BW to avoid glitches */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF); ++} ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++static void ++pcie_phy_36mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE ++static void ++pcie_phy_36mhz_ssc_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ ++ /* PLL Setting */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF); ++ ++ /* Increase the bias reference voltage */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF); ++ ++ /* Endcnt */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF); ++ ++ /* Force */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008); ++ ++ /* Predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF); ++ ++ /* ctrl_lim */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF); ++ ++ /* ctrl */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00); ++ ++ /* RTERM*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF); ++ ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0400, 0x0400); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0100); ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1c72, 0xFFFF); ++ ++ /* improved 100MHz clock output */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF); ++ ++ /* reduced CDR BW to avoid glitches */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_25MHZ_MODE ++static void ++pcie_phy_25mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0200); ++ ++ /* en_ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0002, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0040, 0x0070); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x6000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x4000, 0x4000); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_25MHZ_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_100MHZ_MODE ++static void ++pcie_phy_100mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_100MHZ_MODE */ ++ ++static int ++pcie_phy_wait_startup_ready(int pcie_port) ++{ ++ int i; ++ ++ for (i = 0; i < IFX_PCIE_PLL_TIMEOUT; i++) { ++ if ((IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_STATUS(pcie_port)) & 0x0040) != 0) { ++ break; ++ } ++ udelay(10); ++ } ++ if (i >= IFX_PCIE_PLL_TIMEOUT) { ++ printk(KERN_ERR "%s PLL Link timeout\n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static void ++pcie_phy_load_enable(int pcie_port, int slice) ++{ ++ /* Set the load_en of tx/rx slice to '1' */ ++ switch (slice) { ++ case 1: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0010, 0x0010); ++ break; ++ case 2: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0010, 0x0010); ++ break; ++ case 3: ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0002, 0x0002); ++ break; ++ } ++} ++ ++static void ++pcie_phy_load_disable(int pcie_port, int slice) ++{ ++ /* set the load_en of tx/rx slice to '0' */ ++ switch (slice) { ++ case 1: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0000, 0x0010); ++ break; ++ case 2: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0000, 0x0010); ++ break; ++ case 3: ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0000, 0x0002); ++ break; ++ } ++} ++ ++static void ++pcie_phy_load_war(int pcie_port) ++{ ++ int slice; ++ ++ for (slice = 1; slice < 4; slice++) { ++ pcie_phy_load_enable(pcie_port, slice); ++ udelay(1); ++ pcie_phy_load_disable(pcie_port, slice); ++ } ++} ++ ++static void ++pcie_phy_tx2_modulation(int pcie_port) ++{ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD1(pcie_port), 0x1FFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD2(pcie_port), 0xFFFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0601, 0xFFFF); ++ mdelay(1); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0001, 0xFFFF); ++} ++ ++static void ++pcie_phy_tx1_modulation(int pcie_port) ++{ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD1(pcie_port), 0x1FFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD2(pcie_port), 0xFFFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0601, 0xFFFF); ++ mdelay(1); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0001, 0xFFFF); ++} ++ ++static void ++pcie_phy_tx_modulation_war(int pcie_port) ++{ ++ int i; ++ ++#define PCIE_PHY_MODULATION_NUM 5 ++ for (i = 0; i < PCIE_PHY_MODULATION_NUM; i++) { ++ pcie_phy_tx2_modulation(pcie_port); ++ pcie_phy_tx1_modulation(pcie_port); ++ } ++#undef PCIE_PHY_MODULATION_NUM ++} ++ ++void ++pcie_phy_clock_mode_setup(int pcie_port) ++{ ++ pcie_pdi_big_endian(pcie_port); ++ ++ /* Enable PDI to access PCIe PHY register */ ++ pcie_pdi_pmu_enable(pcie_port); ++ ++ /* Configure PLL and PHY clock */ ++ pcie_phy_comm_setup(pcie_port); ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++ pcie_phy_36mhz_mode_setup(pcie_port); ++#elif defined(CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE) ++ pcie_phy_36mhz_ssc_mode_setup(pcie_port); ++#elif defined(CONFIG_IFX_PCIE_PHY_25MHZ_MODE) ++ pcie_phy_25mhz_mode_setup(pcie_port); ++#elif defined (CONFIG_IFX_PCIE_PHY_100MHZ_MODE) ++ pcie_phy_100mhz_mode_setup(pcie_port); ++#else ++ #error "PCIE PHY Clock Mode must be chosen first!!!!" ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */ ++ ++ /* Enable PCIe PHY and make PLL setting take effect */ ++ pcie_phy_pmu_enable(pcie_port); ++ ++ /* Check if we are in startup_ready status */ ++ pcie_phy_wait_startup_ready(pcie_port); ++ ++ pcie_phy_load_war(pcie_port); ++ ++ /* Apply TX modulation workarounds */ ++ pcie_phy_tx_modulation_war(pcie_port); ++ ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Modified PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++} ++ +diff --git a/arch/mips/pci/ifxmips_pcie_pm.c b/arch/mips/pci/ifxmips_pcie_pm.c +new file mode 100644 +index 0000000..a10ecad +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_pm.c +@@ -0,0 +1,176 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_pm.c ++** PROJECT : IFX UEIP ++** MODULES : PCIE Root Complex Driver ++** ++** DATE : 21 Dec 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIE Root Complex Driver Power Managment ++** COPYRIGHT : Copyright (c) 2009 ++** Lantiq Deutschland GmbH ++** Am Campeon 3, 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 ++** $Date $Author $Comment ++** 21 Dec,2009 Lei Chuanhua First UEIP release ++*******************************************************************************/ ++/*! ++ \defgroup IFX_PCIE_PM Power Management functions ++ \ingroup IFX_PCIE ++ \brief IFX PCIE Root Complex Driver power management functions ++*/ ++ ++/*! ++ \file ifxmips_pcie_pm.c ++ \ingroup IFX_PCIE ++ \brief source file for PCIE Root Complex Driver Power Management ++*/ ++ ++#ifndef EXPORT_SYMTAB ++#define EXPORT_SYMTAB ++#endif ++#ifndef AUTOCONF_INCLUDED ++#include ++#endif /* AUTOCONF_INCLUDED */ ++#include ++#include ++#include ++#include ++#include ++ ++/* Project header */ ++#include ++#include ++#include ++#include ++#include "ifxmips_pcie_pm.h" ++ ++/** ++ * \fn static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) ++ * \brief the callback function to request pmcu state in the power management hardware-dependent module ++ * ++ * \param pmcuState This parameter is a PMCU state. ++ * ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \return IFX_PMCU_RETURN_DENIED Not allowed to operate power state ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) ++{ ++ switch(pmcuState) ++ { ++ case IFX_PMCU_STATE_D0: ++ return IFX_PMCU_RETURN_SUCCESS; ++ case IFX_PMCU_STATE_D1: // Not Applicable ++ return IFX_PMCU_RETURN_DENIED; ++ case IFX_PMCU_STATE_D2: // Not Applicable ++ return IFX_PMCU_RETURN_DENIED; ++ case IFX_PMCU_STATE_D3: // Module clock gating and Power gating ++ return IFX_PMCU_RETURN_SUCCESS; ++ default: ++ return IFX_PMCU_RETURN_DENIED; ++ } ++} ++ ++/** ++ * \fn static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) ++ * \brief the callback function to get pmcu state in the power management hardware-dependent module ++ ++ * \param pmcuState Pointer to return power state. ++ * ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \return IFX_PMCU_RETURN_DENIED Not allowed to operate power state ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) ++{ ++ return IFX_PMCU_RETURN_SUCCESS; ++} ++ ++/** ++ * \fn IFX_PMCU_RETURN_t ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++ * \brief Apply all callbacks registered to be executed before a state change for pmcuModule ++ * ++ * \param pmcuModule Module ++ * \param newState New state ++ * \param oldState Old state ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++{ ++ return IFX_PMCU_RETURN_SUCCESS; ++} ++ ++/** ++ * \fn IFX_PMCU_RETURN_t ifx_pcie_pmcu_postchange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++ * \brief Apply all callbacks registered to be executed before a state change for pmcuModule ++ * ++ * \param pmcuModule Module ++ * \param newState New state ++ * \param oldState Old state ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_postchange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++{ ++ return IFX_PMCU_RETURN_SUCCESS; ++} ++ ++/** ++ * \fn static void ifx_pcie_pmcu_init(void) ++ * \brief Register with central PMCU module ++ * \return none ++ * \ingroup IFX_PCIE_PM ++ */ ++void ++ifx_pcie_pmcu_init(void) ++{ ++ IFX_PMCU_REGISTER_t pmcuRegister; ++ ++ /* XXX, hook driver context */ ++ ++ /* State function register */ ++ memset(&pmcuRegister, 0, sizeof(IFX_PMCU_REGISTER_t)); ++ pmcuRegister.pmcuModule = IFX_PMCU_MODULE_PCIE; ++ pmcuRegister.pmcuModuleNr = 0; ++ pmcuRegister.ifx_pmcu_state_change = ifx_pcie_pmcu_state_change; ++ pmcuRegister.ifx_pmcu_state_get = ifx_pcie_pmcu_state_get; ++ pmcuRegister.pre = ifx_pcie_pmcu_prechange; ++ pmcuRegister.post= ifx_pcie_pmcu_postchange; ++ ifx_pmcu_register(&pmcuRegister); ++} ++ ++/** ++ * \fn static void ifx_pcie_pmcu_exit(void) ++ * \brief Unregister with central PMCU module ++ * ++ * \return none ++ * \ingroup IFX_PCIE_PM ++ */ ++void ++ifx_pcie_pmcu_exit(void) ++{ ++ IFX_PMCU_REGISTER_t pmcuUnRegister; ++ ++ /* XXX, hook driver context */ ++ ++ pmcuUnRegister.pmcuModule = IFX_PMCU_MODULE_PCIE; ++ pmcuUnRegister.pmcuModuleNr = 0; ++ ifx_pmcu_unregister(&pmcuUnRegister); ++} ++ +diff --git a/arch/mips/pci/ifxmips_pcie_pm.h b/arch/mips/pci/ifxmips_pcie_pm.h +new file mode 100644 +index 0000000..6ece20d +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_pm.h +@@ -0,0 +1,36 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_pm.h ++** PROJECT : IFX UEIP ++** MODULES : PCIe Root Complex Driver ++** ++** DATE : 21 Dec 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver Power Managment ++** COPYRIGHT : Copyright (c) 2009 ++** Lantiq Deutschland GmbH ++** Am Campeon 3, 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 ++** $Date $Author $Comment ++** 21 Dec,2009 Lei Chuanhua First UEIP release ++*******************************************************************************/ ++/*! ++ \file ifxmips_pcie_pm.h ++ \ingroup IFX_PCIE ++ \brief header file for PCIe Root Complex Driver Power Management ++*/ ++ ++#ifndef IFXMIPS_PCIE_PM_H ++#define IFXMIPS_PCIE_PM_H ++ ++void ifx_pcie_pmcu_init(void); ++void ifx_pcie_pmcu_exit(void); ++ ++#endif /* IFXMIPS_PCIE_PM_H */ ++ +diff --git a/arch/mips/pci/ifxmips_pcie_reg.h b/arch/mips/pci/ifxmips_pcie_reg.h +new file mode 100644 +index 0000000..e7e4b6c +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_reg.h +@@ -0,0 +1,1001 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_reg.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_REG_H ++#define IFXMIPS_PCIE_REG_H ++/*! ++ \file ifxmips_pcie_reg.h ++ \ingroup IFX_PCIE ++ \brief header file for PCIe module register definition ++*/ ++/* PCIe Address Mapping Base */ ++#define PCIE_CFG_PHY_BASE 0x1D000000UL ++#define PCIE_CFG_BASE (KSEG1 + PCIE_CFG_PHY_BASE) ++#define PCIE_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE_MEM_PHY_BASE 0x1C000000UL ++#define PCIE_MEM_BASE (KSEG1 + PCIE_MEM_PHY_BASE) ++#define PCIE_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE_MEM_PHY_END (PCIE_MEM_PHY_BASE + PCIE_MEM_SIZE - 1) ++ ++#define PCIE_IO_PHY_BASE 0x1D800000UL ++#define PCIE_IO_BASE (KSEG1 + PCIE_IO_PHY_BASE) ++#define PCIE_IO_SIZE (1 * 1024 * 1024) ++#define PCIE_IO_PHY_END (PCIE_IO_PHY_BASE + PCIE_IO_SIZE - 1) ++ ++#define PCIE_RC_CFG_BASE (KSEG1 + 0x1D900000) ++#define PCIE_APP_LOGIC_REG (KSEG1 + 0x1E100900) ++#define PCIE_MSI_PHY_BASE 0x1F600000UL ++ ++#define PCIE_PDI_PHY_BASE 0x1F106800UL ++#define PCIE_PDI_BASE (KSEG1 + PCIE_PDI_PHY_BASE) ++#define PCIE_PDI_SIZE 0x400 ++ ++#define PCIE1_CFG_PHY_BASE 0x19000000UL ++#define PCIE1_CFG_BASE (KSEG1 + PCIE1_CFG_PHY_BASE) ++#define PCIE1_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE1_MEM_PHY_BASE 0x18000000UL ++#define PCIE1_MEM_BASE (KSEG1 + PCIE1_MEM_PHY_BASE) ++#define PCIE1_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE1_MEM_PHY_END (PCIE1_MEM_PHY_BASE + PCIE1_MEM_SIZE - 1) ++ ++#define PCIE1_IO_PHY_BASE 0x19800000UL ++#define PCIE1_IO_BASE (KSEG1 + PCIE1_IO_PHY_BASE) ++#define PCIE1_IO_SIZE (1 * 1024 * 1024) ++#define PCIE1_IO_PHY_END (PCIE1_IO_PHY_BASE + PCIE1_IO_SIZE - 1) ++ ++#define PCIE1_RC_CFG_BASE (KSEG1 + 0x19900000) ++#define PCIE1_APP_LOGIC_REG (KSEG1 + 0x1E100700) ++#define PCIE1_MSI_PHY_BASE 0x1F400000UL ++ ++#define PCIE1_PDI_PHY_BASE 0x1F700400UL ++#define PCIE1_PDI_BASE (KSEG1 + PCIE1_PDI_PHY_BASE) ++#define PCIE1_PDI_SIZE 0x400 ++ ++#define PCIE_CFG_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_CFG_BASE) : (PCIE_CFG_BASE)) ++#define PCIE_MEM_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_BASE) : (PCIE_MEM_BASE)) ++#define PCIE_IO_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_BASE) : (PCIE_IO_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_PHY_BASE) : (PCIE_MEM_PHY_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_MEM_PHY_END) : (PCIE_MEM_PHY_END)) ++#define PCIE_IO_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_PHY_BASE) : (PCIE_IO_PHY_BASE)) ++#define PCIE_IO_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_IO_PHY_END) : (PCIE_IO_PHY_END)) ++#define PCIE_APP_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_APP_LOGIC_REG) : (PCIE_APP_LOGIC_REG)) ++#define PCIE_RC_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_RC_CFG_BASE) : (PCIE_RC_CFG_BASE)) ++#define PCIE_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_PDI_BASE) : (PCIE_PDI_BASE)) ++ ++/* PCIe Application Logic Register */ ++/* RC Core Control Register */ ++#define PCIE_RC_CCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x10) ++/* This should be enabled after initializing configuratin registers ++ * Also should check link status retraining bit ++ */ ++#define PCIE_RC_CCR_LTSSM_ENABLE 0x00000001 /* Enable LTSSM to continue link establishment */ ++ ++/* RC Core Debug Register */ ++#define PCIE_RC_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x14) ++#define PCIE_RC_DR_DLL_UP 0x00000001 /* Data Link Layer Up */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE 0x0000000E /* Current Power State */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE_S 1 ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE 0x000001F0 /* Current LTSSM State */ ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE_S 4 ++ ++#define PCIE_RC_DR_PM_DEV_STATE 0x00000E00 /* Power Management D-State */ ++#define PCIE_RC_DR_PM_DEV_STATE_S 9 ++ ++#define PCIE_RC_DR_PM_ENABLED 0x00001000 /* Power Management State from PMU */ ++#define PCIE_RC_DR_PME_EVENT_ENABLED 0x00002000 /* Power Management Event Enable State */ ++#define PCIE_RC_DR_AUX_POWER_ENABLED 0x00004000 /* Auxiliary Power Enable */ ++ ++/* Current Power State Definition */ ++enum { ++ PCIE_RC_DR_D0 = 0, ++ PCIE_RC_DR_D1, /* Not supported */ ++ PCIE_RC_DR_D2, /* Not supported */ ++ PCIE_RC_DR_D3, ++ PCIE_RC_DR_UN, ++}; ++ ++/* PHY Link Status Register */ ++#define PCIE_PHY_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x18) ++#define PCIE_PHY_SR_PHY_LINK_UP 0x00000001 /* PHY Link Up/Down Indicator */ ++ ++/* Electromechanical Control Register */ ++#define PCIE_EM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x1C) ++#define PCIE_EM_CR_CARD_IS_PRESENT 0x00000001 /* Card Presence Detect State */ ++#define PCIE_EM_CR_MRL_OPEN 0x00000002 /* MRL Sensor State */ ++#define PCIE_EM_CR_POWER_FAULT_SET 0x00000004 /* Power Fault Detected */ ++#define PCIE_EM_CR_MRL_SENSOR_SET 0x00000008 /* MRL Sensor Changed */ ++#define PCIE_EM_CR_PRESENT_DETECT_SET 0x00000010 /* Card Presense Detect Changed */ ++#define PCIE_EM_CR_CMD_CPL_INT_SET 0x00000020 /* Command Complete Interrupt */ ++#define PCIE_EM_CR_SYS_INTERLOCK_SET 0x00000040 /* System Electromechanical IterLock Engaged */ ++#define PCIE_EM_CR_ATTENTION_BUTTON_SET 0x00000080 /* Attention Button Pressed */ ++ ++/* Interrupt Status Register */ ++#define PCIE_IR_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x20) ++#define PCIE_IR_SR_PME_CAUSE_MSI 0x00000002 /* MSI caused by PME */ ++#define PCIE_IR_SR_HP_PME_WAKE_GEN 0x00000004 /* Hotplug PME Wake Generation */ ++#define PCIE_IR_SR_HP_MSI 0x00000008 /* Hotplug MSI */ ++#define PCIE_IR_SR_AHB_LU_ERR 0x00000030 /* AHB Bridge Lookup Error Signals */ ++#define PCIE_IR_SR_AHB_LU_ERR_S 4 ++#define PCIE_IR_SR_INT_MSG_NUM 0x00003E00 /* Interrupt Message Number */ ++#define PCIE_IR_SR_INT_MSG_NUM_S 9 ++#define PCIE_IR_SR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_IR_SR_AER_INT_MSG_NUM_S 27 ++ ++/* Message Control Register */ ++#define PCIE_MSG_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x30) ++#define PCIE_MSG_CR_GEN_PME_TURN_OFF_MSG 0x00000001 /* Generate PME Turn Off Message */ ++#define PCIE_MSG_CR_GEN_UNLOCK_MSG 0x00000002 /* Generate Unlock Message */ ++ ++#define PCIE_VDM_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x34) ++ ++/* Vendor-Defined Message Requester ID Register */ ++#define PCIE_VDM_RID(X) (PCIE_APP_PORT_TO_BASE (X) + 0x38) ++#define PCIE_VDM_RID_VENROR_MSG_REQ_ID 0x0000FFFF ++#define PCIE_VDM_RID_VDMRID_S 0 ++ ++/* ASPM Control Register */ ++#define PCIE_ASPM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x40) ++#define PCIE_ASPM_CR_HOT_RST 0x00000001 /* Hot Reset Request to the downstream device */ ++#define PCIE_ASPM_CR_REQ_EXIT_L1 0x00000002 /* Request to Exit L1 */ ++#define PCIE_ASPM_CR_REQ_ENTER_L1 0x00000004 /* Request to Enter L1 */ ++ ++/* Vendor Message DW0 Register */ ++#define PCIE_VM_MSG_DW0(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x50) ++#define PCIE_VM_MSG_DW0_TYPE 0x0000001F /* Message type */ ++#define PCIE_VM_MSG_DW0_TYPE_S 0 ++#define PCIE_VM_MSG_DW0_FORMAT 0x00000060 /* Format */ ++#define PCIE_VM_MSG_DW0_FORMAT_S 5 ++#define PCIE_VM_MSG_DW0_TC 0x00007000 /* Traffic Class */ ++#define PCIE_VM_MSG_DW0_TC_S 12 ++#define PCIE_VM_MSG_DW0_ATTR 0x000C0000 /* Atrributes */ ++#define PCIE_VM_MSG_DW0_ATTR_S 18 ++#define PCIE_VM_MSG_DW0_EP_TLP 0x00100000 /* Poisoned TLP */ ++#define PCIE_VM_MSG_DW0_TD 0x00200000 /* TLP Digest */ ++#define PCIE_VM_MSG_DW0_LEN 0xFFC00000 /* Length */ ++#define PCIE_VM_MSG_DW0_LEN_S 22 ++ ++/* Format Definition */ ++enum { ++ PCIE_VM_MSG_FORMAT_00 = 0, /* 3DW Hdr, no data*/ ++ PCIE_VM_MSG_FORMAT_01, /* 4DW Hdr, no data */ ++ PCIE_VM_MSG_FORMAT_10, /* 3DW Hdr, with data */ ++ PCIE_VM_MSG_FORMAT_11, /* 4DW Hdr, with data */ ++}; ++ ++/* Traffic Class Definition */ ++enum { ++ PCIE_VM_MSG_TC0 = 0, ++ PCIE_VM_MSG_TC1, ++ PCIE_VM_MSG_TC2, ++ PCIE_VM_MSG_TC3, ++ PCIE_VM_MSG_TC4, ++ PCIE_VM_MSG_TC5, ++ PCIE_VM_MSG_TC6, ++ PCIE_VM_MSG_TC7, ++}; ++ ++/* Attributes Definition */ ++enum { ++ PCIE_VM_MSG_ATTR_00 = 0, /* RO and No Snoop cleared */ ++ PCIE_VM_MSG_ATTR_01, /* RO cleared , No Snoop set */ ++ PCIE_VM_MSG_ATTR_10, /* RO set, No Snoop cleared*/ ++ PCIE_VM_MSG_ATTR_11, /* RO and No Snoop set */ ++}; ++ ++/* Payload Size Definition */ ++#define PCIE_VM_MSG_LEN_MIN 0 ++#define PCIE_VM_MSG_LEN_MAX 1024 ++ ++/* Vendor Message DW1 Register */ ++#define PCIE_VM_MSG_DW1(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x54) ++#define PCIE_VM_MSG_DW1_FUNC_NUM 0x00000070 /* Function Number */ ++#define PCIE_VM_MSG_DW1_FUNC_NUM_S 8 ++#define PCIE_VM_MSG_DW1_CODE 0x00FF0000 /* Message Code */ ++#define PCIE_VM_MSG_DW1_CODE_S 16 ++#define PCIE_VM_MSG_DW1_TAG 0xFF000000 /* Tag */ ++#define PCIE_VM_MSG_DW1_TAG_S 24 ++ ++#define PCIE_VM_MSG_DW2(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x58) ++#define PCIE_VM_MSG_DW3(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x5C) ++ ++/* Vendor Message Request Register */ ++#define PCIE_VM_MSG_REQR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x60) ++#define PCIE_VM_MSG_REQR_REQ 0x00000001 /* Vendor Message Request */ ++ ++ ++/* AHB Slave Side Band Control Register */ ++#define PCIE_AHB_SSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x70) ++#define PCIE_AHB_SSB_REQ_BCM 0x00000001 /* Slave Reques BCM filed */ ++#define PCIE_AHB_SSB_REQ_EP 0x00000002 /* Slave Reques EP filed */ ++#define PCIE_AHB_SSB_REQ_TD 0x00000004 /* Slave Reques TD filed */ ++#define PCIE_AHB_SSB_REQ_ATTR 0x00000018 /* Slave Reques Attribute number */ ++#define PCIE_AHB_SSB_REQ_ATTR_S 3 ++#define PCIE_AHB_SSB_REQ_TC 0x000000E0 /* Slave Request TC Field */ ++#define PCIE_AHB_SSB_REQ_TC_S 5 ++ ++/* AHB Master SideBand Ctrl Register */ ++#define PCIE_AHB_MSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x74) ++#define PCIE_AHB_MSB_RESP_ATTR 0x00000003 /* Master Response Attribute number */ ++#define PCIE_AHB_MSB_RESP_ATTR_S 0 ++#define PCIE_AHB_MSB_RESP_BAD_EOT 0x00000004 /* Master Response Badeot filed */ ++#define PCIE_AHB_MSB_RESP_BCM 0x00000008 /* Master Response BCM filed */ ++#define PCIE_AHB_MSB_RESP_EP 0x00000010 /* Master Response EP filed */ ++#define PCIE_AHB_MSB_RESP_TD 0x00000020 /* Master Response TD filed */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM 0x000003C0 /* Master Response Function number */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM_S 6 ++ ++/* AHB Control Register, fixed bus enumeration exception */ ++#define PCIE_AHB_CTRL(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x78) ++#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 ++ ++/* Interrupt Enalbe Register */ ++#define PCIE_IRNEN(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF4) ++#define PCIE_IRNCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF8) ++#define PCIE_IRNICR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xFC) ++ ++/* PCIe interrupt enable/control/capture register definition */ ++#define PCIE_IRN_AER_REPORT 0x00000001 /* AER Interrupt */ ++#define PCIE_IRN_AER_MSIX 0x00000002 /* Advanced Error MSI-X Interrupt */ ++#define PCIE_IRN_PME 0x00000004 /* PME Interrupt */ ++#define PCIE_IRN_HOTPLUG 0x00000008 /* Hotplug Interrupt */ ++#define PCIE_IRN_RX_VDM_MSG 0x00000010 /* Vendor-Defined Message Interrupt */ ++#define PCIE_IRN_RX_CORRECTABLE_ERR_MSG 0x00000020 /* Correctable Error Message Interrupt */ ++#define PCIE_IRN_RX_NON_FATAL_ERR_MSG 0x00000040 /* Non-fatal Error Message */ ++#define PCIE_IRN_RX_FATAL_ERR_MSG 0x00000080 /* Fatal Error Message */ ++#define PCIE_IRN_RX_PME_MSG 0x00000100 /* PME Message Interrupt */ ++#define PCIE_IRN_RX_PME_TURNOFF_ACK 0x00000200 /* PME Turnoff Ack Message Interrupt */ ++#define PCIE_IRN_AHB_BR_FATAL_ERR 0x00000400 /* AHB Fatal Error Interrupt */ ++#define PCIE_IRN_LINK_AUTO_BW_STATUS 0x00000800 /* Link Auto Bandwidth Status Interrupt */ ++#define PCIE_IRN_BW_MGT 0x00001000 /* Bandwidth Managment Interrupt */ ++#define PCIE_IRN_INTA 0x00002000 /* INTA */ ++#define PCIE_IRN_INTB 0x00004000 /* INTB */ ++#define PCIE_IRN_INTC 0x00008000 /* INTC */ ++#define PCIE_IRN_INTD 0x00010000 /* INTD */ ++#define PCIE_IRN_WAKEUP 0x00020000 /* Wake up Interrupt */ ++ ++#define PCIE_RC_CORE_COMBINED_INT (PCIE_IRN_AER_REPORT | PCIE_IRN_AER_MSIX | PCIE_IRN_PME | \ ++ PCIE_IRN_HOTPLUG | PCIE_IRN_RX_VDM_MSG | PCIE_IRN_RX_CORRECTABLE_ERR_MSG |\ ++ PCIE_IRN_RX_NON_FATAL_ERR_MSG | PCIE_IRN_RX_FATAL_ERR_MSG | \ ++ PCIE_IRN_RX_PME_MSG | PCIE_IRN_RX_PME_TURNOFF_ACK | PCIE_IRN_AHB_BR_FATAL_ERR | \ ++ PCIE_IRN_LINK_AUTO_BW_STATUS | PCIE_IRN_BW_MGT) ++/* PCIe RC Configuration Register */ ++#define PCIE_VDID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x00) ++ ++/* Bit definition from pci_reg.h */ ++#define PCIE_PCICMDSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x04) ++#define PCIE_CCRID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x08) ++#define PCIE_CLSLTHTBR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x0C) /* EP only */ ++/* BAR0, BAR1,Only necessary if the bridges implements a device-specific register set or memory buffer */ ++#define PCIE_BAR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10) /* Not used*/ ++#define PCIE_BAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14) /* Not used */ ++ ++#define PCIE_BNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x18) /* Mandatory */ ++/* Bus Number Register bits */ ++#define PCIE_BNR_PRIMARY_BUS_NUM 0x000000FF ++#define PCIE_BNR_PRIMARY_BUS_NUM_S 0 ++#define PCIE_PNR_SECONDARY_BUS_NUM 0x0000FF00 ++#define PCIE_PNR_SECONDARY_BUS_NUM_S 8 ++#define PCIE_PNR_SUB_BUS_NUM 0x00FF0000 ++#define PCIE_PNR_SUB_BUS_NUM_S 16 ++ ++/* IO Base/Limit Register bits */ ++#define PCIE_IOBLSECS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x1C) /* RC only */ ++#define PCIE_IOBLSECS_32BIT_IO_ADDR 0x00000001 ++#define PCIE_IOBLSECS_IO_BASE_ADDR 0x000000F0 ++#define PCIE_IOBLSECS_IO_BASE_ADDR_S 4 ++#define PCIE_IOBLSECS_32BIT_IOLIMT 0x00000100 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR 0x0000F000 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR_S 12 ++ ++/* Non-prefetchable Memory Base/Limit Register bit */ ++#define PCIE_MBML(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x20) /* RC only */ ++#define PCIE_MBML_MEM_BASE_ADDR 0x0000FFF0 ++#define PCIE_MBML_MEM_BASE_ADDR_S 4 ++#define PCIE_MBML_MEM_LIMIT_ADDR 0xFFF00000 ++#define PCIE_MBML_MEM_LIMIT_ADDR_S 20 ++ ++/* Prefetchable Memory Base/Limit Register bit */ ++#define PCIE_PMBL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x24) /* RC only */ ++#define PCIE_PMBL_64BIT_ADDR 0x00000001 ++#define PCIE_PMBL_UPPER_12BIT 0x0000FFF0 ++#define PCIE_PMBL_UPPER_12BIT_S 4 ++#define PCIE_PMBL_E64MA 0x00010000 ++#define PCIE_PMBL_END_ADDR 0xFFF00000 ++#define PCIE_PMBL_END_ADDR_S 20 ++#define PCIE_PMBU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x28) /* RC only */ ++#define PCIE_PMLU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x2C) /* RC only */ ++ ++/* I/O Base/Limit Upper 16 bits register */ ++#define PCIE_IO_BANDL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x30) /* RC only */ ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE 0x0000FFFF ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE_S 0 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT 0xFFFF0000 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT_S 16 ++ ++#define PCIE_CPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x34) ++#define PCIE_EBBAR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x38) ++ ++/* Interrupt and Secondary Bridge Control Register */ ++#define PCIE_INTRBCTRL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x3C) ++ ++#define PCIE_INTRBCTRL_INT_LINE 0x000000FF ++#define PCIE_INTRBCTRL_INT_LINE_S 0 ++#define PCIE_INTRBCTRL_INT_PIN 0x0000FF00 ++#define PCIE_INTRBCTRL_INT_PIN_S 8 ++#define PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE 0x00010000 /* #PERR */ ++#define PCIE_INTRBCTRL_SERR_ENABLE 0x00020000 /* #SERR */ ++#define PCIE_INTRBCTRL_ISA_ENABLE 0x00040000 /* ISA enable, IO 64KB only */ ++#define PCIE_INTRBCTRL_VGA_ENABLE 0x00080000 /* VGA enable */ ++#define PCIE_INTRBCTRL_VGA_16BIT_DECODE 0x00100000 /* VGA 16bit decode */ ++#define PCIE_INTRBCTRL_RST_SECONDARY_BUS 0x00400000 /* Secondary bus rest, hot rest, 1ms */ ++/* Others are read only */ ++enum { ++ PCIE_INTRBCTRL_INT_NON = 0, ++ PCIE_INTRBCTRL_INTA, ++ PCIE_INTRBCTRL_INTB, ++ PCIE_INTRBCTRL_INTC, ++ PCIE_INTRBCTRL_INTD, ++}; ++ ++#define PCIE_PM_CAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x40) ++ ++/* Power Management Control and Status Register */ ++#define PCIE_PM_CSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x44) ++ ++#define PCIE_PM_CSR_POWER_STATE 0x00000003 /* Power State */ ++#define PCIE_PM_CSR_POWER_STATE_S 0 ++#define PCIE_PM_CSR_SW_RST 0x00000008 /* Soft Reset Enabled */ ++#define PCIE_PM_CSR_PME_ENABLE 0x00000100 /* PME Enable */ ++#define PCIE_PM_CSR_PME_STATUS 0x00008000 /* PME status */ ++ ++/* MSI Capability Register for EP */ ++#define PCIE_MCAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x50) ++ ++#define PCIE_MCAPR_MSI_CAP_ID 0x000000FF /* MSI Capability ID */ ++#define PCIE_MCAPR_MSI_CAP_ID_S 0 ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR_S 8 ++#define PCIE_MCAPR_MSI_ENABLE 0x00010000 /* MSI Enable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP 0x000E0000 /* Multiple Message Capable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP_S 17 ++#define PCIE_MCAPR_MULTI_MSG_ENABLE 0x00700000 /* Multiple Message Enable */ ++#define PCIE_MCAPR_MULTI_MSG_ENABLE_S 20 ++#define PCIE_MCAPR_ADDR64_CAP 0X00800000 /* 64-bit Address Capable */ ++ ++/* MSI Message Address Register */ ++#define PCIE_MA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x54) ++ ++#define PCIE_MA_ADDR_MASK 0xFFFFFFFC /* Message Address */ ++ ++/* MSI Message Upper Address Register */ ++#define PCIE_MUA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x58) ++ ++/* MSI Message Data Register */ ++#define PCIE_MD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x5C) ++ ++#define PCIE_MD_DATA 0x0000FFFF /* Message Data */ ++#define PCIE_MD_DATA_S 0 ++ ++/* PCI Express Capability Register */ ++#define PCIE_XCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70) ++ ++#define PCIE_XCAP_ID 0x000000FF /* PCI Express Capability ID */ ++#define PCIE_XCAP_ID_S 0 ++#define PCIE_XCAP_NEXT_CAP 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_XCAP_NEXT_CAP_S 8 ++#define PCIE_XCAP_VER 0x000F0000 /* PCI Express Capability Version */ ++#define PCIE_XCAP_VER_S 16 ++#define PCIE_XCAP_DEV_PORT_TYPE 0x00F00000 /* Device Port Type */ ++#define PCIE_XCAP_DEV_PORT_TYPE_S 20 ++#define PCIE_XCAP_SLOT_IMPLEMENTED 0x01000000 /* Slot Implemented */ ++#define PCIE_XCAP_MSG_INT_NUM 0x3E000000 /* Interrupt Message Number */ ++#define PCIE_XCAP_MSG_INT_NUM_S 25 ++ ++/* Device Capability Register */ ++#define PCIE_DCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74) ++ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE 0x00000007 /* Max Payload size */ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE_S 0 ++#define PCIE_DCAP_PHANTOM_FUNC 0x00000018 /* Phanton Function, not supported */ ++#define PCIE_DCAP_PHANTOM_FUNC_S 3 ++#define PCIE_DCAP_EXT_TAG 0x00000020 /* Extended Tag Field */ ++#define PCIE_DCAP_EP_L0S_LATENCY 0x000001C0 /* EP L0s latency only */ ++#define PCIE_DCAP_EP_L0S_LATENCY_S 6 ++#define PCIE_DCAP_EP_L1_LATENCY 0x00000E00 /* EP L1 latency only */ ++#define PCIE_DCAP_EP_L1_LATENCY_S 9 ++#define PCIE_DCAP_ROLE_BASE_ERR_REPORT 0x00008000 /* Role Based ERR */ ++ ++/* Maximum payload size supported */ ++enum { ++ PCIE_MAX_PAYLOAD_128 = 0, ++ PCIE_MAX_PAYLOAD_256, ++ PCIE_MAX_PAYLOAD_512, ++ PCIE_MAX_PAYLOAD_1024, ++ PCIE_MAX_PAYLOAD_2048, ++ PCIE_MAX_PAYLOAD_4096, ++}; ++ ++/* Device Control and Status Register */ ++#define PCIE_DCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x78) ++ ++#define PCIE_DCTLSTS_CORRECTABLE_ERR_EN 0x00000001 /* COR-ERR */ ++#define PCIE_DCTLSTS_NONFATAL_ERR_EN 0x00000002 /* Non-fatal ERR */ ++#define PCIE_DCTLSTS_FATAL_ERR_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_DCTLSYS_UR_REQ_EN 0x00000008 /* UR ERR */ ++#define PCIE_DCTLSTS_RELAXED_ORDERING_EN 0x00000010 /* Enable relaxing ordering */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE 0x000000E0 /* Max payload mask */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE_S 5 ++#define PCIE_DCTLSTS_EXT_TAG_EN 0x00000100 /* Extended tag field */ ++#define PCIE_DCTLSTS_PHANTOM_FUNC_EN 0x00000200 /* Phantom Function Enable */ ++#define PCIE_DCTLSTS_AUX_PM_EN 0x00000400 /* AUX Power PM Enable */ ++#define PCIE_DCTLSTS_NO_SNOOP_EN 0x00000800 /* Enable no snoop, except root port*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE 0x00007000 /* Max Read Request size*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE_S 12 ++#define PCIE_DCTLSTS_CORRECTABLE_ERR 0x00010000 /* COR-ERR Detected */ ++#define PCIE_DCTLSTS_NONFATAL_ERR 0x00020000 /* Non-Fatal ERR Detected */ ++#define PCIE_DCTLSTS_FATAL_ER 0x00040000 /* Fatal ERR Detected */ ++#define PCIE_DCTLSTS_UNSUPPORTED_REQ 0x00080000 /* UR Detected */ ++#define PCIE_DCTLSTS_AUX_POWER 0x00100000 /* Aux Power Detected */ ++#define PCIE_DCTLSTS_TRANSACT_PENDING 0x00200000 /* Transaction pending */ ++ ++#define PCIE_DCTLSTS_ERR_EN (PCIE_DCTLSTS_CORRECTABLE_ERR_EN | \ ++ PCIE_DCTLSTS_NONFATAL_ERR_EN | PCIE_DCTLSTS_FATAL_ERR_EN | \ ++ PCIE_DCTLSYS_UR_REQ_EN) ++ ++/* Link Capability Register */ ++#define PCIE_LCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7C) ++#define PCIE_LCAP_MAX_LINK_SPEED 0x0000000F /* Max link speed, 0x1 by default */ ++#define PCIE_LCAP_MAX_LINK_SPEED_S 0 ++#define PCIE_LCAP_MAX_LENGTH_WIDTH 0x000003F0 /* Maxium Length Width */ ++#define PCIE_LCAP_MAX_LENGTH_WIDTH_S 4 ++#define PCIE_LCAP_ASPM_LEVEL 0x00000C00 /* Active State Link PM Support */ ++#define PCIE_LCAP_ASPM_LEVEL_S 10 ++#define PCIE_LCAP_L0S_EIXT_LATENCY 0x00007000 /* L0s Exit Latency */ ++#define PCIE_LCAP_L0S_EIXT_LATENCY_S 12 ++#define PCIE_LCAP_L1_EXIT_LATENCY 0x00038000 /* L1 Exit Latency */ ++#define PCIE_LCAP_L1_EXIT_LATENCY_S 15 ++#define PCIE_LCAP_CLK_PM 0x00040000 /* Clock Power Management */ ++#define PCIE_LCAP_SDER 0x00080000 /* Surprise Down Error Reporting */ ++#define PCIE_LCAP_DLL_ACTIVE_REPROT 0x00100000 /* Data Link Layer Active Reporting Capable */ ++#define PCIE_LCAP_PORT_NUM 0xFF0000000 /* Port number */ ++#define PCIE_LCAP_PORT_NUM_S 24 ++ ++/* Maximum Length width definition */ ++#define PCIE_MAX_LENGTH_WIDTH_RES 0x00 ++#define PCIE_MAX_LENGTH_WIDTH_X1 0x01 /* Default */ ++#define PCIE_MAX_LENGTH_WIDTH_X2 0x02 ++#define PCIE_MAX_LENGTH_WIDTH_X4 0x04 ++#define PCIE_MAX_LENGTH_WIDTH_X8 0x08 ++#define PCIE_MAX_LENGTH_WIDTH_X12 0x0C ++#define PCIE_MAX_LENGTH_WIDTH_X16 0x10 ++#define PCIE_MAX_LENGTH_WIDTH_X32 0x20 ++ ++/* Active State Link PM definition */ ++enum { ++ PCIE_ASPM_RES0 = 0, ++ PCIE_ASPM_L0S_ENTRY_SUPPORT, /* L0s */ ++ PCIE_ASPM_RES1, ++ PCIE_ASPM_L0S_L1_ENTRY_SUPPORT, /* L0s and L1, default */ ++}; ++ ++/* L0s Exit Latency definition */ ++enum { ++ PCIE_L0S_EIXT_LATENCY_L64NS = 0, /* < 64 ns */ ++ PCIE_L0S_EIXT_LATENCY_B64A128, /* > 64 ns < 128 ns */ ++ PCIE_L0S_EIXT_LATENCY_B128A256, /* > 128 ns < 256 ns */ ++ PCIE_L0S_EIXT_LATENCY_B256A512, /* > 256 ns < 512 ns */ ++ PCIE_L0S_EIXT_LATENCY_B512TO1U, /* > 512 ns < 1 us */ ++ PCIE_L0S_EIXT_LATENCY_B1A2U, /* > 1 us < 2 us */ ++ PCIE_L0S_EIXT_LATENCY_B2A4U, /* > 2 us < 4 us */ ++ PCIE_L0S_EIXT_LATENCY_M4US, /* > 4 us */ ++}; ++ ++/* L1 Exit Latency definition */ ++enum { ++ PCIE_L1_EXIT_LATENCY_L1US = 0, /* < 1 us */ ++ PCIE_L1_EXIT_LATENCY_B1A2, /* > 1 us < 2 us */ ++ PCIE_L1_EXIT_LATENCY_B2A4, /* > 2 us < 4 us */ ++ PCIE_L1_EXIT_LATENCY_B4A8, /* > 4 us < 8 us */ ++ PCIE_L1_EXIT_LATENCY_B8A16, /* > 8 us < 16 us */ ++ PCIE_L1_EXIT_LATENCY_B16A32, /* > 16 us < 32 us */ ++ PCIE_L1_EXIT_LATENCY_B32A64, /* > 32 us < 64 us */ ++ PCIE_L1_EXIT_LATENCY_M64US, /* > 64 us */ ++}; ++ ++/* Link Control and Status Register */ ++#define PCIE_LCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x80) ++#define PCIE_LCTLSTS_ASPM_ENABLE 0x00000003 /* Active State Link PM Control */ ++#define PCIE_LCTLSTS_ASPM_ENABLE_S 0 ++#define PCIE_LCTLSTS_RCB128 0x00000008 /* Read Completion Boundary 128*/ ++#define PCIE_LCTLSTS_LINK_DISABLE 0x00000010 /* Link Disable */ ++#define PCIE_LCTLSTS_RETRIAN_LINK 0x00000020 /* Retrain Link */ ++#define PCIE_LCTLSTS_COM_CLK_CFG 0x00000040 /* Common Clock Configuration */ ++#define PCIE_LCTLSTS_EXT_SYNC 0x00000080 /* Extended Synch */ ++#define PCIE_LCTLSTS_CLK_PM_EN 0x00000100 /* Enable Clock Powerm Management */ ++#define PCIE_LCTLSTS_LINK_SPEED 0x000F0000 /* Link Speed */ ++#define PCIE_LCTLSTS_LINK_SPEED_S 16 ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH 0x03F00000 /* Negotiated Link Width */ ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH_S 20 ++#define PCIE_LCTLSTS_RETRAIN_PENDING 0x08000000 /* Link training is ongoing */ ++#define PCIE_LCTLSTS_SLOT_CLK_CFG 0x10000000 /* Slot Clock Configuration */ ++#define PCIE_LCTLSTS_DLL_ACTIVE 0x20000000 /* Data Link Layer Active */ ++ ++/* Slot Capabilities Register */ ++#define PCIE_SLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x84) ++ ++/* Slot Capabilities */ ++#define PCIE_SLCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x88) ++ ++/* Root Control and Capability Register */ ++#define PCIE_RCTLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x8C) ++#define PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR 0x00000001 /* #SERR on COR-ERR */ ++#define PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR 0x00000002 /* #SERR on Non-Fatal ERR */ ++#define PCIE_RCTLCAP_SERR_ON_FATAL_ERR 0x00000004 /* #SERR on Fatal ERR */ ++#define PCIE_RCTLCAP_PME_INT_EN 0x00000008 /* PME Interrupt Enable */ ++#define PCIE_RCTLCAP_SERR_ENABLE (PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR | \ ++ PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR | PCIE_RCTLCAP_SERR_ON_FATAL_ERR) ++/* Root Status Register */ ++#define PCIE_RSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x90) ++#define PCIE_RSTS_PME_REQ_ID 0x0000FFFF /* PME Request ID */ ++#define PCIE_RSTS_PME_REQ_ID_S 0 ++#define PCIE_RSTS_PME_STATUS 0x00010000 /* PME Status */ ++#define PCIE_RSTS_PME_PENDING 0x00020000 /* PME Pending */ ++ ++/* PCI Express Enhanced Capability Header */ ++#define PCIE_ENHANCED_CAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x100) ++#define PCIE_ENHANCED_CAP_ID 0x0000FFFF /* PCI Express Extended Capability ID */ ++#define PCIE_ENHANCED_CAP_ID_S 0 ++#define PCIE_ENHANCED_CAP_VER 0x000F0000 /* Capability Version */ ++#define PCIE_ENHANCED_CAP_VER_S 16 ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET 0xFFF00000 /* Next Capability Offset */ ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET_S 20 ++ ++/* Uncorrectable Error Status Register */ ++#define PCIE_UES_R(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x104) ++#define PCIE_DATA_LINK_PROTOCOL_ERR 0x00000010 /* Data Link Protocol Error Status */ ++#define PCIE_SURPRISE_DOWN_ERROR 0x00000020 /* Surprise Down Error Status */ ++#define PCIE_POISONED_TLP 0x00001000 /* Poisoned TLP Status */ ++#define PCIE_FC_PROTOCOL_ERR 0x00002000 /* Flow Control Protocol Error Status */ ++#define PCIE_COMPLETION_TIMEOUT 0x00004000 /* Completion Timeout Status */ ++#define PCIE_COMPLETOR_ABORT 0x00008000 /* Completer Abort Error */ ++#define PCIE_UNEXPECTED_COMPLETION 0x00010000 /* Unexpected Completion Status */ ++#define PCIE_RECEIVER_OVERFLOW 0x00020000 /* Receive Overflow Status */ ++#define PCIE_MALFORNED_TLP 0x00040000 /* Malformed TLP Stauts */ ++#define PCIE_ECRC_ERR 0x00080000 /* ECRC Error Stauts */ ++#define PCIE_UR_REQ 0x00100000 /* Unsupported Request Error Status */ ++#define PCIE_ALL_UNCORRECTABLE_ERR (PCIE_DATA_LINK_PROTOCOL_ERR | PCIE_SURPRISE_DOWN_ERROR | \ ++ PCIE_POISONED_TLP | PCIE_FC_PROTOCOL_ERR | PCIE_COMPLETION_TIMEOUT | \ ++ PCIE_COMPLETOR_ABORT | PCIE_UNEXPECTED_COMPLETION | PCIE_RECEIVER_OVERFLOW |\ ++ PCIE_MALFORNED_TLP | PCIE_ECRC_ERR | PCIE_UR_REQ) ++ ++/* Uncorrectable Error Mask Register, Mask means no report */ ++#define PCIE_UEMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x108) ++ ++/* Uncorrectable Error Severity Register */ ++#define PCIE_UESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10C) ++ ++/* Correctable Error Status Register */ ++#define PCIE_CESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x110) ++#define PCIE_RX_ERR 0x00000001 /* Receive Error Status */ ++#define PCIE_BAD_TLP 0x00000040 /* Bad TLP Status */ ++#define PCIE_BAD_DLLP 0x00000080 /* Bad DLLP Status */ ++#define PCIE_REPLAY_NUM_ROLLOVER 0x00000100 /* Replay Number Rollover Status */ ++#define PCIE_REPLAY_TIMER_TIMEOUT_ERR 0x00001000 /* Reply Timer Timeout Status */ ++#define PCIE_ADVISORY_NONFTAL_ERR 0x00002000 /* Advisory Non-Fatal Error Status */ ++#define PCIE_CORRECTABLE_ERR (PCIE_RX_ERR | PCIE_BAD_TLP | PCIE_BAD_DLLP | PCIE_REPLAY_NUM_ROLLOVER |\ ++ PCIE_REPLAY_TIMER_TIMEOUT_ERR | PCIE_ADVISORY_NONFTAL_ERR) ++ ++/* Correctable Error Mask Register */ ++#define PCIE_CEMR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x114) ++ ++/* Advanced Error Capabilities and Control Register */ ++#define PCIE_AECCR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x118) ++#define PCIE_AECCR_FIRST_ERR_PTR 0x0000001F /* First Error Pointer */ ++#define PCIE_AECCR_FIRST_ERR_PTR_S 0 ++#define PCIE_AECCR_ECRC_GEN_CAP 0x00000020 /* ECRC Generation Capable */ ++#define PCIE_AECCR_ECRC_GEN_EN 0x00000040 /* ECRC Generation Enable */ ++#define PCIE_AECCR_ECRC_CHECK_CAP 0x00000080 /* ECRC Check Capable */ ++#define PCIE_AECCR_ECRC_CHECK_EN 0x00000100 /* ECRC Check Enable */ ++ ++/* Header Log Register 1 */ ++#define PCIE_HLR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x11C) ++ ++/* Header Log Register 2 */ ++#define PCIE_HLR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x120) ++ ++/* Header Log Register 3 */ ++#define PCIE_HLR3(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x124) ++ ++/* Header Log Register 4 */ ++#define PCIE_HLR4(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x128) ++ ++/* Root Error Command Register */ ++#define PCIE_RECR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x12C) ++#define PCIE_RECR_CORRECTABLE_ERR_REPORT_EN 0x00000001 /* COR-ERR */ ++#define PCIE_RECR_NONFATAL_ERR_REPORT_EN 0x00000002 /* Non-Fatal ERR */ ++#define PCIE_RECR_FATAL_ERR_REPORT_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_RECR_ERR_REPORT_EN (PCIE_RECR_CORRECTABLE_ERR_REPORT_EN | \ ++ PCIE_RECR_NONFATAL_ERR_REPORT_EN | PCIE_RECR_FATAL_ERR_REPORT_EN) ++ ++/* Root Error Status Register */ ++#define PCIE_RESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x130) ++#define PCIE_RESR_CORRECTABLE_ERR 0x00000001 /* COR-ERR Receveid */ ++#define PCIE_RESR_MULTI_CORRECTABLE_ERR 0x00000002 /* Multiple COR-ERR Received */ ++#define PCIE_RESR_FATAL_NOFATAL_ERR 0x00000004 /* ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_MULTI_FATAL_NOFATAL_ERR 0x00000008 /* Multiple ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_FIRST_UNCORRECTABLE_FATAL_ERR 0x00000010 /* First UN-COR Fatal */ ++#define PCIR_RESR_NON_FATAL_ERR 0x00000020 /* Non-Fatal Error Message Received */ ++#define PCIE_RESR_FATAL_ERR 0x00000040 /* Fatal Message Received */ ++#define PCIE_RESR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_RESR_AER_INT_MSG_NUM_S 27 ++ ++/* Error Source Indentification Register */ ++#define PCIE_ESIR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x134) ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID 0x0000FFFF ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID_S 0 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID 0xFFFF0000 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID_S 16 ++ ++/* VC Enhanced Capability Header */ ++#define PCIE_VC_ECH(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x140) ++ ++/* Port VC Capability Register */ ++#define PCIE_PVC1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x144) ++#define PCIE_PVC1_EXT_VC_CNT 0x00000007 /* Extended VC Count */ ++#define PCIE_PVC1_EXT_VC_CNT_S 0 ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT 0x00000070 /* Low Priority Extended VC Count */ ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT_S 4 ++#define PCIE_PVC1_REF_CLK 0x00000300 /* Reference Clock */ ++#define PCIE_PVC1_REF_CLK_S 8 ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE 0x00000C00 /* Port Arbitration Table Entry Size */ ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE_S 10 ++ ++/* Extended Virtual Channel Count Defintion */ ++#define PCIE_EXT_VC_CNT_MIN 0 ++#define PCIE_EXT_VC_CNT_MAX 7 ++ ++/* Port Arbitration Table Entry Size Definition */ ++enum { ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S1BIT = 0, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S2BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S4BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S8BIT, ++}; ++ ++/* Port VC Capability Register 2 */ ++#define PCIE_PVC2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x148) ++#define PCIE_PVC2_VC_ARB_16P_FIXED_WRR 0x00000001 /* HW Fixed arbitration, 16 phase WRR */ ++#define PCIE_PVC2_VC_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_PVC2_VC_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_PVC2_VC_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_PVC2_VC_ARB_WRR 0x0000000F ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET 0xFF000000 /* VC arbitration table offset, not support */ ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET_S 24 ++ ++/* Port VC Control and Status Register */ ++#define PCIE_PVCCRSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14C) ++#define PCIE_PVCCRSR_LOAD_VC_ARB_TAB 0x00000001 /* Load VC Arbitration Table */ ++#define PCIE_PVCCRSR_VC_ARB_SEL 0x0000000E /* VC Arbitration Select */ ++#define PCIE_PVCCRSR_VC_ARB_SEL_S 1 ++#define PCIE_PVCCRSR_VC_ARB_TAB_STATUS 0x00010000 /* Arbitration Status */ ++ ++/* VC0 Resource Capability Register */ ++#define PCIE_VC0_RC(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x150) ++#define PCIE_VC0_RC_PORT_ARB_HW_FIXED 0x00000001 /* HW Fixed arbitration */ ++#define PCIE_VC0_RC_PORT_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_128P_WRR 0x00000010 /* Time-based 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_256P_WRR 0x00000020 /* Time-based 256 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB (PCIE_VC0_RC_PORT_ARB_HW_FIXED | PCIE_VC0_RC_PORT_ARB_32P_WRR |\ ++ PCIE_VC0_RC_PORT_ARB_64P_WRR | PCIE_VC0_RC_PORT_ARB_128P_WRR | \ ++ PCIE_VC0_RC_PORT_ARB_TM_128P_WRR | PCIE_VC0_RC_PORT_ARB_TM_256P_WRR) ++ ++#define PCIE_VC0_RC_REJECT_SNOOP 0x00008000 /* Reject Snoop Transactioin */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS 0x007F0000 /* Maximum time Slots */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS_S 16 ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET 0xFF000000 /* Port Arbitration Table Offset */ ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET_S 24 ++ ++/* VC0 Resource Control Register */ ++#define PCIE_VC0_RC0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x154) ++#define PCIE_VC0_RC0_TVM0 0x00000001 /* TC0 and VC0 */ ++#define PCIE_VC0_RC0_TVM1 0x00000002 /* TC1 and VC1 */ ++#define PCIE_VC0_RC0_TVM2 0x00000004 /* TC2 and VC2 */ ++#define PCIE_VC0_RC0_TVM3 0x00000008 /* TC3 and VC3 */ ++#define PCIE_VC0_RC0_TVM4 0x00000010 /* TC4 and VC4 */ ++#define PCIE_VC0_RC0_TVM5 0x00000020 /* TC5 and VC5 */ ++#define PCIE_VC0_RC0_TVM6 0x00000040 /* TC6 and VC6 */ ++#define PCIE_VC0_RC0_TVM7 0x00000080 /* TC7 and VC7 */ ++#define PCIE_VC0_RC0_TC_VC 0x000000FF /* TC/VC mask */ ++ ++#define PCIE_VC0_RC0_LOAD_PORT_ARB_TAB 0x00010000 /* Load Port Arbitration Table */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL 0x000E0000 /* Port Arbitration Select */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL_S 17 ++#define PCIE_VC0_RC0_VC_ID 0x07000000 /* VC ID */ ++#define PCIE_VC0_RC0_VC_ID_S 24 ++#define PCIE_VC0_RC0_VC_EN 0x80000000 /* VC Enable */ ++ ++/* VC0 Resource Status Register */ ++#define PCIE_VC0_RSR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x158) ++#define PCIE_VC0_RSR0_PORT_ARB_TAB_STATUS 0x00010000 /* Port Arbitration Table Status,not used */ ++#define PCIE_VC0_RSR0_VC_NEG_PENDING 0x00020000 /* VC Negotiation Pending */ ++ ++/* Ack Latency Timer and Replay Timer Register */ ++#define PCIE_ALTRT(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x700) ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT 0x0000FFFF /* Round Trip Latency Time Limit */ ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT_S 0 ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT 0xFFFF0000 /* Replay Time Limit */ ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT_S 16 ++ ++/* Other Message Register */ ++#define PCIE_OMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x704) ++ ++/* Port Force Link Register */ ++#define PCIE_PFLR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x708) ++#define PCIE_PFLR_LINK_NUM 0x000000FF /* Link Number */ ++#define PCIE_PFLR_LINK_NUM_S 0 ++#define PCIE_PFLR_FORCE_LINK 0x00008000 /* Force link */ ++#define PCIE_PFLR_LINK_STATE 0x003F0000 /* Link State */ ++#define PCIE_PFLR_LINK_STATE_S 16 ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT 0xFF000000 /* Low Power Entrance Count, only for EP */ ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT_S 24 ++ ++/* Ack Frequency Register */ ++#define PCIE_AFR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70C) ++#define PCIE_AFR_AF 0x000000FF /* Ack Frequency */ ++#define PCIE_AFR_AF_S 0 ++#define PCIE_AFR_FTS_NUM 0x0000FF00 /* The number of Fast Training Sequence from L0S to L0 */ ++#define PCIE_AFR_FTS_NUM_S 8 ++#define PCIE_AFR_COM_FTS_NUM 0x00FF0000 /* N_FTS; when common clock is used*/ ++#define PCIE_AFR_COM_FTS_NUM_S 16 ++#define PCIE_AFR_L0S_ENTRY_LATENCY 0x07000000 /* L0s Entrance Latency */ ++#define PCIE_AFR_L0S_ENTRY_LATENCY_S 24 ++#define PCIE_AFR_L1_ENTRY_LATENCY 0x38000000 /* L1 Entrance Latency */ ++#define PCIE_AFR_L1_ENTRY_LATENCY_S 27 ++#define PCIE_AFR_FTS_NUM_DEFAULT 32 ++#define PCIE_AFR_L0S_ENTRY_LATENCY_DEFAULT 7 ++#define PCIE_AFR_L1_ENTRY_LATENCY_DEFAULT 5 ++ ++/* Port Link Control Register */ ++#define PCIE_PLCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x710) ++#define PCIE_PLCR_OTHER_MSG_REQ 0x00000001 /* Other Message Request */ ++#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ ++#define PCIE_PLCR_LOOPBACK_EN 0x00000004 /* Loopback Enable */ ++#define PCIE_PLCR_LTSSM_HOT_RST 0x00000008 /* Force LTSSM to the hot reset */ ++#define PCIE_PLCR_DLL_LINK_EN 0x00000020 /* Enable Link initialization */ ++#define PCIE_PLCR_FAST_LINK_SIM_EN 0x00000080 /* Sets all internal timers to fast mode for simulation purposes */ ++#define PCIE_PLCR_LINK_MODE 0x003F0000 /* Link Mode Enable Mask */ ++#define PCIE_PLCR_LINK_MODE_S 16 ++#define PCIE_PLCR_CORRUPTED_CRC_EN 0x02000000 /* Enabled Corrupt CRC */ ++ ++/* Lane Skew Register */ ++#define PCIE_LSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x714) ++#define PCIE_LSR_LANE_SKEW_NUM 0x00FFFFFF /* Insert Lane Skew for Transmit, not applicable */ ++#define PCIE_LSR_LANE_SKEW_NUM_S 0 ++#define PCIE_LSR_FC_DISABLE 0x01000000 /* Disable of Flow Control */ ++#define PCIE_LSR_ACKNAK_DISABLE 0x02000000 /* Disable of Ack/Nak */ ++#define PCIE_LSR_LANE_DESKEW_DISABLE 0x80000000 /* Disable of Lane-to-Lane Skew */ ++ ++/* Symbol Number Register */ ++#define PCIE_SNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x718) ++#define PCIE_SNR_TS 0x0000000F /* Number of TS Symbol */ ++#define PCIE_SNR_TS_S 0 ++#define PCIE_SNR_SKP 0x00000700 /* Number of SKP Symbol */ ++#define PCIE_SNR_SKP_S 8 ++#define PCIE_SNR_REPLAY_TIMER 0x0007C000 /* Timer Modifier for Replay Timer */ ++#define PCIE_SNR_REPLAY_TIMER_S 14 ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER 0x00F80000 /* Timer Modifier for Ack/Nak Latency Timer */ ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER_S 19 ++#define PCIE_SNR_FC_TIMER 0x1F000000 /* Timer Modifier for Flow Control Watchdog Timer */ ++#define PCIE_SNR_FC_TIMER_S 28 ++ ++/* Symbol Timer Register and Filter Mask Register 1 */ ++#define PCIE_STRFMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x71C) ++#define PCIE_STRFMR_SKP_INTERVAL 0x000007FF /* SKP lnterval Value */ ++#define PCIE_STRFMR_SKP_INTERVAL_S 0 ++#define PCIE_STRFMR_FC_WDT_DISABLE 0x00008000 /* Disable of FC Watchdog Timer */ ++#define PCIE_STRFMR_TLP_FUNC_MISMATCH_OK 0x00010000 /* Mask Function Mismatch Filtering for Incoming Requests */ ++#define PCIE_STRFMR_POISONED_TLP_OK 0x00020000 /* Mask Poisoned TLP Filtering */ ++#define PCIE_STRFMR_BAR_MATCH_OK 0x00040000 /* Mask BAR Match Filtering */ ++#define PCIE_STRFMR_TYPE1_CFG_REQ_OK 0x00080000 /* Mask Type 1 Configuration Request Filtering */ ++#define PCIE_STRFMR_LOCKED_REQ_OK 0x00100000 /* Mask Locked Request Filtering */ ++#define PCIE_STRFMR_CPL_TAG_ERR_RULES_OK 0x00200000 /* Mask Tag Error Rules for Received Completions */ ++#define PCIE_STRFMR_CPL_REQUESTOR_ID_MISMATCH_OK 0x00400000 /* Mask Requester ID Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_FUNC_MISMATCH_OK 0x00800000 /* Mask Function Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_TC_MISMATCH_OK 0x01000000 /* Mask Traffic Class Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_ATTR_MISMATCH_OK 0x02000000 /* Mask Attribute Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_LENGTH_MISMATCH_OK 0x04000000 /* Mask Length Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_TLP_ECRC_ERR_OK 0x08000000 /* Mask ECRC Error Filtering */ ++#define PCIE_STRFMR_CPL_TLP_ECRC_OK 0x10000000 /* Mask ECRC Error Filtering for Completions */ ++#define PCIE_STRFMR_RX_TLP_MSG_NO_DROP 0x20000000 /* Send Message TLPs */ ++#define PCIE_STRFMR_RX_IO_TRANS_ENABLE 0x40000000 /* Mask Filtering of received I/O Requests */ ++#define PCIE_STRFMR_RX_CFG_TRANS_ENABLE 0x80000000 /* Mask Filtering of Received Configuration Requests */ ++ ++#define PCIE_DEF_SKP_INTERVAL 700 /* 1180 ~1538 , 125MHz * 2, 250MHz * 1 */ ++ ++/* Filter Masker Register 2 */ ++#define PCIE_FMR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x720) ++#define PCIE_FMR2_VENDOR_MSG0_PASSED_TO_TRGT1 0x00000001 /* Mask RADM Filtering and Error Handling Rules */ ++#define PCIE_FMR2_VENDOR_MSG1_PASSED_TO_TRGT1 0x00000002 /* Mask RADM Filtering and Error Handling Rules */ ++ ++/* Debug Register 0 */ ++#define PCIE_DBR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x728) ++ ++/* Debug Register 1 */ ++#define PCIE_DBR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x72C) ++ ++/* Transmit Posted FC Credit Status Register */ ++#define PCIE_TPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x730) ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS 0x00000FFF /* Transmit Posted Data FC Credits */ ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS_S 0 ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS 0x000FF000 /* Transmit Posted Header FC Credits */ ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Non-Posted FC Credit Status */ ++#define PCIE_TNPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x734) ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS 0x00000FFF /* Transmit Non-Posted Data FC Credits */ ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS_S 0 ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS 0x000FF000 /* Transmit Non-Posted Header FC Credits */ ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Complete FC Credit Status Register */ ++#define PCIE_TCFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x738) ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS 0x00000FFF /* Transmit Completion Data FC Credits */ ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS_S 0 ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS 0x000FF000 /* Transmit Completion Header FC Credits */ ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS_S 12 ++ ++/* Queue Status Register */ ++#define PCIE_QSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x73C) ++#define PCIE_QSR_WAIT_UPDATE_FC_DLL 0x00000001 /* Received TLP FC Credits Not Returned */ ++#define PCIE_QSR_TX_RETRY_BUF_NOT_EMPTY 0x00000002 /* Transmit Retry Buffer Not Empty */ ++#define PCIE_QSR_RX_QUEUE_NOT_EMPTY 0x00000004 /* Received Queue Not Empty */ ++ ++/* VC Transmit Arbitration Register 1 */ ++#define PCIE_VCTAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x740) ++#define PCIE_VCTAR1_WRR_WEIGHT_VC0 0x000000FF /* WRR Weight for VC0 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC1 0x0000FF00 /* WRR Weight for VC1 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC2 0x00FF0000 /* WRR Weight for VC2 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC3 0xFF000000 /* WRR Weight for VC3 */ ++ ++/* VC Transmit Arbitration Register 2 */ ++#define PCIE_VCTAR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x744) ++#define PCIE_VCTAR2_WRR_WEIGHT_VC4 0x000000FF /* WRR Weight for VC4 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC5 0x0000FF00 /* WRR Weight for VC5 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC6 0x00FF0000 /* WRR Weight for VC6 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC7 0xFF000000 /* WRR Weight for VC7 */ ++ ++/* VC0 Posted Receive Queue Control Register */ ++#define PCIE_VC0_PRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x748) ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS 0x00000FFF /* VC0 Posted Data Credits */ ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS_S 0 ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS 0x000FF000 /* VC0 Posted Header Credits */ ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS_S 12 ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE 0x00E00000 /* VC0 Posted TLP Queue Mode */ ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE_S 20 ++#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ ++#define PCIE_VC0_PRQCR_VC_STRICT_ORDER 0x80000000 /* VC0 Ordering for Receive Queues */ ++ ++/* VC0 Non-Posted Receive Queue Control */ ++#define PCIE_VC0_NPRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74C) ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS 0x00000FFF /* VC0 Non-Posted Data Credits */ ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS_S 0 ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS 0x000FF000 /* VC0 Non-Posted Header Credits */ ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS_S 12 ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE 0x00E00000 /* VC0 Non-Posted TLP Queue Mode */ ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE_S 20 ++ ++/* VC0 Completion Receive Queue Control */ ++#define PCIE_VC0_CRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x750) ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS 0x00000FFF /* VC0 Completion TLP Queue Mode */ ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS_S 0 ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS 0x000FF000 /* VC0 Completion Header Credits */ ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS_S 12 ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE 0x00E00000 /* VC0 Completion Data Credits */ ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE_S 21 ++ ++/* Applicable to the above three registers */ ++enum { ++ PCIE_VC0_TLP_QUEUE_MODE_STORE_FORWARD = 1, ++ PCIE_VC0_TLP_QUEUE_MODE_CUT_THROUGH = 2, ++ PCIE_VC0_TLP_QUEUE_MODE_BYPASS = 4, ++}; ++ ++/* VC0 Posted Buffer Depth Register */ ++#define PCIE_VC0_PBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7A8) ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Posted Data Queue Depth */ ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Posted Header Queue Depth */ ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Non-Posted Buffer Depth Register */ ++#define PCIE_VC0_NPBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7AC) ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Non-Posted Data Queue Depth */ ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Non-Posted Header Queue Depth */ ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Completion Buffer Depth Register */ ++#define PCIE_VC0_CBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7B0) ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES 0x00003FFF /* C0 Completion Data Queue Depth */ ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Completion Header Queue Depth */ ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES_S 16 ++ ++/* PHY Status Register, all zeros in VR9 */ ++#define PCIE_PHYSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x810) ++ ++/* PHY Control Register, all zeros in VR9 */ ++#define PCIE_PHYCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x814) ++ ++/* ++ * PCIe PDI PHY register definition, suppose all the following ++ * stuff is confidential. ++ * XXX, detailed bit definition ++ */ ++#define PCIE_PHY_PLL_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x22 << 1)) ++#define PCIE_PHY_PLL_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x23 << 1)) ++#define PCIE_PHY_PLL_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x24 << 1)) ++#define PCIE_PHY_PLL_CTRL4(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x25 << 1)) ++#define PCIE_PHY_PLL_CTRL5(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x26 << 1)) ++#define PCIE_PHY_PLL_CTRL6(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x27 << 1)) ++#define PCIE_PHY_PLL_CTRL7(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x28 << 1)) ++#define PCIE_PHY_PLL_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x29 << 1)) ++#define PCIE_PHY_PLL_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2A << 1)) ++#define PCIE_PHY_PLL_A_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2B << 1)) ++#define PCIE_PHY_PLL_STATUS(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2C << 1)) ++ ++#define PCIE_PHY_TX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x30 << 1)) ++#define PCIE_PHY_TX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x31 << 1)) ++#define PCIE_PHY_TX1_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x32 << 1)) ++#define PCIE_PHY_TX1_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x33 << 1)) ++#define PCIE_PHY_TX1_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x34 << 1)) ++#define PCIE_PHY_TX1_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x35 << 1)) ++#define PCIE_PHY_TX1_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x36 << 1)) ++#define PCIE_PHY_TX1_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x37 << 1)) ++ ++#define PCIE_PHY_TX2_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x38 << 1)) ++#define PCIE_PHY_TX2_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x39 << 1)) ++#define PCIE_PHY_TX2_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3B << 1)) ++#define PCIE_PHY_TX2_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3C << 1)) ++#define PCIE_PHY_TX2_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3D << 1)) ++#define PCIE_PHY_TX2_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3E << 1)) ++#define PCIE_PHY_TX2_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3F << 1)) ++ ++#define PCIE_PHY_RX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x50 << 1)) ++#define PCIE_PHY_RX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x51 << 1)) ++#define PCIE_PHY_RX1_CDR(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x52 << 1)) ++#define PCIE_PHY_RX1_EI(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x53 << 1)) ++#define PCIE_PHY_RX1_A_CTRL(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x55 << 1)) ++ ++/* Interrupt related stuff */ ++#define PCIE_LEGACY_DISABLE 0 ++#define PCIE_LEGACY_INTA 1 ++#define PCIE_LEGACY_INTB 2 ++#define PCIE_LEGACY_INTC 3 ++#define PCIE_LEGACY_INTD 4 ++#define PCIE_LEGACY_INT_MAX PCIE_LEGACY_INTD ++ ++#endif /* IFXMIPS_PCIE_REG_H */ ++ +diff --git a/arch/mips/pci/ifxmips_pcie_vr9.h b/arch/mips/pci/ifxmips_pcie_vr9.h +new file mode 100644 +index 0000000..57d9368 +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_vr9.h +@@ -0,0 +1,271 @@ ++/**************************************************************************** ++ 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_vr9.h ++ \ingroup IFX_PCIE ++ \brief PCIe RC driver vr9 specific file ++*/ ++ ++#ifndef IFXMIPS_PCIE_VR9_H ++#define IFXMIPS_PCIE_VR9_H ++ ++#include ++#include ++ ++#include ++#include ++ ++#define IFX_PCIE_GPIO_RESET 238 ++ ++#define IFX_REG_R32 ltq_r32 ++#define IFX_REG_W32 ltq_w32 ++#define CONFIG_IFX_PCIE_HW_SWAP ++#define IFX_RCU_AHB_ENDIAN ((volatile u32*)(IFX_RCU + 0x004C)) ++#define IFX_RCU_RST_REQ ((volatile u32*)(IFX_RCU + 0x0010)) ++#define IFX_RCU_AHB_BE_PCIE_PDI 0x00000080 /* Configure PCIE PDI module in big endian*/ ++ ++#define IFX_RCU (KSEG1 | 0x1F203000) ++#define IFX_RCU_AHB_BE_PCIE_M 0x00000001 /* Configure AHB master port that connects to PCIe RC in big endian */ ++#define IFX_RCU_AHB_BE_PCIE_S 0x00000010 /* Configure AHB slave port that connects to PCIe RC in little endian */ ++#define IFX_RCU_AHB_BE_XBAR_M 0x00000002 /* Configure AHB master port that connects to XBAR in big endian */ ++#define CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++ ++#define IFX_PMU1_MODULE_PCIE_PHY (0) ++#define IFX_PMU1_MODULE_PCIE_CTRL (1) ++#define IFX_PMU1_MODULE_PDI (4) ++#define IFX_PMU1_MODULE_MSI (5) ++ ++#define IFX_PMU_MODULE_PCIE_L0_CLK (31) ++ ++ ++#define IFX_GPIO (KSEG1 | 0x1E100B00) ++#define ALT0 ((volatile u32*)(IFX_GPIO + 0x007c)) ++#define ALT1 ((volatile u32*)(IFX_GPIO + 0x0080)) ++#define OD ((volatile u32*)(IFX_GPIO + 0x0084)) ++#define DIR ((volatile u32*)(IFX_GPIO + 0x0078)) ++#define OUT ((volatile u32*)(IFX_GPIO + 0x0070)) ++ ++ ++static inline void pcie_ep_gpio_rst_init(int pcie_port) ++{ ++ ++ gpio_request(IFX_PCIE_GPIO_RESET, "pcie-reset"); ++ gpio_direction_output(IFX_PCIE_GPIO_RESET, 1); ++ gpio_set_value(IFX_PCIE_GPIO_RESET, 1); ++ ++/* ifx_gpio_pin_reserve(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_output_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_dir_out_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_altsel0_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_altsel1_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_open_drain_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id);*/ ++} ++ ++static inline void pcie_ahb_pmu_setup(void) ++{ ++ /* Enable AHB bus master/slave */ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "ahb"); ++ clk_enable(clk); ++ ++ //AHBM_PMU_SETUP(IFX_PMU_ENABLE); ++ //AHBS_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_rcu_endian_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg |= IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#else ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg &= ~IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ 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) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "phy"); ++ clk_enable(clk); ++ ++ //PCIE_PHY_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_phy_pmu_disable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "phy"); ++ clk_disable(clk); ++ ++// PCIE_PHY_PMU_SETUP(IFX_PMU_DISABLE); ++} ++ ++static inline void pcie_pdi_big_endian(int pcie_port) ++{ ++ u32 reg; ++ ++ /* SRAM2PDI endianness control. */ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++ /* Config AHB->PCIe and PDI endianness */ ++ reg |= IFX_RCU_AHB_BE_PCIE_PDI; ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++} ++ ++static inline void pcie_pdi_pmu_enable(int pcie_port) ++{ ++ /* Enable PDI to access PCIe PHY register */ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "pdi"); ++ clk_enable(clk); ++ //PDI_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_core_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ ++ /* Reset PCIe PHY & Core, bit 22, bit 26 may be affected if write it directly */ ++ reg |= 0x00400000; ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_core_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ /* Reset PCIe PHY & Core, bit 22 */ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg &= ~0x00400000; ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg |= 0x00001000; /* Bit 12 */ ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg &= ~0x00001000; /* Bit 12 */ ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_device_rst_assert(int pcie_port) ++{ ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ gpio_set_value(IFX_PCIE_GPIO_RESET, 0); ++// ifx_gpio_output_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++} ++ ++static inline void pcie_device_rst_deassert(int pcie_port) ++{ ++ mdelay(100); ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ gpio_direction_output(IFX_PCIE_GPIO_RESET, 1); ++// gpio_set_value(IFX_PCIE_GPIO_RESET, 1); ++ //ifx_gpio_output_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++} ++ ++static inline void pcie_core_pmu_setup(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "ctl"); ++ clk_enable(clk); ++ clk = clk_get_sys("1d900000.pcie", "bus"); ++ clk_enable(clk); ++ ++ /* PCIe Core controller enabled */ ++// PCIE_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ ++ /* Enable PCIe L0 Clock */ ++// PCIE_L0_CLK_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_msi_init(int pcie_port) ++{ ++ struct clk *clk; ++ pcie_msi_pic_init(pcie_port); ++ clk = clk_get_sys("ltq_pcie", "msi"); ++ clk_enable(clk); ++// MSI_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline u32 ++ifx_pcie_bus_nr_deduct(u32 bus_number, int pcie_port) ++{ ++ u32 tbus_number = bus_number; ++ ++#ifdef CONFIG_IFX_PCI ++ if (pcibios_host_nr() > 1) { ++ tbus_number -= pcibios_1st_host_bus_nr(); ++ } ++#endif /* CONFIG_IFX_PCI */ ++ return tbus_number; ++} ++ ++static inline u32 ++ifx_pcie_bus_enum_hack(struct pci_bus *bus, u32 devfn, int where, u32 value, int pcie_port, int read) ++{ ++ struct pci_dev *pdev; ++ u32 tvalue = value; ++ ++ /* Sanity check */ ++ pdev = pci_get_slot(bus, devfn); ++ if (pdev == NULL) { ++ return tvalue; ++ } ++ ++ /* Only care about PCI bridge */ ++ if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { ++ return tvalue; ++ } ++ ++ if (read) { /* Read hack */ ++ #ifdef CONFIG_IFX_PCI ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_read_hack(where, tvalue); ++ } ++ #endif /* CONFIG_IFX_PCI */ ++ } ++ else { /* Write hack */ ++ #ifdef CONFIG_IFX_PCI ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_write_hack(where, tvalue); ++ } ++ #endif ++ } ++ return tvalue; ++} ++ ++#endif /* IFXMIPS_PCIE_VR9_H */ ++ +diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c +index 04e35bc..2e85529 100644 +--- a/arch/mips/pci/pci.c ++++ b/arch/mips/pci/pci.c +@@ -250,6 +250,31 @@ static int __init pcibios_init(void) + + subsys_initcall(pcibios_init); + ++int pcibios_host_nr(void) ++{ ++ int count; ++ struct pci_controller *hose; ++ for (count = 0, hose = hose_head; hose; hose = hose->next, count++) { ++ ; ++ } ++ return count; ++} ++EXPORT_SYMBOL(pcibios_host_nr); ++ ++int pcibios_1st_host_bus_nr(void) ++{ ++ int bus_nr = 0; ++ struct pci_controller *hose = hose_head; ++ ++ if (hose != NULL) { ++ if (hose->bus != NULL) { ++ bus_nr = hose->bus->number + 1; ++ } ++ } ++ return bus_nr; ++} ++EXPORT_SYMBOL(pcibios_1st_host_bus_nr); ++ + static int pcibios_enable_resources(struct pci_dev *dev, int mask) + { + u16 cmd, old_cmd; +diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig +index 50e94e0..4bf848f 100644 +--- a/drivers/pci/pcie/aer/Kconfig ++++ b/drivers/pci/pcie/aer/Kconfig +@@ -5,7 +5,7 @@ + config PCIEAER + boolean "Root Port Advanced Error Reporting support" + depends on PCIEPORTBUS +- default y ++ default n + help + This enables PCI Express Root Port Advanced Error Reporting + (AER) driver support. Error reporting messages sent to Root +diff --git a/include/linux/pci.h b/include/linux/pci.h +index ee21795..0d08026 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1038,6 +1038,8 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), + int pci_cfg_space_size_ext(struct pci_dev *dev); + int pci_cfg_space_size(struct pci_dev *dev); + unsigned char pci_bus_max_busnr(struct pci_bus *bus); ++int pcibios_host_nr(void); ++int pcibios_1st_host_bus_nr(void); + void pci_setup_bridge(struct pci_bus *bus); + resource_size_t pcibios_window_alignment(struct pci_bus *bus, + unsigned long type); +diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h +index 9d36b82..493ada4 100644 +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -1040,6 +1040,12 @@ + #define PCI_DEVICE_ID_SGI_LITHIUM 0x1002 + #define PCI_DEVICE_ID_SGI_IOC4 0x100a + ++#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 0x00 ++ + #define PCI_VENDOR_ID_WINBOND 0x10ad + #define PCI_DEVICE_ID_WINBOND_82C105 0x0105 + #define PCI_DEVICE_ID_WINBOND_83C553 0x0565 +-- +1.7.10.4 + diff --git a/target/linux/lantiq/patches-3.7/0123-USB-fix-roothub-for-IFXHCD.patch b/target/linux/lantiq/patches-3.7/0123-USB-fix-roothub-for-IFXHCD.patch new file mode 100644 index 0000000000..0d1feb59a4 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0123-USB-fix-roothub-for-IFXHCD.patch @@ -0,0 +1,34 @@ +From 1b6941ae603f2885e6cf729119ef753deb7eb835 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 6 Dec 2012 19:59:53 +0100 +Subject: [PATCH 123/123] USB: fix roothub for IFXHCD + +--- + drivers/usb/core/hub.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: linux-3.7-rc8/drivers/usb/core/hub.c +=================================================================== +--- linux-3.7-rc8.orig/drivers/usb/core/hub.c 2012-12-03 20:22:37.000000000 +0100 ++++ linux-3.7-rc8/drivers/usb/core/hub.c 2012-12-10 23:37:16.658956109 +0100 +@@ -3839,7 +3839,7 @@ + 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; +Index: linux-3.7-rc8/arch/mips/lantiq/Kconfig +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/lantiq/Kconfig 2012-12-10 23:37:16.622956108 +0100 ++++ linux-3.7-rc8/arch/mips/lantiq/Kconfig 2012-12-11 13:58:10.816251053 +0100 +@@ -3,6 +3,7 @@ + config SOC_TYPE_XWAY + bool + select PINCTRL_XWAY ++ select USB_ARCH_HAS_HCD + default n + + choice diff --git a/target/linux/lantiq/patches-3.7/0124-pci_fix.patch b/target/linux/lantiq/patches-3.7/0124-pci_fix.patch new file mode 100644 index 0000000000..013dac76cb --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0124-pci_fix.patch @@ -0,0 +1,22 @@ +Index: linux-3.7-rc8/arch/mips/pci/pci-lantiq.c +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/pci/pci-lantiq.c 2012-12-03 20:22:37.000000000 +0100 ++++ linux-3.7-rc8/arch/mips/pci/pci-lantiq.c 2012-12-14 23:28:24.355606776 +0100 +@@ -129,8 +129,15 @@ + + /* 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); diff --git a/target/linux/lantiq/patches-3.7/0300-owrt-mtd-split.patch b/target/linux/lantiq/patches-3.7/0300-owrt-mtd-split.patch new file mode 100644 index 0000000000..4f69ef96b3 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0300-owrt-mtd-split.patch @@ -0,0 +1,230 @@ +From 2a295753a10823a47542c779a25bbb1f52c71281 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 3 Aug 2012 10:27:13 +0200 +Subject: [PATCH 19/25] owrt mtd split + +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 1 + + arch/mips/lantiq/setup.c | 7 + + drivers/mtd/Kconfig | 4 + + drivers/mtd/mtdpart.c | 173 +++++++++++++++++++- + 4 files changed, 184 insertions(+), 1 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 1ec8f2a..1ff93cc 100644 +diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig +index 982a98b..e2f3f3e 100644 +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -31,6 +31,10 @@ config MTD_ROOTFS_SPLIT + bool "Automatically split 'rootfs' partition for squashfs" + default y + ++config MTD_UIMAGE_SPLIT ++ bool "Automatically split 'linux' partition into 'kernel' and 'rootfs'" ++ default y ++ + config MTD_REDBOOT_PARTS + tristate "RedBoot partition table parsing" + ---help--- +diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c +index 855b70b..93711e2 100644 +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -867,6 +867,168 @@ static int refresh_rootfs_split(struct mtd_info *mtd) + } + #endif /* CONFIG_MTD_ROOTFS_SPLIT */ + ++#ifdef CONFIG_MTD_UIMAGE_SPLIT ++static unsigned long find_uimage_size(struct mtd_info *mtd, ++ unsigned long offset) ++{ ++#define UBOOT_MAGIC 0x56190527 ++ unsigned long magic = 0; ++ unsigned long temp; ++ size_t len; ++ int ret; ++ ++ ret = mtd_read(mtd, offset, 4, &len, (void *)&magic); ++ if (ret || len != sizeof(magic)) ++ return 0; ++ ++ if (le32_to_cpu(magic) != UBOOT_MAGIC) ++ return 0; ++ ++ ret = mtd_read(mtd, offset + 12, 4, &len, (void *)&temp); ++ if (ret || len != sizeof(temp)) ++ return 0; ++ ++ return temp + 0x40; ++} ++ ++static unsigned long find_eva_size(struct mtd_info *mtd, ++ unsigned long offset) ++{ ++#define EVA_MAGIC 0xfeed1281 ++ unsigned long magic = 0; ++ unsigned long temp; ++ size_t len; ++ int ret; ++ ++ ret = mtd_read(mtd, offset, 4, &len, (void *)&magic); ++ if (ret || len != sizeof(magic)) ++ return 0; ++ ++ if (le32_to_cpu(magic) != EVA_MAGIC) ++ return 0; ++ ++ ret = mtd_read(mtd, offset + 4, 4, &len, (void *)&temp); ++ if (ret || len != sizeof(temp)) ++ return 0; ++ ++ /* add eva header size */ ++ temp = le32_to_cpu(temp) + 0x18; ++ ++ temp &= ~0xffff; ++ temp += 0x10000; ++ return temp; ++} ++ ++static int detect_squashfs_partition(struct mtd_info *mtd, unsigned long offset) ++{ ++ unsigned long temp; ++ size_t len; ++ int ret; ++ ++ ret = mtd_read(mtd, offset, 4, &len, (void *)&temp); ++ if (ret || len != sizeof(temp)) ++ return 0; ++ ++ ++ return le32_to_cpu(temp) == SQUASHFS_MAGIC; ++} ++ ++static int detect_eva_squashfs_partition(struct mtd_info *mtd, unsigned long offset) ++{ ++ unsigned long temp; ++ size_t len; ++ int ret; ++ ++ ret = mtd_read(mtd, offset, 4, &len, (void *)&temp); ++ if (ret || len != sizeof(temp)) ++ return 0; ++ ++ return be32_to_cpu(temp) == SQUASHFS_MAGIC; ++} ++ ++static unsigned long find_brnimage_size(struct mtd_info *mtd, ++ unsigned long offset) ++{ ++ unsigned long buf[4]; ++ // Assume at most 2MB of kernel image ++ unsigned long end = offset + (2 << 20); ++ unsigned long ptr = offset + 0x400 - 12; ++ size_t len; ++ int ret; ++ ++ while (ptr < end) { ++ long size_min = ptr - 0x400 - 12 - offset; ++ long size_max = ptr + 12 - offset; ++ ret = mtd_read(mtd, ptr, 16, &len, (void *)buf); ++ if (ret || len != 16) ++ return 0; ++ ++ if (le32_to_cpu(buf[0]) < size_min || ++ le32_to_cpu(buf[0]) > size_max) { ++ ptr += 0x400; ++ continue; ++ } ++ ++ if (le32_to_cpu(buf[3]) == SQUASHFS_MAGIC) ++ return ptr + 12 - offset; ++ ++ ptr += 0x400; ++ } ++ ++ return 0; ++} ++ ++static int split_uimage(struct mtd_info *mtd, ++ const struct mtd_partition *part) ++{ ++ static struct mtd_partition split_partitions[] = { ++ { ++ .name = "kernel", ++ .offset = 0x0, ++ .size = 0x0, ++ }, { ++ .name = "rootfs", ++ .offset = 0x0, ++ .size = 0x0, ++ }, ++ }; ++ ++ split_partitions[0].size = find_uimage_size(mtd, part->offset); ++ if (!split_partitions[0].size) { ++ split_partitions[0].size = find_eva_size(mtd, part->offset); ++ if (!split_partitions[0].size) { ++ split_partitions[0].size = find_brnimage_size(mtd, part->offset); ++ if (!split_partitions[0].size) { ++ printk(KERN_NOTICE "no uImage or brnImage or eva found in linux partition\n"); ++ return -1; ++ } ++ } ++ } ++ ++ if (detect_eva_squashfs_partition(mtd, ++ part->offset ++ + split_partitions[0].size)) { ++ split_partitions[0].size += 0x100; ++ pr_info("found eva dummy squashfs behind kernel\n"); ++ } else if (!detect_squashfs_partition(mtd, ++ part->offset ++ + split_partitions[0].size)) { ++ split_partitions[0].size &= ~(mtd->erasesize - 1); ++ split_partitions[0].size += mtd->erasesize; ++ } else { ++ pr_info("found squashfs behind kernel\n"); ++ } ++ ++ split_partitions[0].offset = part->offset; ++ split_partitions[1].offset = part->offset + split_partitions[0].size; ++ split_partitions[1].size = part->size - split_partitions[0].size; ++ ++ add_mtd_partitions(mtd, split_partitions, 2); ++ ++ return 0; ++} ++#endif ++ + /* + * This function, given a master MTD object and a partition table, creates + * and registers slave MTD objects which are bound to the master according to +@@ -883,7 +1045,7 @@ int add_mtd_partitions(struct mtd_info *master, + struct mtd_part *slave; + uint64_t cur_offset = 0; + int i; +-#ifdef CONFIG_MTD_ROOTFS_SPLIT ++#if defined(CONFIG_MTD_ROOTFS_SPLIT) || defined(CONFIG_MTD_UIMAGE_SPLIT) + int ret; + #endif + +@@ -900,6 +1062,15 @@ int add_mtd_partitions(struct mtd_info *master, + + add_mtd_device(&slave->mtd); + ++#ifdef CONFIG_MTD_UIMAGE_SPLIT ++ if (!strcmp(parts[i].name, "linux")) { ++ ret = split_uimage(master, &parts[i]); ++ ++ if (ret) ++ printk(KERN_WARNING "Can't split linux partition\n"); ++ } ++#endif ++ + if (!strcmp(parts[i].name, "rootfs")) { + #ifdef CONFIG_MTD_ROOTFS_ROOT_DEV + if (ROOT_DEV == 0) { +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.7/0301-gptu.path b/target/linux/lantiq/patches-3.7/0301-gptu.path new file mode 100644 index 0000000000..d6d1ae88f9 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0301-gptu.path @@ -0,0 +1,1049 @@ +Index: linux-3.7-rc8/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h 2012-12-13 10:34:27.044276614 +0100 ++++ linux-3.7-rc8/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h 2012-12-13 10:41:34.332287326 +0100 +@@ -90,5 +90,8 @@ + extern void ltq_pmu_enable(unsigned int module); + extern void ltq_pmu_disable(unsigned int module); + ++/* allow tapi driver to read the gptu value */ ++long gptu_get_count(struct clk *clk); ++ + #endif /* CONFIG_SOC_TYPE_XWAY */ + #endif /* _LTQ_XWAY_H__ */ +Index: linux-3.7-rc8/arch/mips/lantiq/xway/gptu.c +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/lantiq/xway/gptu.c 2012-12-13 10:34:27.044276614 +0100 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/gptu.c 2012-12-13 10:34:30.564276702 +0100 +@@ -105,8 +105,11 @@ + gptu_w32(CON_CNT | CON_EDGE_ANY | CON_SYNC | CON_CLK_INT, + GPTU_CON(clk->bits)); + gptu_w32(1, GPTU_RLD(clk->bits)); +- gptu_w32(gptu_r32(GPTU_IRNEN) | BIT(clk->bits), GPTU_IRNEN); ++/* gptu_w32(gptu_r32(GPTU_IRNEN) | BIT(clk->bits), GPTU_IRNEN);*/ + gptu_w32(RUN_SEN | RUN_RL, GPTU_RUN(clk->bits)); ++ ++ printk("%s:%s[%d]%X %X %X\n", __FILE__, __func__, __LINE__, gptu_r32(GPTU_CON(clk->bits)), gptu_r32(GPTU_RLD(clk->bits)), gptu_r32(GPTU_RUN(clk->bits))); ++ + return 0; + } + +@@ -119,6 +122,12 @@ + free_irq(irqres[clk->bits].start, NULL); + } + ++long gptu_get_count(struct clk *clk) ++{ ++ return gptu_r32(GPTU_CNT(clk->bits)); ++} ++EXPORT_SYMBOL_GPL(gptu_get_count); ++ + static inline void clkdev_add_gptu(struct device *dev, const char *con, + unsigned int timer) + { +Index: linux-3.7-rc8/arch/mips/lantiq/xway/timer.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/timer.c 2012-12-13 10:41:16.360286872 +0100 +@@ -0,0 +1,841 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "../clk.h" ++ ++#include ++#include ++#include ++ ++#define MAX_NUM_OF_32BIT_TIMER_BLOCKS 6 ++ ++#ifdef TIMER1A ++#define FIRST_TIMER TIMER1A ++#else ++#define FIRST_TIMER 2 ++#endif ++ ++/* ++ * GPTC divider is set or not. ++ */ ++#define GPTU_CLC_RMC_IS_SET 0 ++ ++/* ++ * Timer Interrupt (IRQ) ++ */ ++/* Must be adjusted when ICU driver is available */ ++#define TIMER_INTERRUPT (INT_NUM_IM3_IRL0 + 22) ++ ++/* ++ * Bits Operation ++ */ ++#define GET_BITS(x, msb, lsb) \ ++ (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb)) ++#define SET_BITS(x, msb, lsb, value) \ ++ (((x) & ~(((1 << ((msb) + 1)) - 1) ^ ((1 << (lsb)) - 1))) | \ ++ (((value) & ((1 << (1 + (msb) - (lsb))) - 1)) << (lsb))) ++ ++/* ++ * GPTU Register Mapping ++ */ ++#define LQ_GPTU (KSEG1 + 0x1E100A00) ++#define LQ_GPTU_CLC ((volatile u32 *)(LQ_GPTU + 0x0000)) ++#define LQ_GPTU_ID ((volatile u32 *)(LQ_GPTU + 0x0008)) ++#define LQ_GPTU_CON(n, X) ((volatile u32 *)(LQ_GPTU + 0x0010 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_RUN(n, X) ((volatile u32 *)(LQ_GPTU + 0x0018 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_RELOAD(n, X) ((volatile u32 *)(LQ_GPTU + 0x0020 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_COUNT(n, X) ((volatile u32 *)(LQ_GPTU + 0x0028 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_IRNEN ((volatile u32 *)(LQ_GPTU + 0x00F4)) ++#define LQ_GPTU_IRNICR ((volatile u32 *)(LQ_GPTU + 0x00F8)) ++#define LQ_GPTU_IRNCR ((volatile u32 *)(LQ_GPTU + 0x00FC)) ++ ++/* ++ * Clock Control Register ++ */ ++#define GPTU_CLC_SMC GET_BITS(*LQ_GPTU_CLC, 23, 16) ++#define GPTU_CLC_RMC GET_BITS(*LQ_GPTU_CLC, 15, 8) ++#define GPTU_CLC_FSOE (*LQ_GPTU_CLC & (1 << 5)) ++#define GPTU_CLC_EDIS (*LQ_GPTU_CLC & (1 << 3)) ++#define GPTU_CLC_SPEN (*LQ_GPTU_CLC & (1 << 2)) ++#define GPTU_CLC_DISS (*LQ_GPTU_CLC & (1 << 1)) ++#define GPTU_CLC_DISR (*LQ_GPTU_CLC & (1 << 0)) ++ ++#define GPTU_CLC_SMC_SET(value) SET_BITS(0, 23, 16, (value)) ++#define GPTU_CLC_RMC_SET(value) SET_BITS(0, 15, 8, (value)) ++#define GPTU_CLC_FSOE_SET(value) ((value) ? (1 << 5) : 0) ++#define GPTU_CLC_SBWE_SET(value) ((value) ? (1 << 4) : 0) ++#define GPTU_CLC_EDIS_SET(value) ((value) ? (1 << 3) : 0) ++#define GPTU_CLC_SPEN_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_CLC_DISR_SET(value) ((value) ? (1 << 0) : 0) ++ ++/* ++ * ID Register ++ */ ++#define GPTU_ID_ID GET_BITS(*LQ_GPTU_ID, 15, 8) ++#define GPTU_ID_CFG GET_BITS(*LQ_GPTU_ID, 7, 5) ++#define GPTU_ID_REV GET_BITS(*LQ_GPTU_ID, 4, 0) ++ ++/* ++ * Control Register of Timer/Counter nX ++ * n is the index of block (1 based index) ++ * X is either A or B ++ */ ++#define GPTU_CON_SRC_EG(n, X) (*LQ_GPTU_CON(n, X) & (1 << 10)) ++#define GPTU_CON_SRC_EXT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 9)) ++#define GPTU_CON_SYNC(n, X) (*LQ_GPTU_CON(n, X) & (1 << 8)) ++#define GPTU_CON_EDGE(n, X) GET_BITS(*LQ_GPTU_CON(n, X), 7, 6) ++#define GPTU_CON_INV(n, X) (*LQ_GPTU_CON(n, X) & (1 << 5)) ++#define GPTU_CON_EXT(n, X) (*LQ_GPTU_CON(n, A) & (1 << 4)) /* Timer/Counter B does not have this bit */ ++#define GPTU_CON_STP(n, X) (*LQ_GPTU_CON(n, X) & (1 << 3)) ++#define GPTU_CON_CNT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 2)) ++#define GPTU_CON_DIR(n, X) (*LQ_GPTU_CON(n, X) & (1 << 1)) ++#define GPTU_CON_EN(n, X) (*LQ_GPTU_CON(n, X) & (1 << 0)) ++ ++#define GPTU_CON_SRC_EG_SET(value) ((value) ? 0 : (1 << 10)) ++#define GPTU_CON_SRC_EXT_SET(value) ((value) ? (1 << 9) : 0) ++#define GPTU_CON_SYNC_SET(value) ((value) ? (1 << 8) : 0) ++#define GPTU_CON_EDGE_SET(value) SET_BITS(0, 7, 6, (value)) ++#define GPTU_CON_INV_SET(value) ((value) ? (1 << 5) : 0) ++#define GPTU_CON_EXT_SET(value) ((value) ? (1 << 4) : 0) ++#define GPTU_CON_STP_SET(value) ((value) ? (1 << 3) : 0) ++#define GPTU_CON_CNT_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_CON_DIR_SET(value) ((value) ? (1 << 1) : 0) ++ ++#define GPTU_RUN_RL_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_RUN_CEN_SET(value) ((value) ? (1 << 1) : 0) ++#define GPTU_RUN_SEN_SET(value) ((value) ? (1 << 0) : 0) ++ ++#define GPTU_IRNEN_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0) ++#define GPTU_IRNCR_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0) ++ ++#define TIMER_FLAG_MASK_SIZE(x) (x & 0x0001) ++#define TIMER_FLAG_MASK_TYPE(x) (x & 0x0002) ++#define TIMER_FLAG_MASK_STOP(x) (x & 0x0004) ++#define TIMER_FLAG_MASK_DIR(x) (x & 0x0008) ++#define TIMER_FLAG_NONE_EDGE 0x0000 ++#define TIMER_FLAG_MASK_EDGE(x) (x & 0x0030) ++#define TIMER_FLAG_REAL 0x0000 ++#define TIMER_FLAG_INVERT 0x0040 ++#define TIMER_FLAG_MASK_INVERT(x) (x & 0x0040) ++#define TIMER_FLAG_MASK_TRIGGER(x) (x & 0x0070) ++#define TIMER_FLAG_MASK_SYNC(x) (x & 0x0080) ++#define TIMER_FLAG_CALLBACK_IN_HB 0x0200 ++#define TIMER_FLAG_MASK_HANDLE(x) (x & 0x0300) ++#define TIMER_FLAG_MASK_SRC(x) (x & 0x1000) ++ ++struct timer_dev_timer { ++ unsigned int f_irq_on; ++ unsigned int irq; ++ unsigned int flag; ++ unsigned long arg1; ++ unsigned long arg2; ++}; ++ ++struct timer_dev { ++ struct mutex gptu_mutex; ++ unsigned int number_of_timers; ++ unsigned int occupation; ++ unsigned int f_gptu_on; ++ struct timer_dev_timer timer[MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2]; ++}; ++ ++ ++unsigned int ltq_get_fpi_bus_clock(int fpi) { ++ struct clk *clk = clk_get_fpi(); ++ return clk_get_rate(clk); ++} ++ ++ ++static long gptu_ioctl(struct file *, unsigned int, unsigned long); ++static int gptu_open(struct inode *, struct file *); ++static int gptu_release(struct inode *, struct file *); ++ ++static struct file_operations gptu_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = gptu_ioctl, ++ .open = gptu_open, ++ .release = gptu_release ++}; ++ ++static struct miscdevice gptu_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "gptu", ++ .fops = &gptu_fops, ++}; ++ ++static struct timer_dev timer_dev; ++ ++static irqreturn_t timer_irq_handler(int irq, void *p) ++{ ++ unsigned int timer; ++ unsigned int flag; ++ struct timer_dev_timer *dev_timer = (struct timer_dev_timer *)p; ++ ++ timer = irq - TIMER_INTERRUPT; ++ if (timer < timer_dev.number_of_timers ++ && dev_timer == &timer_dev.timer[timer]) { ++ /* Clear interrupt. */ ++ ltq_w32(1 << timer, LQ_GPTU_IRNCR); ++ ++ /* Call user hanler or signal. */ ++ flag = dev_timer->flag; ++ if (!(timer & 0x01) ++ || TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) { ++ /* 16-bit timer or timer A of 32-bit timer */ ++ switch (TIMER_FLAG_MASK_HANDLE(flag)) { ++ case TIMER_FLAG_CALLBACK_IN_IRQ: ++ case TIMER_FLAG_CALLBACK_IN_HB: ++ if (dev_timer->arg1) ++ (*(timer_callback)dev_timer->arg1)(dev_timer->arg2); ++ break; ++ case TIMER_FLAG_SIGNAL: ++ send_sig((int)dev_timer->arg2, (struct task_struct *)dev_timer->arg1, 0); ++ break; ++ } ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++static inline void lq_enable_gptu(void) ++{ ++ struct clk *clk = clk_get_sys("ltq_gptu", NULL); ++ clk_enable(clk); ++ ++ //ltq_pmu_enable(PMU_GPT); ++ ++ /* Set divider as 1, disable write protection for SPEN, enable module. */ ++ *LQ_GPTU_CLC = ++ GPTU_CLC_SMC_SET(0x00) | ++ GPTU_CLC_RMC_SET(0x01) | ++ GPTU_CLC_FSOE_SET(0) | ++ GPTU_CLC_SBWE_SET(1) | ++ GPTU_CLC_EDIS_SET(0) | ++ GPTU_CLC_SPEN_SET(0) | ++ GPTU_CLC_DISR_SET(0); ++} ++ ++static inline void lq_disable_gptu(void) ++{ ++ struct clk *clk = clk_get_sys("ltq_gptu", NULL); ++ ltq_w32(0x00, LQ_GPTU_IRNEN); ++ ltq_w32(0xfff, LQ_GPTU_IRNCR); ++ ++ /* Set divider as 0, enable write protection for SPEN, disable module. */ ++ *LQ_GPTU_CLC = ++ GPTU_CLC_SMC_SET(0x00) | ++ GPTU_CLC_RMC_SET(0x00) | ++ GPTU_CLC_FSOE_SET(0) | ++ GPTU_CLC_SBWE_SET(0) | ++ GPTU_CLC_EDIS_SET(0) | ++ GPTU_CLC_SPEN_SET(0) | ++ GPTU_CLC_DISR_SET(1); ++ ++ clk_enable(clk); ++} ++ ++int lq_request_timer(unsigned int timer, unsigned int flag, ++ unsigned long value, unsigned long arg1, unsigned long arg2) ++{ ++ int ret = 0; ++ unsigned int con_reg, irnen_reg; ++ int n, X; ++ ++ if (timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ printk(KERN_INFO "request_timer(%d, 0x%08X, %lu)...", ++ timer, flag, value); ++ ++ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) ++ value &= 0xFFFF; ++ else ++ timer &= ~0x01; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ /* ++ * Allocate timer. ++ */ ++ if (timer < FIRST_TIMER) { ++ unsigned int mask; ++ unsigned int shift; ++ /* This takes care of TIMER1B which is the only choice for Voice TAPI system */ ++ unsigned int offset = TIMER2A; ++ ++ /* ++ * Pick up a free timer. ++ */ ++ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) { ++ mask = 1 << offset; ++ shift = 1; ++ } else { ++ mask = 3 << offset; ++ shift = 2; ++ } ++ for (timer = offset; ++ timer < offset + timer_dev.number_of_timers; ++ timer += shift, mask <<= shift) ++ if (!(timer_dev.occupation & mask)) { ++ timer_dev.occupation |= mask; ++ break; ++ } ++ if (timer >= offset + timer_dev.number_of_timers) { ++ printk("failed![%d]\n", __LINE__); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } else ++ ret = timer; ++ } else { ++ register unsigned int mask; ++ ++ /* ++ * Check if the requested timer is free. ++ */ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if ((timer_dev.occupation & mask)) { ++ printk("failed![%d] mask %#x, timer_dev.occupation %#x\n", ++ __LINE__, mask, timer_dev.occupation); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EBUSY; ++ } else { ++ timer_dev.occupation |= mask; ++ ret = 0; ++ } ++ } ++ ++ /* ++ * Prepare control register value. ++ */ ++ switch (TIMER_FLAG_MASK_EDGE(flag)) { ++ default: ++ case TIMER_FLAG_NONE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x00); ++ break; ++ case TIMER_FLAG_RISE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x01); ++ break; ++ case TIMER_FLAG_FALL_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x02); ++ break; ++ case TIMER_FLAG_ANY_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x03); ++ break; ++ } ++ if (TIMER_FLAG_MASK_TYPE(flag) == TIMER_FLAG_TIMER) ++ con_reg |= ++ TIMER_FLAG_MASK_SRC(flag) == ++ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : ++ GPTU_CON_SRC_EXT_SET(0); ++ else ++ con_reg |= ++ TIMER_FLAG_MASK_SRC(flag) == ++ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : ++ GPTU_CON_SRC_EG_SET(0); ++ con_reg |= ++ TIMER_FLAG_MASK_SYNC(flag) == ++ TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : ++ GPTU_CON_SYNC_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_INVERT(flag) == ++ TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_SIZE(flag) == ++ TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : ++ GPTU_CON_EXT_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_STOP(flag) == ++ TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0); ++ con_reg |= ++ TIMER_FLAG_MASK_TYPE(flag) == ++ TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : ++ GPTU_CON_CNT_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_DIR(flag) == ++ TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0); ++ ++ /* ++ * Fill up running data. ++ */ ++ timer_dev.timer[timer - FIRST_TIMER].flag = flag; ++ timer_dev.timer[timer - FIRST_TIMER].arg1 = arg1; ++ timer_dev.timer[timer - FIRST_TIMER].arg2 = arg2; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flag; ++ ++ /* ++ * Enable GPTU module. ++ */ ++ if (!timer_dev.f_gptu_on) { ++ lq_enable_gptu(); ++ timer_dev.f_gptu_on = 1; ++ } ++ ++ /* ++ * Enable IRQ. ++ */ ++ if (TIMER_FLAG_MASK_HANDLE(flag) != TIMER_FLAG_NO_HANDLE) { ++ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL) ++ timer_dev.timer[timer - FIRST_TIMER].arg1 = ++ (unsigned long) find_task_by_vpid((int) arg1); ++ ++ irnen_reg = 1 << (timer - FIRST_TIMER); ++ ++ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL ++ || (TIMER_FLAG_MASK_HANDLE(flag) == ++ TIMER_FLAG_CALLBACK_IN_IRQ ++ && timer_dev.timer[timer - FIRST_TIMER].arg1)) { ++ enable_irq(timer_dev.timer[timer - FIRST_TIMER].irq); ++ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 1; ++ } ++ } else ++ irnen_reg = 0; ++ ++ /* ++ * Write config register, reload value and enable interrupt. ++ */ ++ n = timer >> 1; ++ X = timer & 0x01; ++ *LQ_GPTU_CON(n, X) = con_reg; ++ *LQ_GPTU_RELOAD(n, X) = value; ++ /* printk("reload value = %d\n", (u32)value); */ ++ *LQ_GPTU_IRNEN |= irnen_reg; ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ printk("successful!\n"); ++ return ret; ++} ++EXPORT_SYMBOL(lq_request_timer); ++ ++int lq_free_timer(unsigned int timer) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ if (GPTU_CON_EN(n, X)) ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1); ++ ++ *LQ_GPTU_IRNEN &= ~GPTU_IRNEN_TC_SET(n, X, 1); ++ *LQ_GPTU_IRNCR |= GPTU_IRNCR_TC_SET(n, X, 1); ++ ++ if (timer_dev.timer[timer - FIRST_TIMER].f_irq_on) { ++ disable_irq(timer_dev.timer[timer - FIRST_TIMER].irq); ++ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 0; ++ } ++ ++ timer_dev.occupation &= ~mask; ++ if (!timer_dev.occupation && timer_dev.f_gptu_on) { ++ lq_disable_gptu(); ++ timer_dev.f_gptu_on = 0; ++ } ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_free_timer); ++ ++int lq_start_timer(unsigned int timer, int is_resume) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == ++ TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_RL_SET(!is_resume) | GPTU_RUN_SEN_SET(1); ++ ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_start_timer); ++ ++int lq_stop_timer(unsigned int timer) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER ++ || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1); ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_stop_timer); ++ ++int lq_reset_counter_flags(u32 timer, u32 flags) ++{ ++ unsigned int oflag; ++ unsigned int mask, con_reg; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ oflag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(oflag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(oflag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ switch (TIMER_FLAG_MASK_EDGE(flags)) { ++ default: ++ case TIMER_FLAG_NONE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x00); ++ break; ++ case TIMER_FLAG_RISE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x01); ++ break; ++ case TIMER_FLAG_FALL_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x02); ++ break; ++ case TIMER_FLAG_ANY_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x03); ++ break; ++ } ++ if (TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER) ++ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : GPTU_CON_SRC_EXT_SET(0); ++ else ++ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : GPTU_CON_SRC_EG_SET(0); ++ con_reg |= TIMER_FLAG_MASK_SYNC(flags) == TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : GPTU_CON_SYNC_SET(1); ++ con_reg |= TIMER_FLAG_MASK_INVERT(flags) == TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1); ++ con_reg |= TIMER_FLAG_MASK_SIZE(flags) == TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : GPTU_CON_EXT_SET(1); ++ con_reg |= TIMER_FLAG_MASK_STOP(flags) == TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0); ++ con_reg |= TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : GPTU_CON_CNT_SET(1); ++ con_reg |= TIMER_FLAG_MASK_DIR(flags) == TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0); ++ ++ timer_dev.timer[timer - FIRST_TIMER].flag = flags; ++ if (TIMER_FLAG_MASK_SIZE(flags) != TIMER_FLAG_16BIT) ++ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flags; ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_CON(n, X) = con_reg; ++ smp_wmb(); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return 0; ++} ++EXPORT_SYMBOL(lq_reset_counter_flags); ++ ++int lq_get_count_value(unsigned int timer, unsigned long *value) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER ++ || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *value = *LQ_GPTU_COUNT(n, X); ++ ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_get_count_value); ++ ++u32 lq_cal_divider(unsigned long freq) ++{ ++ u64 module_freq, fpi = ltq_get_fpi_bus_clock(2); ++ u32 clock_divider = 1; ++ module_freq = fpi * 1000; ++ do_div(module_freq, clock_divider * freq); ++ return module_freq; ++} ++EXPORT_SYMBOL(lq_cal_divider); ++ ++int lq_set_timer(unsigned int timer, unsigned int freq, int is_cyclic, ++ int is_ext_src, unsigned int handle_flag, unsigned long arg1, ++ unsigned long arg2) ++{ ++ unsigned long divider; ++ unsigned int flag; ++ ++ divider = lq_cal_divider(freq); ++ if (divider == 0) ++ return -EINVAL; ++ flag = ((divider & ~0xFFFF) ? TIMER_FLAG_32BIT : TIMER_FLAG_16BIT) ++ | (is_cyclic ? TIMER_FLAG_CYCLIC : TIMER_FLAG_ONCE) ++ | (is_ext_src ? TIMER_FLAG_EXT_SRC : TIMER_FLAG_INT_SRC) ++ | TIMER_FLAG_TIMER | TIMER_FLAG_DOWN ++ | TIMER_FLAG_MASK_HANDLE(handle_flag); ++ ++ printk(KERN_INFO "lq_set_timer(%d, %d), divider = %lu\n", ++ timer, freq, divider); ++ return lq_request_timer(timer, flag, divider, arg1, arg2); ++} ++EXPORT_SYMBOL(lq_set_timer); ++ ++int lq_set_counter(unsigned int timer, unsigned int flag, u32 reload, ++ unsigned long arg1, unsigned long arg2) ++{ ++ printk(KERN_INFO "lq_set_counter(%d, %#x, %d)\n", timer, flag, reload); ++ return lq_request_timer(timer, flag, reload, arg1, arg2); ++} ++EXPORT_SYMBOL(lq_set_counter); ++ ++static long gptu_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret; ++ struct gptu_ioctl_param param; ++ ++ if (!access_ok(VERIFY_READ, arg, sizeof(struct gptu_ioctl_param))) ++ return -EFAULT; ++ copy_from_user(¶m, (void *) arg, sizeof(param)); ++ ++ if ((((cmd == GPTU_REQUEST_TIMER || cmd == GPTU_SET_TIMER ++ || GPTU_SET_COUNTER) && param.timer < 2) ++ || cmd == GPTU_GET_COUNT_VALUE || cmd == GPTU_CALCULATE_DIVIDER) ++ && !access_ok(VERIFY_WRITE, arg, ++ sizeof(struct gptu_ioctl_param))) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case GPTU_REQUEST_TIMER: ++ ret = lq_request_timer(param.timer, param.flag, param.value, ++ (unsigned long) param.pid, ++ (unsigned long) param.sig); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ case GPTU_FREE_TIMER: ++ ret = lq_free_timer(param.timer); ++ break; ++ case GPTU_START_TIMER: ++ ret = lq_start_timer(param.timer, param.flag); ++ break; ++ case GPTU_STOP_TIMER: ++ ret = lq_stop_timer(param.timer); ++ break; ++ case GPTU_GET_COUNT_VALUE: ++ ret = lq_get_count_value(param.timer, ¶m.value); ++ if (!ret) ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ value, ¶m.value, ++ sizeof(param.value)); ++ break; ++ case GPTU_CALCULATE_DIVIDER: ++ param.value = lq_cal_divider(param.value); ++ if (param.value == 0) ++ ret = -EINVAL; ++ else { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ value, ¶m.value, ++ sizeof(param.value)); ++ ret = 0; ++ } ++ break; ++ case GPTU_SET_TIMER: ++ ret = lq_set_timer(param.timer, param.value, ++ TIMER_FLAG_MASK_STOP(param.flag) != ++ TIMER_FLAG_ONCE ? 1 : 0, ++ TIMER_FLAG_MASK_SRC(param.flag) == ++ TIMER_FLAG_EXT_SRC ? 1 : 0, ++ TIMER_FLAG_MASK_HANDLE(param.flag) == ++ TIMER_FLAG_SIGNAL ? TIMER_FLAG_SIGNAL : ++ TIMER_FLAG_NO_HANDLE, ++ (unsigned long) param.pid, ++ (unsigned long) param.sig); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ case GPTU_SET_COUNTER: ++ lq_set_counter(param.timer, param.flag, param.value, 0, 0); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ default: ++ ret = -ENOTTY; ++ } ++ ++ return ret; ++} ++ ++static int gptu_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int gptu_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++int __init lq_gptu_init(void) ++{ ++ int ret; ++ unsigned int i; ++ ++ ltq_w32(0, LQ_GPTU_IRNEN); ++ ltq_w32(0xfff, LQ_GPTU_IRNCR); ++ ++ memset(&timer_dev, 0, sizeof(timer_dev)); ++ mutex_init(&timer_dev.gptu_mutex); ++ ++ lq_enable_gptu(); ++ timer_dev.number_of_timers = GPTU_ID_CFG * 2; ++ lq_disable_gptu(); ++ if (timer_dev.number_of_timers > MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2) ++ timer_dev.number_of_timers = MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2; ++ printk(KERN_INFO "gptu: totally %d 16-bit timers/counters\n", timer_dev.number_of_timers); ++ ++ ret = misc_register(&gptu_miscdev); ++ if (ret) { ++ printk(KERN_ERR "gptu: can't misc_register, get error %d\n", -ret); ++ return ret; ++ } else { ++ printk(KERN_INFO "gptu: misc_register on minor %d\n", gptu_miscdev.minor); ++ } ++ ++ for (i = 0; i < timer_dev.number_of_timers; i++) { ++ ret = request_irq(TIMER_INTERRUPT + i, timer_irq_handler, IRQF_TIMER, gptu_miscdev.name, &timer_dev.timer[i]); ++ if (ret) { ++ for (; i >= 0; i--) ++ free_irq(TIMER_INTERRUPT + i, &timer_dev.timer[i]); ++ misc_deregister(&gptu_miscdev); ++ printk(KERN_ERR "gptu: failed in requesting irq (%d), get error %d\n", i, -ret); ++ return ret; ++ } else { ++ timer_dev.timer[i].irq = TIMER_INTERRUPT + i; ++ disable_irq(timer_dev.timer[i].irq); ++ printk(KERN_INFO "gptu: succeeded to request irq %d\n", timer_dev.timer[i].irq); ++ } ++ } ++ ++ return 0; ++} ++ ++void __exit lq_gptu_exit(void) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < timer_dev.number_of_timers; i++) { ++ if (timer_dev.timer[i].f_irq_on) ++ disable_irq(timer_dev.timer[i].irq); ++ free_irq(timer_dev.timer[i].irq, &timer_dev.timer[i]); ++ } ++ lq_disable_gptu(); ++ misc_deregister(&gptu_miscdev); ++} ++ ++module_init(lq_gptu_init); ++module_exit(lq_gptu_exit); +Index: linux-3.7-rc8/arch/mips/include/asm/mach-lantiq/lantiq_timer.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc8/arch/mips/include/asm/mach-lantiq/lantiq_timer.h 2012-12-13 10:34:30.564276702 +0100 +@@ -0,0 +1,155 @@ ++#ifndef __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ ++#define __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ ++ ++ ++/****************************************************************************** ++ Copyright (c) 2002, Infineon Technologies. All rights reserved. ++ ++ No Warranty ++ 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. ++ ++ 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. ++******************************************************************************/ ++ ++ ++/* ++ * #################################### ++ * Definition ++ * #################################### ++ */ ++ ++/* ++ * Available Timer/Counter Index ++ */ ++#define TIMER(n, X) (n * 2 + (X ? 1 : 0)) ++#define TIMER_ANY 0x00 ++#define TIMER1A TIMER(1, 0) ++#define TIMER1B TIMER(1, 1) ++#define TIMER2A TIMER(2, 0) ++#define TIMER2B TIMER(2, 1) ++#define TIMER3A TIMER(3, 0) ++#define TIMER3B TIMER(3, 1) ++ ++/* ++ * Flag of Timer/Counter ++ * These flags specify the way in which timer is configured. ++ */ ++/* Bit size of timer/counter. */ ++#define TIMER_FLAG_16BIT 0x0000 ++#define TIMER_FLAG_32BIT 0x0001 ++/* Switch between timer and counter. */ ++#define TIMER_FLAG_TIMER 0x0000 ++#define TIMER_FLAG_COUNTER 0x0002 ++/* Stop or continue when overflowing/underflowing. */ ++#define TIMER_FLAG_ONCE 0x0000 ++#define TIMER_FLAG_CYCLIC 0x0004 ++/* Count up or counter down. */ ++#define TIMER_FLAG_UP 0x0000 ++#define TIMER_FLAG_DOWN 0x0008 ++/* Count on specific level or edge. */ ++#define TIMER_FLAG_HIGH_LEVEL_SENSITIVE 0x0000 ++#define TIMER_FLAG_LOW_LEVEL_SENSITIVE 0x0040 ++#define TIMER_FLAG_RISE_EDGE 0x0010 ++#define TIMER_FLAG_FALL_EDGE 0x0020 ++#define TIMER_FLAG_ANY_EDGE 0x0030 ++/* Signal is syncronous to module clock or not. */ ++#define TIMER_FLAG_UNSYNC 0x0000 ++#define TIMER_FLAG_SYNC 0x0080 ++/* Different interrupt handle type. */ ++#define TIMER_FLAG_NO_HANDLE 0x0000 ++#if defined(__KERNEL__) ++ #define TIMER_FLAG_CALLBACK_IN_IRQ 0x0100 ++#endif // defined(__KERNEL__) ++#define TIMER_FLAG_SIGNAL 0x0300 ++/* Internal clock source or external clock source */ ++#define TIMER_FLAG_INT_SRC 0x0000 ++#define TIMER_FLAG_EXT_SRC 0x1000 ++ ++ ++/* ++ * ioctl Command ++ */ ++#define GPTU_REQUEST_TIMER 0x01 /* General method to setup timer/counter. */ ++#define GPTU_FREE_TIMER 0x02 /* Free timer/counter. */ ++#define GPTU_START_TIMER 0x03 /* Start or resume timer/counter. */ ++#define GPTU_STOP_TIMER 0x04 /* Suspend timer/counter. */ ++#define GPTU_GET_COUNT_VALUE 0x05 /* Get current count value. */ ++#define GPTU_CALCULATE_DIVIDER 0x06 /* Calculate timer divider from given freq.*/ ++#define GPTU_SET_TIMER 0x07 /* Simplified method to setup timer. */ ++#define GPTU_SET_COUNTER 0x08 /* Simplified method to setup counter. */ ++ ++/* ++ * Data Type Used to Call ioctl ++ */ ++struct gptu_ioctl_param { ++ unsigned int timer; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and * ++ * GPTU_SET_COUNTER, this field is ID of expected * ++ * timer/counter. If it's zero, a timer/counter would * ++ * be dynamically allocated and ID would be stored in * ++ * this field. * ++ * In command GPTU_GET_COUNT_VALUE, this field is * ++ * ignored. * ++ * In other command, this field is ID of timer/counter * ++ * allocated. */ ++ unsigned int flag; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and * ++ * GPTU_SET_COUNTER, this field contains flags to * ++ * specify how to configure timer/counter. * ++ * In command GPTU_START_TIMER, zero indicate start * ++ * and non-zero indicate resume timer/counter. * ++ * In other command, this field is ignored. */ ++ unsigned long value; /* In command GPTU_REQUEST_TIMER, this field contains * ++ * init/reload value. * ++ * In command GPTU_SET_TIMER, this field contains * ++ * frequency (0.001Hz) of timer. * ++ * In command GPTU_GET_COUNT_VALUE, current count * ++ * value would be stored in this field. * ++ * In command GPTU_CALCULATE_DIVIDER, this field * ++ * contains frequency wanted, and after calculation, * ++ * divider would be stored in this field to overwrite * ++ * the frequency. * ++ * In other command, this field is ignored. */ ++ int pid; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, * ++ * if signal is required, this field contains process * ++ * ID to which signal would be sent. * ++ * In other command, this field is ignored. */ ++ int sig; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, * ++ * if signal is required, this field contains signal * ++ * number which would be sent. * ++ * In other command, this field is ignored. */ ++}; ++ ++/* ++ * #################################### ++ * Data Type ++ * #################################### ++ */ ++typedef void (*timer_callback)(unsigned long arg); ++ ++extern int lq_request_timer(unsigned int, unsigned int, unsigned long, unsigned long, unsigned long); ++extern int lq_free_timer(unsigned int); ++extern int lq_start_timer(unsigned int, int); ++extern int lq_stop_timer(unsigned int); ++extern int lq_reset_counter_flags(u32 timer, u32 flags); ++extern int lq_get_count_value(unsigned int, unsigned long *); ++extern u32 lq_cal_divider(unsigned long); ++extern int lq_set_timer(unsigned int, unsigned int, int, int, unsigned int, unsigned long, unsigned long); ++extern int lq_set_counter(unsigned int timer, unsigned int flag, ++ u32 reload, unsigned long arg1, unsigned long arg2); ++ ++#endif /* __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ */ diff --git a/target/linux/lantiq/patches-3.7/0302-wifi-eep.patch b/target/linux/lantiq/patches-3.7/0302-wifi-eep.patch new file mode 100644 index 0000000000..65ec0d17b6 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0302-wifi-eep.patch @@ -0,0 +1,319 @@ +Index: linux-3.7-rc8/arch/mips/lantiq/xway/Makefile +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/lantiq/xway/Makefile 2012-12-13 10:59:54.176314899 +0100 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/Makefile 2012-12-13 13:58:51.696584083 +0100 +@@ -1,3 +1,5 @@ + obj-y := prom.o sysctrl.o clk.o reset.o dma.o timer.o dcdc.o + ++obj-y += ath_eep.o rt_eep.o eth_mac.o ++ + obj-$(CONFIG_XRX200_PHY_FW) += xrx200_phy_fw.o +Index: linux-3.7-rc8/arch/mips/lantiq/xway/ath_eep.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/ath_eep.c 2012-12-13 13:49:12.472569552 +0100 +@@ -0,0 +1,120 @@ ++/* ++ * Copyright (C) 2011 John Crispin ++ * Copyright (C) 2011 Andrej Vlašić ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern int (*ltq_pci_plat_dev_init)(struct pci_dev *dev); ++struct ath5k_platform_data ath5k_pdata; ++/*struct ath9k_platform_data ath9k_pdata = { ++ .led_pin = -1, ++ .endian_check = true, ++};*/ ++static u16 ath5k_eeprom_data[ATH5K_PLAT_EEP_MAX_WORDS]; ++//static u16 ath9k_eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; ++static u8 athxk_eeprom_mac[6]; ++ ++/*static int ++ath9k_pci_plat_dev_init(struct pci_dev *dev) ++{ ++ dev->dev.platform_data = &ath9k_pdata; ++ return 0; ++} ++ ++void __init ++ltq_register_ath9k(u16 *eeprom_data, u8 *macaddr) ++{ ++ memcpy(ath9k_pdata.eeprom_data, eeprom_data, sizeof(ath9k_pdata.eeprom_data)); ++ ath9k_pdata.macaddr = macaddr; ++ ltq_pci_plat_dev_init = ath9k_pci_plat_dev_init; ++} ++*/ ++static int ath5k_pci_plat_dev_init(struct pci_dev *dev) ++{ ++ dev->dev.platform_data = &ath5k_pdata; ++ return 0; ++} ++ ++int __init of_ath5k_eeprom_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *eep_res, *mac_res; ++ void __iomem *eep, *mac; ++ int mac_offset; ++ u32 mac_inc = 0; ++ int i; ++ ++ eep_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ mac_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ ++ if (!eep_res) { ++ dev_err(&pdev->dev, "failed to load eeprom address\n"); ++ return -ENODEV; ++ } ++ if (resource_size(eep_res) != ATH5K_PLAT_EEP_MAX_WORDS) { ++ dev_err(&pdev->dev, "eeprom has an invalid size\n"); ++ return -EINVAL; ++ } ++ ++ eep = ioremap(eep_res->start, resource_size(eep_res)); ++ memcpy_fromio(ath5k_eeprom_data, eep, ATH5K_PLAT_EEP_MAX_WORDS); ++ ++ if (of_find_property(np, "ath,eep-swap", NULL)) ++ for (i = 0; i < (ATH5K_PLAT_EEP_MAX_WORDS >> 1); i++) ++ ath5k_eeprom_data[i] = swab16(ath5k_eeprom_data[i]); ++ ++ if (!of_property_read_u32(np, "ath,mac-offset", &mac_offset)) { ++ memcpy_fromio(athxk_eeprom_mac, (void*) ath5k_eeprom_data, 6); ++ } else if (mac_res) { ++ if (resource_size(mac_res) != 6) { ++ dev_err(&pdev->dev, "mac has an invalid size\n"); ++ return -EINVAL; ++ } ++ mac = ioremap(mac_res->start, resource_size(mac_res)); ++ memcpy_fromio(athxk_eeprom_mac, mac, 6); ++ } else { ++ dev_warn(&pdev->dev, "using random mac\n"); ++ random_ether_addr(athxk_eeprom_mac); ++ } ++ ++ if (!of_property_read_u32(np, "ath,mac-increment", &mac_inc)) ++ athxk_eeprom_mac[5] += mac_inc; ++ ++ ath5k_pdata.eeprom_data = ath5k_eeprom_data; ++ ath5k_pdata.macaddr = athxk_eeprom_mac; ++ ltq_pci_plat_dev_init = ath5k_pci_plat_dev_init; ++ ++ dev_info(&pdev->dev, "loaded ath5k eeprom\n"); ++ ++ return 0; ++} ++ ++static struct of_device_id ath5k_eeprom_ids[] = { ++ { .compatible = "ath5k,eeprom" }, ++ { } ++}; ++ ++static struct platform_driver ath5k_eeprom_driver = { ++ .driver = { ++ .name = "ath5k,eeprom", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(ath5k_eeprom_ids), ++ }, ++}; ++ ++static int __init of_ath5k_eeprom_init(void) ++{ ++ return platform_driver_probe(&ath5k_eeprom_driver, of_ath5k_eeprom_probe); ++} ++device_initcall(of_ath5k_eeprom_init); +Index: linux-3.7-rc8/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h 2012-12-13 10:59:57.300314976 +0100 ++++ linux-3.7-rc8/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h 2012-12-13 10:59:57.308314977 +0100 +@@ -93,5 +93,8 @@ + /* allow tapi driver to read the gptu value */ + long gptu_get_count(struct clk *clk); + ++/* allow the ethernet driver to load a flash mapped mac addr */ ++const u8* ltq_get_eth_mac(void); ++ + #endif /* CONFIG_SOC_TYPE_XWAY */ + #endif /* _LTQ_XWAY_H__ */ +Index: linux-3.7-rc8/arch/mips/lantiq/xway/eth_mac.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/eth_mac.c 2012-12-13 10:59:57.308314977 +0100 +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (C) 2012 John Crispin ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++static u8 eth_mac[6]; ++static int eth_mac_set; ++ ++const u8* ltq_get_eth_mac(void) ++{ ++ return eth_mac; ++} ++ ++static int __init setup_ethaddr(char *str) ++{ ++ eth_mac_set = mac_pton(str, eth_mac); ++ return !eth_mac_set; ++} ++__setup("ethaddr=", setup_ethaddr); ++ ++int __init of_eth_mac_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *mac_res; ++ void __iomem *mac; ++ u32 mac_inc = 0; ++ ++ if (eth_mac_set) { ++ dev_err(&pdev->dev, "mac was already set by bootloader\n"); ++ return -EINVAL; ++ } ++ mac_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ if (!mac_res) { ++ dev_err(&pdev->dev, "failed to load mac\n"); ++ return -EINVAL; ++ } ++ if (resource_size(mac_res) != 6) { ++ dev_err(&pdev->dev, "mac has an invalid size\n"); ++ return -EINVAL; ++ } ++ mac = ioremap(mac_res->start, resource_size(mac_res)); ++ memcpy_fromio(eth_mac, mac, 6); ++ ++ if (!of_property_read_u32(np, "mac-increment", &mac_inc)) ++ eth_mac[5] += mac_inc; ++ ++ return 0; ++} ++ ++static struct of_device_id eth_mac_ids[] = { ++ { .compatible = "lantiq,eth-mac" }, ++ { /* sentinel */ } ++}; ++ ++static struct platform_driver eth_mac_driver = { ++ .driver = { ++ .name = "lantiq,eth-mac", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(eth_mac_ids), ++ }, ++}; ++ ++static int __init of_eth_mac_init(void) ++{ ++ return platform_driver_probe(ð_mac_driver, of_eth_mac_probe); ++} ++device_initcall(of_eth_mac_init); +Index: linux-3.7-rc8/drivers/net/ethernet/lantiq_etop.c +=================================================================== +--- linux-3.7-rc8.orig/drivers/net/ethernet/lantiq_etop.c 2012-12-13 10:59:54.176314899 +0100 ++++ linux-3.7-rc8/drivers/net/ethernet/lantiq_etop.c 2012-12-13 10:59:57.308314977 +0100 +@@ -816,7 +816,8 @@ + + ltq_etop_change_mtu(dev, 1500); + +- memcpy(&mac.sa_data, priv->mac, ETH_ALEN); ++ if (priv->mac) ++ memcpy(&mac.sa_data, priv->mac, ETH_ALEN); + if (!is_valid_ether_addr(mac.sa_data)) { + pr_warn("etop: invalid MAC, using random\n"); + random_ether_addr(mac.sa_data); +@@ -940,7 +941,9 @@ + 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->mac = ltq_get_eth_mac(); ++ if (!priv->mac) ++ priv->mac = of_get_mac_address(pdev->dev.of_node); + + priv->clk_ppe = clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk_ppe)) +Index: linux-3.7-rc8/arch/mips/lantiq/xway/rt_eep.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/rt_eep.c 2012-12-13 13:55:43.132579350 +0100 +@@ -0,0 +1,60 @@ ++/* ++ * Copyright (C) 2011 John Crispin ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++extern int (*ltq_pci_plat_dev_init)(struct pci_dev *dev); ++static struct rt2x00_platform_data rt2x00_pdata; ++ ++static int rt2x00_pci_plat_dev_init(struct pci_dev *dev) ++{ ++ dev->dev.platform_data = &rt2x00_pdata; ++ return 0; ++} ++ ++int __init of_ralink_eeprom_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ const char *eeprom; ++ ++ if (of_property_read_string(np, "ralink,eeprom", &eeprom)) { ++ dev_err(&pdev->dev, "failed to load eeprom filename\n"); ++ return 0; ++ } ++ ++ rt2x00_pdata.eeprom_file_name = kstrdup(eeprom, GFP_KERNEL); ++// rt2x00_pdata.mac_address = mac; ++ ltq_pci_plat_dev_init = rt2x00_pci_plat_dev_init; ++ ++ dev_info(&pdev->dev, "using %s as eeprom\n", eeprom); ++ ++ return 0; ++} ++ ++static struct of_device_id ralink_eeprom_ids[] = { ++ { .compatible = "ralink,eeprom" }, ++ { } ++}; ++ ++static struct platform_driver ralink_eeprom_driver = { ++ .driver = { ++ .name = "ralink,eeprom", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(ralink_eeprom_ids), ++ }, ++}; ++ ++static int __init of_ralink_eeprom_init(void) ++{ ++ return platform_driver_probe(&ralink_eeprom_driver, of_ralink_eeprom_probe); ++} ++device_initcall(of_ralink_eeprom_init); diff --git a/target/linux/lantiq/patches-3.7/0303-vmmc.patch b/target/linux/lantiq/patches-3.7/0303-vmmc.patch new file mode 100644 index 0000000000..f0bd584e38 --- /dev/null +++ b/target/linux/lantiq/patches-3.7/0303-vmmc.patch @@ -0,0 +1,79 @@ +Index: linux-3.7-rc8/arch/mips/lantiq/xway/Makefile +=================================================================== +--- linux-3.7-rc8.orig/arch/mips/lantiq/xway/Makefile 2012-12-13 13:40:23.000000000 +0100 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/Makefile 2012-12-13 13:40:49.788556963 +0100 +@@ -1,5 +1,5 @@ + obj-y := prom.o sysctrl.o clk.o reset.o dma.o timer.o dcdc.o + +-obj-y += ath_eep.o rt_eep.o eth_mac.o ++obj-y += ath_eep.o rt_eep.o eth_mac.o vmmc.o + + obj-$(CONFIG_XRX200_PHY_FW) += xrx200_phy_fw.o +Index: linux-3.7-rc8/arch/mips/lantiq/xway/vmmc.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.7-rc8/arch/mips/lantiq/xway/vmmc.c 2012-12-13 13:40:30.520556476 +0100 +@@ -0,0 +1,63 @@ ++/* ++ * 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 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++static unsigned int *cp1_base = 0; ++unsigned int* ltq_get_cp1_base(void) ++{ ++ if (!cp1_base) ++ panic("no cp1 base was set\n"); ++ return cp1_base; ++} ++EXPORT_SYMBOL(ltq_get_cp1_base); ++ ++static int __devinit vmmc_probe(struct platform_device *pdev) ++{ ++#define CP1_SIZE (1 << 20) ++ int gpio_count; ++ dma_addr_t dma; ++ cp1_base = ++ (void*)CPHYSADDR(dma_alloc_coherent(NULL, CP1_SIZE, &dma, GFP_ATOMIC)); ++ ++ gpio_count = of_gpio_count(pdev->dev.of_node); ++ while (gpio_count) { ++ enum of_gpio_flags flags; ++ int gpio = of_get_gpio_flags(pdev->dev.of_node, --gpio_count, &flags); ++ if (gpio_request(gpio, "vmmc-relay")) ++ continue; ++ dev_info(&pdev->dev, "requested GPIO %d\n", gpio); ++ gpio_direction_output(gpio, (flags & OF_GPIO_ACTIVE_LOW) ? (0) : (1)); ++ } ++ ++ dev_info(&pdev->dev, "reserved %dMB at 0x%p", CP1_SIZE >> 20, cp1_base); ++ ++ return 0; ++} ++ ++static const struct of_device_id vmmc_match[] = { ++ { .compatible = "lantiq,vmmc" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, vmmc_match); ++ ++static struct platform_driver vmmc_driver = { ++ .probe = vmmc_probe, ++ .driver = { ++ .name = "lantiq,vmmc", ++ .owner = THIS_MODULE, ++ .of_match_table = vmmc_match, ++ }, ++}; ++ ++module_platform_driver(vmmc_driver); diff --git a/target/linux/lantiq/xway/config-default b/target/linux/lantiq/xway/config-default index 23200b367f..6ee3a3bc54 100644 --- a/target/linux/lantiq/xway/config-default +++ b/target/linux/lantiq/xway/config-default @@ -5,15 +5,28 @@ CONFIG_INPUT=y CONFIG_INPUT_EVDEV=y CONFIG_INPUT_POLLDEV=y # CONFIG_ISDN is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_M25PXX_USE_FAST_READ=y +CONFIG_MTD_M25P80=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_ECC=y CONFIG_MTD_NAND_PLATFORM=y CONFIG_MTD_NAND_XWAY=y # CONFIG_MTD_PHYSMAP_OF is not set # CONFIG_MTD_SM_COMMON is not set -CONFIG_MTD_UIMAGE_SPLIT=y +CONFIG_NLS=y # CONFIG_PROC_DEVICETREE is not set CONFIG_RTL8306_PHY=y +CONFIG_RTL8366S_PHY=y +CONFIG_RTL8367B_PHY=y +CONFIG_RTL8367_PHY=y CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_GPIO=y CONFIG_SPI_MASTER=y +CONFIG_SPI_XWAY=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +# CONFIG_USB_EHCI_HCD is not set CONFIG_USB_SUPPORT=y +# CONFIG_USB_UHCI_HCD is not set diff --git a/target/linux/lantiq/xway/profiles/arv.mk b/target/linux/lantiq/xway/profiles/arv.mk index e8d371f8fb..c0c7c7c810 100644 --- a/target/linux/lantiq/xway/profiles/arv.mk +++ b/target/linux/lantiq/xway/profiles/arv.mk @@ -1,25 +1,104 @@ +define Profile/ARV4525PW + NAME:=Speedport W502V Typ A - ARV4525PW + PACKAGES:=kmod-ath5k wpad-mini \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa +endef + +$(eval $(call Profile,ARV4525PW)) + define Profile/ARV7525PW - NAME:=Speedport W303V - ARV7525PW + NAME:=Speedport W303V Typ A - ARV7525PW + PACKAGES:=kmod-rt2800-pci wpad-mini \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa endef $(eval $(call Profile,ARV7525PW)) +define Profile/ARV4518PW + NAME:=SMC7908A-ISP, Airties WAV-221 - ARV4518PW + PACKAGES:=kmod-ltq-hcd-danube kmod-ledtrig-usbdev \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-ath5k wpad-mini \ + swconfig +endef + +$(eval $(call Profile,ARV4518PW)) + +define Profile/ARV4510PW + NAME:=Wippies Homebox - ARV4510PW + PACKAGES:=kmod-ledtrig-usbdev kmod-usb2 kmod-usb-uhci \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-rt61 wpad-mini \ + swconfig +endef + +$(eval $(call Profile,ARV4510PW)) + +define Profile/ARV7518PW + NAME:=Astoria - ARV7518PW + PACKAGES:=kmod-ltq-hcd-danube kmod-ledtrig-usbdev \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-ath9k wpad-mini \ + swconfig +endef + +$(eval $(call Profile,ARV7518PW)) + define Profile/ARV4520PW NAME:=Easybox 800, WAV-281 - ARV4520PW + PACKAGES:=kmod-ltq-hcd-danube kmod-ledtrig-usbdev \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-rt61-pci wpad-mini \ + swconfig endef $(eval $(call Profile,ARV4520PW)) +define Profile/ARV452CQW + NAME:=Easybox 801 - ARV452CQW + PACKAGES:=kmod-ltq-hcd-danube kmod-ledtrig-usbdev \ + kmod-ath5k wpad-mini \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa \ + swconfig +endef + +$(eval $(call Profile,ARV452CQW)) + define Profile/ARV752DPW NAME:=Easybox 802 - ARV752DPW + PACKAGES:=kmod-ltq-hcd-danube kmod-ledtrig-usbdev \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-ltq-tapi kmod-ltq-vmmc \ + kmod-rt2800-pci wpad-mini \ + swconfig endef $(eval $(call Profile,ARV752DPW)) define Profile/ARV752DPW22 NAME:=Easybox 803 - ARV752DPW22 - PACKAGES:= kmod-usb-core kmod-usb2 kmod-usb-uhci kmod-usb-dwc-otg kmod-ledtrig-usbdev \ - kmod-ltq-dsl-firmware-b-danube + PACKAGES:=kmod-usb2 kmod-usb-uhci kmod-ltq-hcd-danube kmod-ledtrig-usbdev \ + kmod-ltq-adsl-danube-mei kmod-ltq-adsl-danube \ + kmod-ltq-adsl-danube-fw-b kmod-ltq-atm-danube \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-ltq-tapi kmod-ltq-vmmc \ + swconfig endef $(eval $(call Profile,ARV752DPW22)) diff --git a/target/linux/lantiq/xway/profiles/avm.mk b/target/linux/lantiq/xway/profiles/avm.mk new file mode 100644 index 0000000000..75db65e31b --- /dev/null +++ b/target/linux/lantiq/xway/profiles/avm.mk @@ -0,0 +1,10 @@ +define Profile/FRITZ7320 + NAME:=1&1 HomeServer - FRITZ7320 + PACKAGES:=kmod-ath9k wpad-mini \ + kmod-ltq-adsl-ar9-mei kmod-ltq-adsl-ar9 \ + kmod-ltq-adsl-ar9-fw-b kmod-ltq-atm-ar9 \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-ltq-deu-ar9 +endef + +$(eval $(call Profile,FRITZ7320)) diff --git a/target/linux/lantiq/xway/profiles/lantiq.mk b/target/linux/lantiq/xway/profiles/lantiq.mk index 2c24d3c95e..b4c914dd50 100644 --- a/target/linux/lantiq/xway/profiles/lantiq.mk +++ b/target/linux/lantiq/xway/profiles/lantiq.mk @@ -1,19 +1,17 @@ -define Profile/EASY80920-NOR - NAME:=EASY80920-NOR +define Profile/EASY50712 + NAME:=Lantiq Danube - EASY50712 endef -define Profile/EASY80920-NOR/Description - Lantiq EASY80920 evalkit (NOR) -endef - -$(eval $(call Profile,EASY80920-NOR)) +$(eval $(call Profile,EASY50712)) -define Profile/EASY80920-NAND - NAME:=EASY80920-NAND +define Profile/EASY80920NOR + NAME:=Lantiq VR9 - EASY80920NOR endef -define Profile/EASY80920-NAND/Description - Lantiq EASY80920 evalkit (NAND) +$(eval $(call Profile,EASY80920NOR)) + +define Profile/EASY80920NAND + NAME:=Lantiq VR9 - EASY80920NAND endef -$(eval $(call Profile,EASY80920-NAND)) +$(eval $(call Profile,EASY80920NAND)) diff --git a/target/linux/lantiq/xway/profiles/netgear.mk b/target/linux/lantiq/xway/profiles/netgear.mk new file mode 100644 index 0000000000..e7b16eee42 --- /dev/null +++ b/target/linux/lantiq/xway/profiles/netgear.mk @@ -0,0 +1,21 @@ +define Profile/DGN3500A + NAME:=Netgear DGN3500A + PACKAGES:=kmod-ath9k wpad-mini \ + kmod-ltq-adsl-ar9-mei kmod-ltq-adsl-ar9 \ + kmod-ltq-adsl-ar9-fw-a kmod-ltq-atm-ar9 \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-ltq-deu-ar9 +endef + +$(eval $(call Profile,DGN3500A)) + +define Profile/DGN3500B + NAME:=Netgear DGN3500B + PACKAGES:=kmod-ath9k wpad-mini \ + kmod-ltq-adsl-ar9-mei kmod-ltq-adsl-ar9 \ + kmod-ltq-adsl-ar9-fw-b kmod-ltq-atm-ar9 \ + ltq-adsl-app ppp-mod-pppoa \ + kmod-ltq-deu-ar9 +endef + +$(eval $(call Profile,DGN3500B)) diff --git a/target/linux/lantiq/xway/target.mk b/target/linux/lantiq/xway/target.mk index 6674665ccb..a58debdc62 100644 --- a/target/linux/lantiq/xway/target.mk +++ b/target/linux/lantiq/xway/target.mk @@ -3,7 +3,7 @@ SUBTARGET:=xway BOARDNAME:=XWAY FEATURES:=squashfs jffs2 atm -DEFAULT_PACKAGES+=swconfig kmod-leds-gpio kmod-button-hotplug +DEFAULT_PACKAGES+=kmod-leds-gpio kmod-button-hotplug kmod-input-gpio-keys-polled define Target/Description Lantiq XWAY -- cgit v1.2.3