From 53ab9865c2b91bc6a239b2adee800dc52875b6bc Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sun, 8 Dec 2019 21:44:23 +0100 Subject: ath79: add support for kernel 5.4 Signed-off-by: David Bauer [refreshed] Signed-off-by: Koen Vandeputte * Sync the patches with the changes done for kernel 4.19 * Use KERNEL_TESTING_PATCHVER * Refresh the configuration * Fix multiple compile bugs in the patches * Only add own ag71xx files for kernel 4.19 and use upstream version for 5.4. Signed-off-by: Hauke Mehrtens --- target/linux/ath79/Makefile | 1 + target/linux/ath79/config-5.4 | 250 +++ .../drivers/net/ethernet/atheros/ag71xx/Kconfig | 25 + .../drivers/net/ethernet/atheros/ag71xx/Makefile | 13 + .../drivers/net/ethernet/atheros/ag71xx/ag71xx.h | 454 +++++ .../net/ethernet/atheros/ag71xx/ag71xx_debugfs.c | 285 ++++ .../net/ethernet/atheros/ag71xx/ag71xx_ethtool.c | 108 ++ .../net/ethernet/atheros/ag71xx/ag71xx_gmac.c | 135 ++ .../net/ethernet/atheros/ag71xx/ag71xx_main.c | 1736 ++++++++++++++++++++ .../net/ethernet/atheros/ag71xx/ag71xx_mdio.c | 254 +++ .../net/ethernet/atheros/ag71xx/ag71xx_phy.c | 92 ++ .../drivers/net/ethernet/atheros/ag71xx/Kconfig | 25 - .../drivers/net/ethernet/atheros/ag71xx/Makefile | 13 - .../drivers/net/ethernet/atheros/ag71xx/ag71xx.h | 454 ----- .../net/ethernet/atheros/ag71xx/ag71xx_debugfs.c | 285 ---- .../net/ethernet/atheros/ag71xx/ag71xx_ethtool.c | 108 -- .../net/ethernet/atheros/ag71xx/ag71xx_gmac.c | 135 -- .../net/ethernet/atheros/ag71xx/ag71xx_main.c | 1736 -------------------- .../net/ethernet/atheros/ag71xx/ag71xx_mdio.c | 254 --- .../net/ethernet/atheros/ag71xx/ag71xx_phy.c | 92 -- target/linux/ath79/generic/config-default | 3 +- target/linux/ath79/nand/config-default | 19 +- .../0002-watchdog-ath79-fix-maximum-timeout.patch | 32 + ...03-leds-add-reset-controller-based-driver.patch | 186 +++ .../patches-5.4/0004-phy-add-ath79-usb-phys.patch | 332 ++++ .../0005-usb-add-more-OF-quirk-properties.patch | 24 + ...-ath79-intc-add-irq-cascade-driver-for-QC.patch | 168 ++ ...irqchip-irq-ath79-cpu-drop-OF-init-helper.patch | 23 + ...1-MIPS-ath79-select-the-PINCTRL-subsystem.patch | 24 + ...bindings-PCI-qcom-ar7100-adds-binding-doc.patch | 57 + .../0018-MIPS-pci-ar71xx-convert-to-OF.patch | 202 +++ ...bindings-PCI-qcom-ar7240-adds-binding-doc.patch | 61 + .../0020-MIPS-pci-ar724x-convert-to-OF.patch | 205 +++ .../0032-MIPS-ath79-sanitize-symbols.patch | 93 ++ .../0033-spi-ath79-drop-pdata-support.patch | 71 + .../0034-MIPS-ath79-ath9k-exports.patch | 27 + .../0036-GPIO-add-named-gpio-exports.patch | 165 ++ .../0036-MIPS-ath79-remove-irq-code-from-pci.patch | 139 ++ .../ath79/patches-5.4/0037-missing-registers.patch | 21 + ...-ath79-add-missing-QCA955x-GMAC-registers.patch | 90 + ...9-MIPS-ath79-export-UART1-reference-clock.patch | 52 + .../004-register_gpio_driver_earlier.patch | 18 + ...-spi-add-driver-for-ar934x-spi-controller.patch | 283 ++++ ...933x_uart-set-UART_CS_-RX-TX-_READY_ORIDE.patch | 64 + .../patches-5.4/404-mtd-cybertan-trx-parser.patch | 24 + .../405-mtd-tp-link-partition-parser.patch | 25 + .../408-mtd-redboot_partition_scan.patch | 44 + .../425-at803x-allow-sgmii-aneg-override.patch | 16 + .../430-drivers-link-spi-before-mtd.patch | 12 + .../patches-5.4/440-mtd-ar934x-nand-driver.patch | 25 + ...MIPS-ath79-swizzle-pci-address-for-ar71xx.patch | 98 ++ .../900-mdio_bitbang_ignore_ta_value.patch | 32 + ...tbang-prevent-rescheduling-during-command.patch | 61 + .../patches-5.4/910-unaligned_access_hacks.patch | 869 ++++++++++ target/linux/ath79/tiny/config-default | 3 + 55 files changed, 6914 insertions(+), 3109 deletions(-) create mode 100644 target/linux/ath79/config-5.4 create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Kconfig create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Makefile create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx.h create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c create mode 100644 target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Kconfig delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c delete mode 100644 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c create mode 100644 target/linux/ath79/patches-5.4/0002-watchdog-ath79-fix-maximum-timeout.patch create mode 100644 target/linux/ath79/patches-5.4/0003-leds-add-reset-controller-based-driver.patch create mode 100644 target/linux/ath79/patches-5.4/0004-phy-add-ath79-usb-phys.patch create mode 100644 target/linux/ath79/patches-5.4/0005-usb-add-more-OF-quirk-properties.patch create mode 100644 target/linux/ath79/patches-5.4/0007-irqchip-irq-ath79-intc-add-irq-cascade-driver-for-QC.patch create mode 100644 target/linux/ath79/patches-5.4/0008-irqchip-irq-ath79-cpu-drop-OF-init-helper.patch create mode 100644 target/linux/ath79/patches-5.4/0011-MIPS-ath79-select-the-PINCTRL-subsystem.patch create mode 100644 target/linux/ath79/patches-5.4/0017-dt-bindings-PCI-qcom-ar7100-adds-binding-doc.patch create mode 100644 target/linux/ath79/patches-5.4/0018-MIPS-pci-ar71xx-convert-to-OF.patch create mode 100644 target/linux/ath79/patches-5.4/0019-dt-bindings-PCI-qcom-ar7240-adds-binding-doc.patch create mode 100644 target/linux/ath79/patches-5.4/0020-MIPS-pci-ar724x-convert-to-OF.patch create mode 100644 target/linux/ath79/patches-5.4/0032-MIPS-ath79-sanitize-symbols.patch create mode 100644 target/linux/ath79/patches-5.4/0033-spi-ath79-drop-pdata-support.patch create mode 100644 target/linux/ath79/patches-5.4/0034-MIPS-ath79-ath9k-exports.patch create mode 100644 target/linux/ath79/patches-5.4/0036-GPIO-add-named-gpio-exports.patch create mode 100644 target/linux/ath79/patches-5.4/0036-MIPS-ath79-remove-irq-code-from-pci.patch create mode 100644 target/linux/ath79/patches-5.4/0037-missing-registers.patch create mode 100644 target/linux/ath79/patches-5.4/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch create mode 100644 target/linux/ath79/patches-5.4/0039-MIPS-ath79-export-UART1-reference-clock.patch create mode 100644 target/linux/ath79/patches-5.4/004-register_gpio_driver_earlier.patch create mode 100644 target/linux/ath79/patches-5.4/0051-spi-add-driver-for-ar934x-spi-controller.patch create mode 100644 target/linux/ath79/patches-5.4/0060-serial-ar933x_uart-set-UART_CS_-RX-TX-_READY_ORIDE.patch create mode 100644 target/linux/ath79/patches-5.4/404-mtd-cybertan-trx-parser.patch create mode 100644 target/linux/ath79/patches-5.4/405-mtd-tp-link-partition-parser.patch create mode 100644 target/linux/ath79/patches-5.4/408-mtd-redboot_partition_scan.patch create mode 100644 target/linux/ath79/patches-5.4/425-at803x-allow-sgmii-aneg-override.patch create mode 100644 target/linux/ath79/patches-5.4/430-drivers-link-spi-before-mtd.patch create mode 100644 target/linux/ath79/patches-5.4/440-mtd-ar934x-nand-driver.patch create mode 100644 target/linux/ath79/patches-5.4/470-MIPS-ath79-swizzle-pci-address-for-ar71xx.patch create mode 100644 target/linux/ath79/patches-5.4/900-mdio_bitbang_ignore_ta_value.patch create mode 100644 target/linux/ath79/patches-5.4/901-phy-mdio-bitbang-prevent-rescheduling-during-command.patch create mode 100644 target/linux/ath79/patches-5.4/910-unaligned_access_hacks.patch (limited to 'target/linux/ath79') diff --git a/target/linux/ath79/Makefile b/target/linux/ath79/Makefile index cfdf8c87e5..e50ad2297b 100644 --- a/target/linux/ath79/Makefile +++ b/target/linux/ath79/Makefile @@ -9,6 +9,7 @@ SUBTARGETS:=generic nand tiny FEATURES:=ramdisk KERNEL_PATCHVER:=4.19 +KERNEL_TESTING_PATCHVER:=5.4 include $(INCLUDE_DIR)/target.mk diff --git a/target/linux/ath79/config-5.4 b/target/linux/ath79/config-5.4 new file mode 100644 index 0000000000..3f7f26a418 --- /dev/null +++ b/target/linux/ath79/config-5.4 @@ -0,0 +1,250 @@ +CONFIG_64BIT_TIME=y +CONFIG_AG71XX=y +CONFIG_AR8216_PHY=y +CONFIG_AR8216_PHY_LEDS=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_ARCH_HAS_DMA_COHERENT_TO_PFN=y +CONFIG_ARCH_HAS_DMA_PREP_COHERENT=y +CONFIG_ARCH_HAS_DMA_WRITE_COMBINE=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y +CONFIG_ARCH_HAS_UNCACHED_SEGMENT=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_ARCH_USE_MEMREMAP_PROT=y +CONFIG_ARCH_USE_QUEUED_RWLOCKS=y +CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y +CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_ATH79=y +CONFIG_ATH79_WDT=y +CONFIG_BLK_MQ_PCI=y +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE="rootfstype=squashfs,jffs2" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_COMMON_CLK=y +# CONFIG_COMMON_CLK_BOSTON is not set +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_LOAD_STORE_LR=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CPU_SUPPORTS_MSA=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CSRC_R4K=y +CONFIG_DMA_DECLARE_COHERENT=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DMA_NONCOHERENT_CACHE_SYNC=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_EFI_EARLYCON=y +CONFIG_ETHERNET_PACKET_MANGLE=y +CONFIG_FIXED_PHY=y +CONFIG_FONT_8x16=y +CONFIG_FONT_AUTOSELECT=y +CONFIG_FONT_SUPPORT=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_74X164=y +CONFIG_GPIO_ATH79=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_SYSFS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAVE_ARCH_COMPILER_H=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_ASM_MODVERSIONS=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_COPY_THREAD_TLS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DEBUG_STACKOVERFLOW=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FAST_GUP=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_GENERIC_VDSO=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_KVM=y +CONFIG_HAVE_LD_DEAD_CODE_DATA_ELIMINATION=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PCI=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_RSEQ=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HZ_PERIODIC=y +CONFIG_IMAGE_CMDLINE_HACK=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +CONFIG_KASAN_STACK=1 +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_RESET is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_GPIO=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND is not set +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MIPS_SPRAM=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +# CONFIG_MTD_CFI_I2 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set +CONFIG_MTD_PARSER_CYBERTAN=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_LZMA_FW=y +CONFIG_MTD_SPLIT_SEAMA_FW=y +CONFIG_MTD_SPLIT_TPLINK_FW=y +CONFIG_MTD_SPLIT_UIMAGE_FW=y +CONFIG_MTD_TPLINK_PARTS=y +CONFIG_MTD_VIRT_CONCAT=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_PCI=y +CONFIG_PCI_AR71XX=y +CONFIG_PCI_AR724X=y +CONFIG_PCI_DISABLE_COMMON_QUIRKS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +# CONFIG_PHY_AR7100_USB is not set +# CONFIG_PHY_AR7200_USB is not set +# CONFIG_PHY_ATH79_USB is not set +CONFIG_PINCTRL=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_ATH79=y +CONFIG_RESET_CONTROLLER=y +CONFIG_SERIAL_8250_NR_UARTS=1 +CONFIG_SERIAL_8250_RUNTIME_UARTS=1 +CONFIG_SERIAL_AR933X=y +CONFIG_SERIAL_AR933X_CONSOLE=y +CONFIG_SERIAL_AR933X_NR_UARTS=2 +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SPI=y +CONFIG_SPI_AR934X=y +CONFIG_SPI_ATH79=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_GPIO=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +# CONFIG_SPI_RB4XX is not set +CONFIG_SRCU=y +CONFIG_SWCONFIG=y +CONFIG_SWCONFIG_LEDS=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=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_MIPS16=y +CONFIG_SYS_SUPPORTS_ZBOOT=y +CONFIG_SYS_SUPPORTS_ZBOOT_UART_PROM=y +CONFIG_TARGET_ISA_REV=2 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TINY_SRCU=y +CONFIG_UBSAN_ALIGNMENT=y +CONFIG_UNIX_SCM=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Kconfig b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Kconfig new file mode 100644 index 0000000000..4df2d21e34 --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Kconfig @@ -0,0 +1,25 @@ +config AG71XX + tristate "Atheros AR7XXX/AR9XXX built-in ethernet mac support" + depends on ATH79 + select PHYLIB + help + If you wish to compile a kernel for AR7XXX/91XXX and enable + ethernet support, then you should always answer Y to this. + +if AG71XX + +config AG71XX_DEBUG + bool "Atheros AR71xx built-in ethernet driver debugging" + default n + help + Atheros AR71xx built-in ethernet driver debugging messages. + +config AG71XX_DEBUG_FS + bool "Atheros AR71xx built-in ethernet driver debugfs support" + depends on DEBUG_FS + default n + help + Say Y, if you need access to various statistics provided by + the ag71xx driver. + +endif diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Makefile b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Makefile new file mode 100644 index 0000000000..87add0d208 --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the Atheros AR71xx built-in ethernet macs +# + +ag71xx-y += ag71xx_main.o +ag71xx-y += ag71xx_gmac.o +ag71xx-y += ag71xx_ethtool.o +ag71xx-y += ag71xx_phy.o + +ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o + +obj-$(CONFIG_AG71XX) += ag71xx_mdio.o +obj-$(CONFIG_AG71XX) += ag71xx.o diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx.h b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx.h new file mode 100644 index 0000000000..fde9db3745 --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx.h @@ -0,0 +1,454 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Based on Atheros' AG7100 driver + * + * 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. + */ + +#ifndef __AG71XX_H +#define __AG71XX_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define AG71XX_DRV_NAME "ag71xx" + +/* + * For our NAPI weight bigger does *NOT* mean better - it means more + * D-cache misses and lots more wasted cycles than we'll ever + * possibly gain from saving instructions. + */ +#define AG71XX_NAPI_WEIGHT 32 +#define AG71XX_OOM_REFILL (1 + HZ/10) + +#define AG71XX_INT_ERR (AG71XX_INT_RX_BE | AG71XX_INT_TX_BE) +#define AG71XX_INT_TX (AG71XX_INT_TX_PS) +#define AG71XX_INT_RX (AG71XX_INT_RX_PR | AG71XX_INT_RX_OF) + +#define AG71XX_INT_POLL (AG71XX_INT_RX | AG71XX_INT_TX) +#define AG71XX_INT_INIT (AG71XX_INT_ERR | AG71XX_INT_POLL) + +#define AG71XX_TX_MTU_LEN 1540 + +#define AG71XX_TX_RING_SPLIT 512 +#define AG71XX_TX_RING_DS_PER_PKT DIV_ROUND_UP(AG71XX_TX_MTU_LEN, \ + AG71XX_TX_RING_SPLIT) +#define AG71XX_TX_RING_SIZE_DEFAULT 128 +#define AG71XX_RX_RING_SIZE_DEFAULT 256 + +#define AG71XX_TX_RING_SIZE_MAX 128 +#define AG71XX_RX_RING_SIZE_MAX 256 + +#ifdef CONFIG_AG71XX_DEBUG +#define DBG(fmt, args...) pr_debug(fmt, ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#define ag71xx_assert(_cond) \ +do { \ + if (_cond) \ + break; \ + printk("%s,%d: assertion failed\n", __FILE__, __LINE__); \ + BUG(); \ +} while (0) + +struct ag71xx_desc { + u32 data; + u32 ctrl; +#define DESC_EMPTY BIT(31) +#define DESC_MORE BIT(24) +#define DESC_PKTLEN_M 0xfff + u32 next; + u32 pad; +} __attribute__((aligned(4))); + +#define AG71XX_DESC_SIZE roundup(sizeof(struct ag71xx_desc), \ + L1_CACHE_BYTES) + +struct ag71xx_buf { + union { + struct sk_buff *skb; + void *rx_buf; + }; + union { + dma_addr_t dma_addr; + unsigned int len; + }; +}; + +struct ag71xx_ring { + struct ag71xx_buf *buf; + u8 *descs_cpu; + dma_addr_t descs_dma; + u16 desc_split; + u16 order; + unsigned int curr; + unsigned int dirty; +}; + +struct ag71xx_int_stats { + unsigned long rx_pr; + unsigned long rx_be; + unsigned long rx_of; + unsigned long tx_ps; + unsigned long tx_be; + unsigned long tx_ur; + unsigned long total; +}; + +struct ag71xx_napi_stats { + unsigned long napi_calls; + unsigned long rx_count; + unsigned long rx_packets; + unsigned long rx_packets_max; + unsigned long tx_count; + unsigned long tx_packets; + unsigned long tx_packets_max; + + unsigned long rx[AG71XX_NAPI_WEIGHT + 1]; + unsigned long tx[AG71XX_NAPI_WEIGHT + 1]; +}; + +struct ag71xx_debug { + struct dentry *debugfs_dir; + + struct ag71xx_int_stats int_stats; + struct ag71xx_napi_stats napi_stats; +}; + +struct ag71xx { + /* + * Critical data related to the per-packet data path are clustered + * early in this structure to help improve the D-cache footprint. + */ + struct ag71xx_ring rx_ring ____cacheline_aligned; + struct ag71xx_ring tx_ring ____cacheline_aligned; + + int mac_idx; + + u16 desc_pktlen_mask; + u16 rx_buf_size; + u8 rx_buf_offset; + u8 tx_hang_workaround:1; + + struct net_device *dev; + struct platform_device *pdev; + spinlock_t lock; + struct napi_struct napi; + u32 msg_enable; + + /* + * From this point onwards we're not looking at per-packet fields. + */ + void __iomem *mac_base; + void __iomem *mii_base; + + struct ag71xx_desc *stop_desc; + dma_addr_t stop_desc_dma; + + struct phy_device *phy_dev; + void *phy_priv; + int phy_if_mode; + + unsigned int link; + unsigned int speed; + int duplex; + + struct delayed_work restart_work; + struct timer_list oom_timer; + + struct reset_control *mac_reset; + struct reset_control *mdio_reset; + + u32 fifodata[3]; + u32 plldata[3]; + u32 pllreg[3]; + struct regmap *pllregmap; + +#ifdef CONFIG_AG71XX_DEBUG_FS + struct ag71xx_debug debug; +#endif +}; + +struct ag71xx_mdio { + struct reset_control *mdio_reset; + struct mii_bus *mii_bus; + struct regmap *mii_regmap; +}; + +extern struct ethtool_ops ag71xx_ethtool_ops; +void ag71xx_link_adjust(struct ag71xx *ag); + +int ag71xx_phy_connect(struct ag71xx *ag); +void ag71xx_phy_disconnect(struct ag71xx *ag); + +static inline int ag71xx_desc_empty(struct ag71xx_desc *desc) +{ + return (desc->ctrl & DESC_EMPTY) != 0; +} + +static inline struct ag71xx_desc * +ag71xx_ring_desc(struct ag71xx_ring *ring, int idx) +{ + return (struct ag71xx_desc *) &ring->descs_cpu[idx * AG71XX_DESC_SIZE]; +} + +static inline int +ag71xx_ring_size_order(int size) +{ + return fls(size - 1); +} + +/* Register offsets */ +#define AG71XX_REG_MAC_CFG1 0x0000 +#define AG71XX_REG_MAC_CFG2 0x0004 +#define AG71XX_REG_MAC_IPG 0x0008 +#define AG71XX_REG_MAC_HDX 0x000c +#define AG71XX_REG_MAC_MFL 0x0010 +#define AG71XX_REG_MII_CFG 0x0020 +#define AG71XX_REG_MII_CMD 0x0024 +#define AG71XX_REG_MII_ADDR 0x0028 +#define AG71XX_REG_MII_CTRL 0x002c +#define AG71XX_REG_MII_STATUS 0x0030 +#define AG71XX_REG_MII_IND 0x0034 +#define AG71XX_REG_MAC_IFCTL 0x0038 +#define AG71XX_REG_MAC_ADDR1 0x0040 +#define AG71XX_REG_MAC_ADDR2 0x0044 +#define AG71XX_REG_FIFO_CFG0 0x0048 +#define AG71XX_REG_FIFO_CFG1 0x004c +#define AG71XX_REG_FIFO_CFG2 0x0050 +#define AG71XX_REG_FIFO_CFG3 0x0054 +#define AG71XX_REG_FIFO_CFG4 0x0058 +#define AG71XX_REG_FIFO_CFG5 0x005c +#define AG71XX_REG_FIFO_RAM0 0x0060 +#define AG71XX_REG_FIFO_RAM1 0x0064 +#define AG71XX_REG_FIFO_RAM2 0x0068 +#define AG71XX_REG_FIFO_RAM3 0x006c +#define AG71XX_REG_FIFO_RAM4 0x0070 +#define AG71XX_REG_FIFO_RAM5 0x0074 +#define AG71XX_REG_FIFO_RAM6 0x0078 +#define AG71XX_REG_FIFO_RAM7 0x007c + +#define AG71XX_REG_TX_CTRL 0x0180 +#define AG71XX_REG_TX_DESC 0x0184 +#define AG71XX_REG_TX_STATUS 0x0188 +#define AG71XX_REG_RX_CTRL 0x018c +#define AG71XX_REG_RX_DESC 0x0190 +#define AG71XX_REG_RX_STATUS 0x0194 +#define AG71XX_REG_INT_ENABLE 0x0198 +#define AG71XX_REG_INT_STATUS 0x019c + +#define AG71XX_REG_FIFO_DEPTH 0x01a8 +#define AG71XX_REG_RX_SM 0x01b0 +#define AG71XX_REG_TX_SM 0x01b4 + +#define MAC_CFG1_TXE BIT(0) /* Tx Enable */ +#define MAC_CFG1_STX BIT(1) /* Synchronize Tx Enable */ +#define MAC_CFG1_RXE BIT(2) /* Rx Enable */ +#define MAC_CFG1_SRX BIT(3) /* Synchronize Rx Enable */ +#define MAC_CFG1_TFC BIT(4) /* Tx Flow Control Enable */ +#define MAC_CFG1_RFC BIT(5) /* Rx Flow Control Enable */ +#define MAC_CFG1_LB BIT(8) /* Loopback mode */ +#define MAC_CFG1_SR BIT(31) /* Soft Reset */ + +#define MAC_CFG2_FDX BIT(0) +#define MAC_CFG2_CRC_EN BIT(1) +#define MAC_CFG2_PAD_CRC_EN BIT(2) +#define MAC_CFG2_LEN_CHECK BIT(4) +#define MAC_CFG2_HUGE_FRAME_EN BIT(5) +#define MAC_CFG2_IF_1000 BIT(9) +#define MAC_CFG2_IF_10_100 BIT(8) + +#define FIFO_CFG0_WTM BIT(0) /* Watermark Module */ +#define FIFO_CFG0_RXS BIT(1) /* Rx System Module */ +#define FIFO_CFG0_RXF BIT(2) /* Rx Fabric Module */ +#define FIFO_CFG0_TXS BIT(3) /* Tx System Module */ +#define FIFO_CFG0_TXF BIT(4) /* Tx Fabric Module */ +#define FIFO_CFG0_ALL (FIFO_CFG0_WTM | FIFO_CFG0_RXS | FIFO_CFG0_RXF \ + | FIFO_CFG0_TXS | FIFO_CFG0_TXF) + +#define FIFO_CFG0_ENABLE_SHIFT 8 + +#define FIFO_CFG4_DE BIT(0) /* Drop Event */ +#define FIFO_CFG4_DV BIT(1) /* RX_DV Event */ +#define FIFO_CFG4_FC BIT(2) /* False Carrier */ +#define FIFO_CFG4_CE BIT(3) /* Code Error */ +#define FIFO_CFG4_CR BIT(4) /* CRC error */ +#define FIFO_CFG4_LM BIT(5) /* Length Mismatch */ +#define FIFO_CFG4_LO BIT(6) /* Length out of range */ +#define FIFO_CFG4_OK BIT(7) /* Packet is OK */ +#define FIFO_CFG4_MC BIT(8) /* Multicast Packet */ +#define FIFO_CFG4_BC BIT(9) /* Broadcast Packet */ +#define FIFO_CFG4_DR BIT(10) /* Dribble */ +#define FIFO_CFG4_LE BIT(11) /* Long Event */ +#define FIFO_CFG4_CF BIT(12) /* Control Frame */ +#define FIFO_CFG4_PF BIT(13) /* Pause Frame */ +#define FIFO_CFG4_UO BIT(14) /* Unsupported Opcode */ +#define FIFO_CFG4_VT BIT(15) /* VLAN tag detected */ +#define FIFO_CFG4_FT BIT(16) /* Frame Truncated */ +#define FIFO_CFG4_UC BIT(17) /* Unicast Packet */ + +#define FIFO_CFG5_DE BIT(0) /* Drop Event */ +#define FIFO_CFG5_DV BIT(1) /* RX_DV Event */ +#define FIFO_CFG5_FC BIT(2) /* False Carrier */ +#define FIFO_CFG5_CE BIT(3) /* Code Error */ +#define FIFO_CFG5_LM BIT(4) /* Length Mismatch */ +#define FIFO_CFG5_LO BIT(5) /* Length Out of Range */ +#define FIFO_CFG5_OK BIT(6) /* Packet is OK */ +#define FIFO_CFG5_MC BIT(7) /* Multicast Packet */ +#define FIFO_CFG5_BC BIT(8) /* Broadcast Packet */ +#define FIFO_CFG5_DR BIT(9) /* Dribble */ +#define FIFO_CFG5_CF BIT(10) /* Control Frame */ +#define FIFO_CFG5_PF BIT(11) /* Pause Frame */ +#define FIFO_CFG5_UO BIT(12) /* Unsupported Opcode */ +#define FIFO_CFG5_VT BIT(13) /* VLAN tag detected */ +#define FIFO_CFG5_LE BIT(14) /* Long Event */ +#define FIFO_CFG5_FT BIT(15) /* Frame Truncated */ +#define FIFO_CFG5_16 BIT(16) /* unknown */ +#define FIFO_CFG5_17 BIT(17) /* unknown */ +#define FIFO_CFG5_SF BIT(18) /* Short Frame */ +#define FIFO_CFG5_BM BIT(19) /* Byte Mode */ + +#define AG71XX_INT_TX_PS BIT(0) +#define AG71XX_INT_TX_UR BIT(1) +#define AG71XX_INT_TX_BE BIT(3) +#define AG71XX_INT_RX_PR BIT(4) +#define AG71XX_INT_RX_OF BIT(6) +#define AG71XX_INT_RX_BE BIT(7) + +#define MAC_IFCTL_SPEED BIT(16) + +#define MII_CFG_CLK_DIV_4 0 +#define MII_CFG_CLK_DIV_6 2 +#define MII_CFG_CLK_DIV_8 3 +#define MII_CFG_CLK_DIV_10 4 +#define MII_CFG_CLK_DIV_14 5 +#define MII_CFG_CLK_DIV_20 6 +#define MII_CFG_CLK_DIV_28 7 +#define MII_CFG_CLK_DIV_34 8 +#define MII_CFG_CLK_DIV_42 9 +#define MII_CFG_CLK_DIV_50 10 +#define MII_CFG_CLK_DIV_58 11 +#define MII_CFG_CLK_DIV_66 12 +#define MII_CFG_CLK_DIV_74 13 +#define MII_CFG_CLK_DIV_82 14 +#define MII_CFG_CLK_DIV_98 15 +#define MII_CFG_RESET BIT(31) + +#define MII_CMD_WRITE 0x0 +#define MII_CMD_READ 0x1 +#define MII_ADDR_SHIFT 8 +#define MII_IND_BUSY BIT(0) +#define MII_IND_INVALID BIT(2) + +#define TX_CTRL_TXE BIT(0) /* Tx Enable */ + +#define TX_STATUS_PS BIT(0) /* Packet Sent */ +#define TX_STATUS_UR BIT(1) /* Tx Underrun */ +#define TX_STATUS_BE BIT(3) /* Bus Error */ + +#define RX_CTRL_RXE BIT(0) /* Rx Enable */ + +#define RX_STATUS_PR BIT(0) /* Packet Received */ +#define RX_STATUS_OF BIT(2) /* Rx Overflow */ +#define RX_STATUS_BE BIT(3) /* Bus Error */ + +static inline void ag71xx_wr(struct ag71xx *ag, unsigned reg, u32 value) +{ + __raw_writel(value, ag->mac_base + reg); + /* flush write */ + (void) __raw_readl(ag->mac_base + reg); +} + +static inline u32 ag71xx_rr(struct ag71xx *ag, unsigned reg) +{ + return __raw_readl(ag->mac_base + reg); +} + +static inline void ag71xx_sb(struct ag71xx *ag, unsigned reg, u32 mask) +{ + void __iomem *r; + + r = ag->mac_base + reg; + __raw_writel(__raw_readl(r) | mask, r); + /* flush write */ + (void) __raw_readl(r); +} + +static inline void ag71xx_cb(struct ag71xx *ag, unsigned reg, u32 mask) +{ + void __iomem *r; + + r = ag->mac_base + reg; + __raw_writel(__raw_readl(r) & ~mask, r); + /* flush write */ + (void) __raw_readl(r); +} + +static inline void ag71xx_int_enable(struct ag71xx *ag, u32 ints) +{ + ag71xx_sb(ag, AG71XX_REG_INT_ENABLE, ints); +} + +static inline void ag71xx_int_disable(struct ag71xx *ag, u32 ints) +{ + ag71xx_cb(ag, AG71XX_REG_INT_ENABLE, ints); +} + +#ifdef CONFIG_AG71XX_DEBUG_FS +int ag71xx_debugfs_root_init(void); +void ag71xx_debugfs_root_exit(void); +int ag71xx_debugfs_init(struct ag71xx *ag); +void ag71xx_debugfs_exit(struct ag71xx *ag); +void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, u32 status); +void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, int rx, int tx); +#else +static inline int ag71xx_debugfs_root_init(void) { return 0; } +static inline void ag71xx_debugfs_root_exit(void) {} +static inline int ag71xx_debugfs_init(struct ag71xx *ag) { return 0; } +static inline void ag71xx_debugfs_exit(struct ag71xx *ag) {} +static inline void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, + u32 status) {} +static inline void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, + int rx, int tx) {} +#endif /* CONFIG_AG71XX_DEBUG_FS */ + +int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np); +void ag71xx_ar7240_cleanup(struct ag71xx *ag); + +int ag71xx_setup_gmac(struct device_node *np); + +int ar7240sw_phy_read(struct mii_bus *mii, int addr, int reg); +int ar7240sw_phy_write(struct mii_bus *mii, int addr, int reg, u16 val); + +#endif /* _AG71XX_H */ diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c new file mode 100644 index 0000000000..20cf1c15c8 --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c @@ -0,0 +1,285 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Based on Atheros' AG7100 driver + * + * 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 "ag71xx.h" + +static struct dentry *ag71xx_debugfs_root; + +static int ag71xx_debugfs_generic_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, u32 status) +{ + if (status) + ag->debug.int_stats.total++; + if (status & AG71XX_INT_TX_PS) + ag->debug.int_stats.tx_ps++; + if (status & AG71XX_INT_TX_UR) + ag->debug.int_stats.tx_ur++; + if (status & AG71XX_INT_TX_BE) + ag->debug.int_stats.tx_be++; + if (status & AG71XX_INT_RX_PR) + ag->debug.int_stats.rx_pr++; + if (status & AG71XX_INT_RX_OF) + ag->debug.int_stats.rx_of++; + if (status & AG71XX_INT_RX_BE) + ag->debug.int_stats.rx_be++; +} + +static ssize_t read_file_int_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define PR_INT_STAT(_label, _field) \ + len += snprintf(buf + len, sizeof(buf) - len, \ + "%20s: %10lu\n", _label, ag->debug.int_stats._field); + + struct ag71xx *ag = file->private_data; + char buf[256]; + unsigned int len = 0; + + PR_INT_STAT("TX Packet Sent", tx_ps); + PR_INT_STAT("TX Underrun", tx_ur); + PR_INT_STAT("TX Bus Error", tx_be); + PR_INT_STAT("RX Packet Received", rx_pr); + PR_INT_STAT("RX Overflow", rx_of); + PR_INT_STAT("RX Bus Error", rx_be); + len += snprintf(buf + len, sizeof(buf) - len, "\n"); + PR_INT_STAT("Total", total); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +#undef PR_INT_STAT +} + +static const struct file_operations ag71xx_fops_int_stats = { + .open = ag71xx_debugfs_generic_open, + .read = read_file_int_stats, + .owner = THIS_MODULE +}; + +void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, int rx, int tx) +{ + struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; + + if (rx) { + stats->rx_count++; + stats->rx_packets += rx; + if (rx <= AG71XX_NAPI_WEIGHT) + stats->rx[rx]++; + if (rx > stats->rx_packets_max) + stats->rx_packets_max = rx; + } + + if (tx) { + stats->tx_count++; + stats->tx_packets += tx; + if (tx <= AG71XX_NAPI_WEIGHT) + stats->tx[tx]++; + if (tx > stats->tx_packets_max) + stats->tx_packets_max = tx; + } +} + +static ssize_t read_file_napi_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ag71xx *ag = file->private_data; + struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; + char *buf; + unsigned int buflen; + unsigned int len = 0; + unsigned long rx_avg = 0; + unsigned long tx_avg = 0; + int ret; + int i; + + buflen = 2048; + buf = kmalloc(buflen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (stats->rx_count) + rx_avg = stats->rx_packets / stats->rx_count; + + if (stats->tx_count) + tx_avg = stats->tx_packets / stats->tx_count; + + len += snprintf(buf + len, buflen - len, "%3s %10s %10s\n", + "len", "rx", "tx"); + + for (i = 1; i <= AG71XX_NAPI_WEIGHT; i++) + len += snprintf(buf + len, buflen - len, + "%3d: %10lu %10lu\n", + i, stats->rx[i], stats->tx[i]); + + len += snprintf(buf + len, buflen - len, "\n"); + + len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", + "sum", stats->rx_count, stats->tx_count); + len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", + "avg", rx_avg, tx_avg); + len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", + "max", stats->rx_packets_max, stats->tx_packets_max); + len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", + "pkt", stats->rx_packets, stats->tx_packets); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return ret; +} + +static const struct file_operations ag71xx_fops_napi_stats = { + .open = ag71xx_debugfs_generic_open, + .read = read_file_napi_stats, + .owner = THIS_MODULE +}; + +#define DESC_PRINT_LEN 64 + +static ssize_t read_file_ring(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos, + struct ag71xx *ag, + struct ag71xx_ring *ring, + unsigned desc_reg) +{ + int ring_size = BIT(ring->order); + int ring_mask = ring_size - 1; + char *buf; + unsigned int buflen; + unsigned int len = 0; + unsigned long flags; + ssize_t ret; + int curr; + int dirty; + u32 desc_hw; + int i; + + buflen = (ring_size * DESC_PRINT_LEN); + buf = kmalloc(buflen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += snprintf(buf + len, buflen - len, + "Idx ... %-8s %-8s %-8s %-8s .\n", + "desc", "next", "data", "ctrl"); + + spin_lock_irqsave(&ag->lock, flags); + + curr = (ring->curr & ring_mask); + dirty = (ring->dirty & ring_mask); + desc_hw = ag71xx_rr(ag, desc_reg); + for (i = 0; i < ring_size; i++) { + struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); + u32 desc_dma = ((u32) ring->descs_dma) + i * AG71XX_DESC_SIZE; + + len += snprintf(buf + len, buflen - len, + "%3d %c%c%c %08x %08x %08x %08x %c\n", + i, + (i == curr) ? 'C' : ' ', + (i == dirty) ? 'D' : ' ', + (desc_hw == desc_dma) ? 'H' : ' ', + desc_dma, + desc->next, + desc->data, + desc->ctrl, + (desc->ctrl & DESC_EMPTY) ? 'E' : '*'); + } + + spin_unlock_irqrestore(&ag->lock, flags); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return ret; +} + +static ssize_t read_file_tx_ring(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ag71xx *ag = file->private_data; + + return read_file_ring(file, user_buf, count, ppos, ag, &ag->tx_ring, + AG71XX_REG_TX_DESC); +} + +static const struct file_operations ag71xx_fops_tx_ring = { + .open = ag71xx_debugfs_generic_open, + .read = read_file_tx_ring, + .owner = THIS_MODULE +}; + +static ssize_t read_file_rx_ring(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ag71xx *ag = file->private_data; + + return read_file_ring(file, user_buf, count, ppos, ag, &ag->rx_ring, + AG71XX_REG_RX_DESC); +} + +static const struct file_operations ag71xx_fops_rx_ring = { + .open = ag71xx_debugfs_generic_open, + .read = read_file_rx_ring, + .owner = THIS_MODULE +}; + +void ag71xx_debugfs_exit(struct ag71xx *ag) +{ + debugfs_remove_recursive(ag->debug.debugfs_dir); +} + +int ag71xx_debugfs_init(struct ag71xx *ag) +{ + struct device *dev = &ag->pdev->dev; + + ag->debug.debugfs_dir = debugfs_create_dir(dev_name(dev), + ag71xx_debugfs_root); + if (!ag->debug.debugfs_dir) { + dev_err(dev, "unable to create debugfs directory\n"); + return -ENOENT; + } + + debugfs_create_file("int_stats", S_IRUGO, ag->debug.debugfs_dir, + ag, &ag71xx_fops_int_stats); + debugfs_create_file("napi_stats", S_IRUGO, ag->debug.debugfs_dir, + ag, &ag71xx_fops_napi_stats); + debugfs_create_file("tx_ring", S_IRUGO, ag->debug.debugfs_dir, + ag, &ag71xx_fops_tx_ring); + debugfs_create_file("rx_ring", S_IRUGO, ag->debug.debugfs_dir, + ag, &ag71xx_fops_rx_ring); + + return 0; +} + +int ag71xx_debugfs_root_init(void) +{ + if (ag71xx_debugfs_root) + return -EBUSY; + + ag71xx_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!ag71xx_debugfs_root) + return -ENOENT; + + return 0; +} + +void ag71xx_debugfs_root_exit(void) +{ + debugfs_remove(ag71xx_debugfs_root); + ag71xx_debugfs_root = NULL; +} diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c new file mode 100644 index 0000000000..2cd7b1be83 --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c @@ -0,0 +1,108 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Based on Atheros' AG7100 driver + * + * 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 "ag71xx.h" + +static u32 ag71xx_ethtool_get_msglevel(struct net_device *dev) +{ + struct ag71xx *ag = netdev_priv(dev); + + return ag->msg_enable; +} + +static void ag71xx_ethtool_set_msglevel(struct net_device *dev, u32 msg_level) +{ + struct ag71xx *ag = netdev_priv(dev); + + ag->msg_enable = msg_level; +} + +static void ag71xx_ethtool_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *er) +{ + struct ag71xx *ag = netdev_priv(dev); + + er->tx_max_pending = AG71XX_TX_RING_SIZE_MAX; + er->rx_max_pending = AG71XX_RX_RING_SIZE_MAX; + er->rx_mini_max_pending = 0; + er->rx_jumbo_max_pending = 0; + + er->tx_pending = BIT(ag->tx_ring.order); + er->rx_pending = BIT(ag->rx_ring.order); + er->rx_mini_pending = 0; + er->rx_jumbo_pending = 0; + + if (ag->tx_ring.desc_split) + er->tx_pending /= AG71XX_TX_RING_DS_PER_PKT; +} + +static int ag71xx_ethtool_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *er) +{ + struct ag71xx *ag = netdev_priv(dev); + unsigned tx_size; + unsigned rx_size; + int err = 0; + + if (er->rx_mini_pending != 0|| + er->rx_jumbo_pending != 0 || + er->rx_pending == 0 || + er->tx_pending == 0) + return -EINVAL; + + tx_size = er->tx_pending < AG71XX_TX_RING_SIZE_MAX ? + er->tx_pending : AG71XX_TX_RING_SIZE_MAX; + + rx_size = er->rx_pending < AG71XX_RX_RING_SIZE_MAX ? + er->rx_pending : AG71XX_RX_RING_SIZE_MAX; + + if (netif_running(dev)) { + err = dev->netdev_ops->ndo_stop(dev); + if (err) + return err; + } + + if (ag->tx_ring.desc_split) + tx_size *= AG71XX_TX_RING_DS_PER_PKT; + + ag->tx_ring.order = ag71xx_ring_size_order(tx_size); + ag->rx_ring.order = ag71xx_ring_size_order(rx_size); + + if (netif_running(dev)) + err = dev->netdev_ops->ndo_open(dev); + + return err; +} + +static int ag71xx_ethtool_nway_reset(struct net_device *dev) +{ + struct ag71xx *ag = netdev_priv(dev); + struct phy_device *phydev = ag->phy_dev; + + if (!phydev) + return -ENODEV; + + return genphy_restart_aneg(phydev); +} + +struct ethtool_ops ag71xx_ethtool_ops = { + .get_msglevel = ag71xx_ethtool_get_msglevel, + .set_msglevel = ag71xx_ethtool_set_msglevel, + .get_ringparam = ag71xx_ethtool_get_ringparam, + .set_ringparam = ag71xx_ethtool_set_ringparam, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_link = ethtool_op_get_link, + .get_ts_info = ethtool_op_get_ts_info, + .nway_reset = ag71xx_ethtool_nway_reset, +}; diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c new file mode 100644 index 0000000000..cc0a15d3a4 --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c @@ -0,0 +1,135 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * 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 "ag71xx.h" + +static void ag71xx_of_set(struct device_node *np, const char *prop, + u32 *reg, u32 shift, u32 mask) +{ + u32 val; + + if (of_property_read_u32(np, prop, &val)) + return; + + *reg &= ~(mask << shift); + *reg |= ((val & mask) << shift); +} + +static void ag71xx_of_bit(struct device_node *np, const char *prop, + u32 *reg, u32 mask) +{ + u32 val; + + if (of_property_read_u32(np, prop, &val)) + return; + + if (val) + *reg |= mask; + else + *reg &= ~mask; +} + +static void ag71xx_setup_gmac_933x(struct device_node *np, void __iomem *base) +{ + u32 val = __raw_readl(base + AR933X_GMAC_REG_ETH_CFG); + + ag71xx_of_bit(np, "switch-phy-swap", &val, AR933X_ETH_CFG_SW_PHY_SWAP); + ag71xx_of_bit(np, "switch-phy-addr-swap", &val, + AR933X_ETH_CFG_SW_PHY_ADDR_SWAP); + + __raw_writel(val, base + AR933X_GMAC_REG_ETH_CFG); +} + +static void ag71xx_setup_gmac_934x(struct device_node *np, void __iomem *base) +{ + u32 val = __raw_readl(base + AR934X_GMAC_REG_ETH_CFG); + + ag71xx_of_bit(np, "rgmii-gmac0", &val, AR934X_ETH_CFG_RGMII_GMAC0); + ag71xx_of_bit(np, "mii-gmac0", &val, AR934X_ETH_CFG_MII_GMAC0); + ag71xx_of_bit(np, "mii-gmac0-slave", &val, AR934X_ETH_CFG_MII_GMAC0_SLAVE); + ag71xx_of_bit(np, "gmii-gmac0", &val, AR934X_ETH_CFG_GMII_GMAC0); + ag71xx_of_bit(np, "switch-phy-swap", &val, AR934X_ETH_CFG_SW_PHY_SWAP); + ag71xx_of_bit(np, "switch-only-mode", &val, + AR934X_ETH_CFG_SW_ONLY_MODE); + ag71xx_of_set(np, "rxdv-delay", &val, + AR934X_ETH_CFG_RDV_DELAY_SHIFT, 0x3); + ag71xx_of_set(np, "rxd-delay", &val, + AR934X_ETH_CFG_RXD_DELAY_SHIFT, 0x3); + ag71xx_of_set(np, "txd-delay", &val, + AR934X_ETH_CFG_TXD_DELAY_SHIFT, 0x3); + ag71xx_of_set(np, "txen-delay", &val, + AR934X_ETH_CFG_TXE_DELAY_SHIFT, 0x3); + + __raw_writel(val, base + AR934X_GMAC_REG_ETH_CFG); +} + +static void ag71xx_setup_gmac_955x(struct device_node *np, void __iomem *base) +{ + u32 val = __raw_readl(base + QCA955X_GMAC_REG_ETH_CFG); + + ag71xx_of_bit(np, "rgmii-enabled", &val, QCA955X_ETH_CFG_RGMII_EN); + ag71xx_of_bit(np, "ge0-sgmii", &val, QCA955X_ETH_CFG_GE0_SGMII); + ag71xx_of_set(np, "txen-delay", &val, QCA955X_ETH_CFG_TXE_DELAY_SHIFT, 0x3); + ag71xx_of_set(np, "txd-delay", &val, QCA955X_ETH_CFG_TXD_DELAY_SHIFT, 0x3); + ag71xx_of_set(np, "rxdv-delay", &val, QCA955X_ETH_CFG_RDV_DELAY_SHIFT, 0x3); + ag71xx_of_set(np, "rxd-delay", &val, QCA955X_ETH_CFG_RXD_DELAY_SHIFT, 0x3); + + __raw_writel(val, base + QCA955X_GMAC_REG_ETH_CFG); +} + +static void ag71xx_setup_gmac_956x(struct device_node *np, void __iomem *base) +{ + u32 val = __raw_readl(base + QCA956X_GMAC_REG_ETH_CFG); + + ag71xx_of_bit(np, "switch-phy-swap", &val, QCA956X_ETH_CFG_SW_PHY_SWAP); + ag71xx_of_bit(np, "switch-phy-addr-swap", &val, + QCA956X_ETH_CFG_SW_PHY_ADDR_SWAP); + + __raw_writel(val, base + QCA956X_GMAC_REG_ETH_CFG); +} + +int ag71xx_setup_gmac(struct device_node *np) +{ + struct device_node *np_dev; + void __iomem *base; + int err = 0; + + np = of_get_child_by_name(np, "gmac-config"); + if (!np) + return 0; + + np_dev = of_parse_phandle(np, "device", 0); + if (!np_dev) + goto out; + + base = of_iomap(np_dev, 0); + if (!base) { + pr_err("%pOF: can't map GMAC registers\n", np_dev); + err = -ENOMEM; + goto err_iomap; + } + + if (of_device_is_compatible(np_dev, "qca,ar9330-gmac")) + ag71xx_setup_gmac_933x(np, base); + else if (of_device_is_compatible(np_dev, "qca,ar9340-gmac")) + ag71xx_setup_gmac_934x(np, base); + else if (of_device_is_compatible(np_dev, "qca,qca9550-gmac")) + ag71xx_setup_gmac_955x(np, base); + else if (of_device_is_compatible(np_dev, "qca,qca9560-gmac")) + ag71xx_setup_gmac_956x(np, base); + + iounmap(base); + +err_iomap: + of_node_put(np_dev); +out: + of_node_put(np); + return err; +} diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c new file mode 100644 index 0000000000..2394ccc90d --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c @@ -0,0 +1,1736 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Based on Atheros' AG7100 driver + * + * 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 "ag71xx.h" + +#define AG71XX_DEFAULT_MSG_ENABLE \ + (NETIF_MSG_DRV \ + | NETIF_MSG_PROBE \ + | NETIF_MSG_LINK \ + | NETIF_MSG_TIMER \ + | NETIF_MSG_IFDOWN \ + | NETIF_MSG_IFUP \ + | NETIF_MSG_RX_ERR \ + | NETIF_MSG_TX_ERR) + +static int ag71xx_msg_level = -1; + +module_param_named(msg_level, ag71xx_msg_level, int, 0); +MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)"); + +#define ETH_SWITCH_HEADER_LEN 2 + +static int ag71xx_tx_packets(struct ag71xx *ag, bool flush); + +static inline unsigned int ag71xx_max_frame_len(unsigned int mtu) +{ + return ETH_SWITCH_HEADER_LEN + ETH_HLEN + VLAN_HLEN + mtu + ETH_FCS_LEN; +} + +static void ag71xx_dump_dma_regs(struct ag71xx *ag) +{ + DBG("%s: dma_tx_ctrl=%08x, dma_tx_desc=%08x, dma_tx_status=%08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_TX_CTRL), + ag71xx_rr(ag, AG71XX_REG_TX_DESC), + ag71xx_rr(ag, AG71XX_REG_TX_STATUS)); + + DBG("%s: dma_rx_ctrl=%08x, dma_rx_desc=%08x, dma_rx_status=%08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_RX_CTRL), + ag71xx_rr(ag, AG71XX_REG_RX_DESC), + ag71xx_rr(ag, AG71XX_REG_RX_STATUS)); +} + +static void ag71xx_dump_regs(struct ag71xx *ag) +{ + DBG("%s: mac_cfg1=%08x, mac_cfg2=%08x, ipg=%08x, hdx=%08x, mfl=%08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_MAC_CFG1), + ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), + ag71xx_rr(ag, AG71XX_REG_MAC_IPG), + ag71xx_rr(ag, AG71XX_REG_MAC_HDX), + ag71xx_rr(ag, AG71XX_REG_MAC_MFL)); + DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL), + ag71xx_rr(ag, AG71XX_REG_MAC_ADDR1), + ag71xx_rr(ag, AG71XX_REG_MAC_ADDR2)); + DBG("%s: fifo_cfg0=%08x, fifo_cfg1=%08x, fifo_cfg2=%08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); + DBG("%s: fifo_cfg3=%08x, fifo_cfg4=%08x, fifo_cfg5=%08x\n", + ag->dev->name, + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), + ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); +} + +static inline void ag71xx_dump_intr(struct ag71xx *ag, char *label, u32 intr) +{ + DBG("%s: %s intr=%08x %s%s%s%s%s%s\n", + ag->dev->name, label, intr, + (intr & AG71XX_INT_TX_PS) ? "TXPS " : "", + (intr & AG71XX_INT_TX_UR) ? "TXUR " : "", + (intr & AG71XX_INT_TX_BE) ? "TXBE " : "", + (intr & AG71XX_INT_RX_PR) ? "RXPR " : "", + (intr & AG71XX_INT_RX_OF) ? "RXOF " : "", + (intr & AG71XX_INT_RX_BE) ? "RXBE " : ""); +} + +static void ag71xx_ring_tx_clean(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->tx_ring; + struct net_device *dev = ag->dev; + int ring_mask = BIT(ring->order) - 1; + u32 bytes_compl = 0, pkts_compl = 0; + + while (ring->curr != ring->dirty) { + struct ag71xx_desc *desc; + u32 i = ring->dirty & ring_mask; + + desc = ag71xx_ring_desc(ring, i); + if (!ag71xx_desc_empty(desc)) { + desc->ctrl = 0; + dev->stats.tx_errors++; + } + + if (ring->buf[i].skb) { + bytes_compl += ring->buf[i].len; + pkts_compl++; + dev_kfree_skb_any(ring->buf[i].skb); + } + ring->buf[i].skb = NULL; + ring->dirty++; + } + + /* flush descriptors */ + wmb(); + + netdev_completed_queue(dev, pkts_compl, bytes_compl); +} + +static void ag71xx_ring_tx_init(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->tx_ring; + int ring_size = BIT(ring->order); + int ring_mask = BIT(ring->order) - 1; + int i; + + for (i = 0; i < ring_size; i++) { + struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); + + desc->next = (u32) (ring->descs_dma + + AG71XX_DESC_SIZE * ((i + 1) & ring_mask)); + + desc->ctrl = DESC_EMPTY; + ring->buf[i].skb = NULL; + } + + /* flush descriptors */ + wmb(); + + ring->curr = 0; + ring->dirty = 0; + netdev_reset_queue(ag->dev); +} + +static void ag71xx_ring_rx_clean(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->rx_ring; + int ring_size = BIT(ring->order); + int i; + + if (!ring->buf) + return; + + for (i = 0; i < ring_size; i++) + if (ring->buf[i].rx_buf) { + dma_unmap_single(&ag->pdev->dev, ring->buf[i].dma_addr, + ag->rx_buf_size, DMA_FROM_DEVICE); + skb_free_frag(ring->buf[i].rx_buf); + } +} + +static int ag71xx_buffer_size(struct ag71xx *ag) +{ + return ag->rx_buf_size + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); +} + +static bool ag71xx_fill_rx_buf(struct ag71xx *ag, struct ag71xx_buf *buf, + int offset, + void *(*alloc)(unsigned int size)) +{ + struct ag71xx_ring *ring = &ag->rx_ring; + struct ag71xx_desc *desc = ag71xx_ring_desc(ring, buf - &ring->buf[0]); + void *data; + + data = alloc(ag71xx_buffer_size(ag)); + if (!data) + return false; + + buf->rx_buf = data; + buf->dma_addr = dma_map_single(&ag->pdev->dev, data, ag->rx_buf_size, + DMA_FROM_DEVICE); + desc->data = (u32) buf->dma_addr + offset; + return true; +} + +static int ag71xx_ring_rx_init(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->rx_ring; + int ring_size = BIT(ring->order); + int ring_mask = BIT(ring->order) - 1; + unsigned int i; + int ret; + + ret = 0; + for (i = 0; i < ring_size; i++) { + struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); + + desc->next = (u32) (ring->descs_dma + + AG71XX_DESC_SIZE * ((i + 1) & ring_mask)); + + DBG("ag71xx: RX desc at %p, next is %08x\n", + desc, desc->next); + } + + for (i = 0; i < ring_size; i++) { + struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); + + if (!ag71xx_fill_rx_buf(ag, &ring->buf[i], ag->rx_buf_offset, + netdev_alloc_frag)) { + ret = -ENOMEM; + break; + } + + desc->ctrl = DESC_EMPTY; + } + + /* flush descriptors */ + wmb(); + + ring->curr = 0; + ring->dirty = 0; + + return ret; +} + +static int ag71xx_ring_rx_refill(struct ag71xx *ag) +{ + struct ag71xx_ring *ring = &ag->rx_ring; + int ring_mask = BIT(ring->order) - 1; + unsigned int count; + int offset = ag->rx_buf_offset; + + count = 0; + for (; ring->curr - ring->dirty > 0; ring->dirty++) { + struct ag71xx_desc *desc; + unsigned int i; + + i = ring->dirty & ring_mask; + desc = ag71xx_ring_desc(ring, i); + + if (!ring->buf[i].rx_buf && + !ag71xx_fill_rx_buf(ag, &ring->buf[i], offset, + napi_alloc_frag)) + break; + + desc->ctrl = DESC_EMPTY; + count++; + } + + /* flush descriptors */ + wmb(); + + DBG("%s: %u rx descriptors refilled\n", ag->dev->name, count); + + return count; +} + +static int ag71xx_rings_init(struct ag71xx *ag) +{ + struct ag71xx_ring *tx = &ag->tx_ring; + struct ag71xx_ring *rx = &ag->rx_ring; + int ring_size = BIT(tx->order) + BIT(rx->order); + int tx_size = BIT(tx->order); + + tx->buf = kzalloc(ring_size * sizeof(*tx->buf), GFP_KERNEL); + if (!tx->buf) + return -ENOMEM; + + tx->descs_cpu = dma_alloc_coherent(&ag->pdev->dev, ring_size * AG71XX_DESC_SIZE, + &tx->descs_dma, GFP_KERNEL); + if (!tx->descs_cpu) { + kfree(tx->buf); + tx->buf = NULL; + return -ENOMEM; + } + + rx->buf = &tx->buf[tx_size]; + rx->descs_cpu = ((void *)tx->descs_cpu) + tx_size * AG71XX_DESC_SIZE; + rx->descs_dma = tx->descs_dma + tx_size * AG71XX_DESC_SIZE; + + ag71xx_ring_tx_init(ag); + return ag71xx_ring_rx_init(ag); +} + +static void ag71xx_rings_free(struct ag71xx *ag) +{ + struct ag71xx_ring *tx = &ag->tx_ring; + struct ag71xx_ring *rx = &ag->rx_ring; + int ring_size = BIT(tx->order) + BIT(rx->order); + + if (tx->descs_cpu) + dma_free_coherent(&ag->pdev->dev, ring_size * AG71XX_DESC_SIZE, + tx->descs_cpu, tx->descs_dma); + + kfree(tx->buf); + + tx->descs_cpu = NULL; + rx->descs_cpu = NULL; + tx->buf = NULL; + rx->buf = NULL; +} + +static void ag71xx_rings_cleanup(struct ag71xx *ag) +{ + ag71xx_ring_rx_clean(ag); + ag71xx_ring_tx_clean(ag); + ag71xx_rings_free(ag); + + netdev_reset_queue(ag->dev); +} + +static unsigned char *ag71xx_speed_str(struct ag71xx *ag) +{ + switch (ag->speed) { + case SPEED_1000: + return "1000"; + case SPEED_100: + return "100"; + case SPEED_10: + return "10"; + } + + return "?"; +} + +static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac) +{ + u32 t; + + t = (((u32) mac[5]) << 24) | (((u32) mac[4]) << 16) + | (((u32) mac[3]) << 8) | ((u32) mac[2]); + + ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t); + + t = (((u32) mac[1]) << 24) | (((u32) mac[0]) << 16); + ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t); +} + +static void ag71xx_dma_reset(struct ag71xx *ag) +{ + u32 val; + int i; + + ag71xx_dump_dma_regs(ag); + + /* stop RX and TX */ + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); + ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); + + /* + * give the hardware some time to really stop all rx/tx activity + * clearing the descriptors too early causes random memory corruption + */ + mdelay(1); + + /* clear descriptor addresses */ + ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->stop_desc_dma); + ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->stop_desc_dma); + + /* clear pending RX/TX interrupts */ + for (i = 0; i < 256; i++) { + ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); + ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); + } + + /* clear pending errors */ + ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF); + ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR); + + val = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); + if (val) + pr_alert("%s: unable to clear DMA Rx status: %08x\n", + ag->dev->name, val); + + val = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); + + /* mask out reserved bits */ + val &= ~0xff000000; + + if (val) + pr_alert("%s: unable to clear DMA Tx status: %08x\n", + ag->dev->name, val); + + ag71xx_dump_dma_regs(ag); +} + +#define MAC_CFG1_INIT (MAC_CFG1_RXE | MAC_CFG1_TXE | \ + MAC_CFG1_SRX | MAC_CFG1_STX) + +#define FIFO_CFG0_INIT (FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT) + +#define FIFO_CFG4_INIT (FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \ + FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \ + FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \ + FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \ + FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \ + FIFO_CFG4_VT) + +#define FIFO_CFG5_INIT (FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \ + FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \ + FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \ + FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \ + FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \ + FIFO_CFG5_17 | FIFO_CFG5_SF) + +static void ag71xx_hw_stop(struct ag71xx *ag) +{ + /* disable all interrupts and stop the rx/tx engine */ + ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, 0); + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); + ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); +} + +static void ag71xx_hw_setup(struct ag71xx *ag) +{ + struct device_node *np = ag->pdev->dev.of_node; + u32 init = MAC_CFG1_INIT; + + /* setup MAC configuration registers */ + if (of_property_read_bool(np, "flow-control")) + init |= MAC_CFG1_TFC | MAC_CFG1_RFC; + ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, init); + + ag71xx_sb(ag, AG71XX_REG_MAC_CFG2, + MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK); + + /* setup max frame length to zero */ + ag71xx_wr(ag, AG71XX_REG_MAC_MFL, 0); + + /* setup FIFO configuration registers */ + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, ag->fifodata[0]); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, ag->fifodata[1]); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT); +} + +static void ag71xx_hw_init(struct ag71xx *ag) +{ + ag71xx_hw_stop(ag); + + ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR); + udelay(20); + + reset_control_assert(ag->mac_reset); + if (ag->mdio_reset) + reset_control_assert(ag->mdio_reset); + msleep(100); + reset_control_deassert(ag->mac_reset); + if (ag->mdio_reset) + reset_control_deassert(ag->mdio_reset); + msleep(200); + + ag71xx_hw_setup(ag); + + ag71xx_dma_reset(ag); +} + +static void ag71xx_fast_reset(struct ag71xx *ag) +{ + struct net_device *dev = ag->dev; + u32 rx_ds; + u32 mii_reg; + + ag71xx_hw_stop(ag); + wmb(); + + mii_reg = ag71xx_rr(ag, AG71XX_REG_MII_CFG); + rx_ds = ag71xx_rr(ag, AG71XX_REG_RX_DESC); + + ag71xx_tx_packets(ag, true); + + reset_control_assert(ag->mac_reset); + udelay(10); + reset_control_deassert(ag->mac_reset); + udelay(10); + + ag71xx_dma_reset(ag); + ag71xx_hw_setup(ag); + ag->tx_ring.curr = 0; + ag->tx_ring.dirty = 0; + netdev_reset_queue(ag->dev); + + /* setup max frame length */ + ag71xx_wr(ag, AG71XX_REG_MAC_MFL, + ag71xx_max_frame_len(ag->dev->mtu)); + + ag71xx_wr(ag, AG71XX_REG_RX_DESC, rx_ds); + ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma); + ag71xx_wr(ag, AG71XX_REG_MII_CFG, mii_reg); + + ag71xx_hw_set_macaddr(ag, dev->dev_addr); +} + +static void ag71xx_hw_start(struct ag71xx *ag) +{ + /* start RX engine */ + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); + + /* enable interrupts */ + ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, AG71XX_INT_INIT); + + netif_wake_queue(ag->dev); +} + +static void ath79_set_pllval(struct ag71xx *ag) +{ + u32 pll_reg = ag->pllreg[1]; + u32 pll_val; + + if (!ag->pllregmap) + return; + + switch (ag->speed) { + case SPEED_10: + pll_val = ag->plldata[2]; + break; + case SPEED_100: + pll_val = ag->plldata[1]; + break; + case SPEED_1000: + pll_val = ag->plldata[0]; + break; + default: + BUG(); + } + + if (pll_val) + regmap_write(ag->pllregmap, pll_reg, pll_val); +} + +static void ath79_set_pll(struct ag71xx *ag) +{ + u32 pll_cfg = ag->pllreg[0]; + u32 pll_shift = ag->pllreg[2]; + + if (!ag->pllregmap) + return; + + regmap_update_bits(ag->pllregmap, pll_cfg, 3 << pll_shift, 2 << pll_shift); + udelay(100); + + ath79_set_pllval(ag); + + regmap_update_bits(ag->pllregmap, pll_cfg, 3 << pll_shift, 3 << pll_shift); + udelay(100); + + regmap_update_bits(ag->pllregmap, pll_cfg, 3 << pll_shift, 0); + udelay(100); +} + +static void ag71xx_bit_set(void __iomem *reg, u32 bit) +{ + u32 val; + + val = __raw_readl(reg) | bit; + __raw_writel(val, reg); + __raw_readl(reg); +} + +static void ag71xx_bit_clear(void __iomem *reg, u32 bit) +{ + u32 val; + + val = __raw_readl(reg) & ~bit; + __raw_writel(val, reg); + __raw_readl(reg); +} + +static void ag71xx_sgmii_init_qca955x(struct device_node *np) +{ + struct device_node *np_dev; + void __iomem *gmac_base; + u32 mr_an_status; + u32 sgmii_status; + u8 tries = 0; + int err = 0; + + np = of_get_child_by_name(np, "gmac-config"); + if (!np) + return; + + np_dev = of_parse_phandle(np, "device", 0); + if (!np_dev) + goto out; + + gmac_base = of_iomap(np_dev, 0); + if (!gmac_base) { + pr_err("%pOF: can't map GMAC registers\n", np_dev); + err = -ENOMEM; + goto err_iomap; + } + + mr_an_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_MR_AN_STATUS); + if (!(mr_an_status & QCA955X_MR_AN_STATUS_AN_ABILITY)) + goto sgmii_out; + + /* SGMII reset sequence */ + __raw_writel(QCA955X_SGMII_RESET_RX_CLK_N_RESET, + gmac_base + QCA955X_GMAC_REG_SGMII_RESET); + __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_RESET); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_HW_RX_125M_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_RX_125M_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_TX_125M_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_RX_CLK_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_TX_CLK_N); + udelay(10); + + /* + * The following is what QCA has to say about what happens here: + * + * Across resets SGMII link status goes to weird state. + * If SGMII_DEBUG register reads other than 0x1f or 0x10, + * we are for sure in a bad state. + * + * Issue a PHY reset in MR_AN_CONTROL to keep going. + */ + do { + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL, + QCA955X_MR_AN_CONTROL_PHY_RESET | + QCA955X_MR_AN_CONTROL_AN_ENABLE); + udelay(200); + ag71xx_bit_clear(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL, + QCA955X_MR_AN_CONTROL_PHY_RESET); + mdelay(300); + sgmii_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_DEBUG) & + QCA955X_SGMII_DEBUG_TX_STATE_MASK; + + if (tries++ >= 20) { + pr_err("ag71xx: max retries for SGMII fixup exceeded\n"); + break; + } + } while (!(sgmii_status == 0xf || sgmii_status == 0x10)); + +sgmii_out: + iounmap(gmac_base); +err_iomap: + of_node_put(np_dev); +out: + of_node_put(np); +} + +static void ath79_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if) +{ + u32 t; + + t = __raw_readl(ag->mii_base); + t &= ~(AR71XX_MII_CTRL_IF_MASK); + t |= (mii_if & AR71XX_MII_CTRL_IF_MASK); + __raw_writel(t, ag->mii_base); +} + +static void ath79_mii0_ctrl_set_if(struct ag71xx *ag) +{ + unsigned int mii_if; + + switch (ag->phy_if_mode) { + case PHY_INTERFACE_MODE_MII: + mii_if = AR71XX_MII0_CTRL_IF_MII; + break; + case PHY_INTERFACE_MODE_GMII: + mii_if = AR71XX_MII0_CTRL_IF_GMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + mii_if = AR71XX_MII0_CTRL_IF_RGMII; + break; + case PHY_INTERFACE_MODE_RMII: + mii_if = AR71XX_MII0_CTRL_IF_RMII; + break; + default: + WARN(1, "Impossible PHY mode defined.\n"); + return; + } + + ath79_mii_ctrl_set_if(ag, mii_if); +} + +static void ath79_mii1_ctrl_set_if(struct ag71xx *ag) +{ + unsigned int mii_if; + + switch (ag->phy_if_mode) { + case PHY_INTERFACE_MODE_RMII: + mii_if = AR71XX_MII1_CTRL_IF_RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + mii_if = AR71XX_MII1_CTRL_IF_RGMII; + break; + default: + WARN(1, "Impossible PHY mode defined.\n"); + return; + } + + ath79_mii_ctrl_set_if(ag, mii_if); +} + +static void ath79_mii_ctrl_set_speed(struct ag71xx *ag) +{ + unsigned int mii_speed; + u32 t; + + if (!ag->mii_base) + return; + + switch (ag->speed) { + case SPEED_10: + mii_speed = AR71XX_MII_CTRL_SPEED_10; + break; + case SPEED_100: + mii_speed = AR71XX_MII_CTRL_SPEED_100; + break; + case SPEED_1000: + mii_speed = AR71XX_MII_CTRL_SPEED_1000; + break; + default: + BUG(); + } + + t = __raw_readl(ag->mii_base); + t &= ~(AR71XX_MII_CTRL_SPEED_MASK << AR71XX_MII_CTRL_SPEED_SHIFT); + t |= mii_speed << AR71XX_MII_CTRL_SPEED_SHIFT; + __raw_writel(t, ag->mii_base); +} + +static void +__ag71xx_link_adjust(struct ag71xx *ag, bool update) +{ + struct device_node *np = ag->pdev->dev.of_node; + u32 cfg2; + u32 ifctl; + u32 fifo5; + + if (!ag->link && update) { + ag71xx_hw_stop(ag); + netif_carrier_off(ag->dev); + if (netif_msg_link(ag)) + pr_info("%s: link down\n", ag->dev->name); + return; + } + + if (!of_device_is_compatible(np, "qca,ar9130-eth") && + !of_device_is_compatible(np, "qca,ar7100-eth")) + ag71xx_fast_reset(ag); + + cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2); + cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX); + cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0; + + ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL); + ifctl &= ~(MAC_IFCTL_SPEED); + + fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5); + fifo5 &= ~FIFO_CFG5_BM; + + switch (ag->speed) { + case SPEED_1000: + cfg2 |= MAC_CFG2_IF_1000; + fifo5 |= FIFO_CFG5_BM; + break; + case SPEED_100: + cfg2 |= MAC_CFG2_IF_10_100; + ifctl |= MAC_IFCTL_SPEED; + break; + case SPEED_10: + cfg2 |= MAC_CFG2_IF_10_100; + break; + default: + BUG(); + return; + } + + if (ag->tx_ring.desc_split) { + ag->fifodata[2] &= 0xffff; + ag->fifodata[2] |= ((2048 - ag->tx_ring.desc_split) / 4) << 16; + } + + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, ag->fifodata[2]); + + if (update) { + if (of_device_is_compatible(np, "qca,ar7100-eth") || + of_device_is_compatible(np, "qca,ar9130-eth")) { + ath79_set_pll(ag); + ath79_mii_ctrl_set_speed(ag); + } else if (of_device_is_compatible(np, "qca,ar7242-eth") || + of_device_is_compatible(np, "qca,ar9340-eth") || + of_device_is_compatible(np, "qca,qca9550-eth") || + of_device_is_compatible(np, "qca,qca9560-eth")) { + ath79_set_pllval(ag); + if (of_property_read_bool(np, "qca955x-sgmii-fixup")) + ag71xx_sgmii_init_qca955x(np); + } + } + + ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2); + ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5); + ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl); + + if (of_device_is_compatible(np, "qca,qca9530-eth") || + of_device_is_compatible(np, "qca,qca9560-eth")) { + /* + * The rx ring buffer can stall on small packets on QCA953x and + * QCA956x. Disabling the inline checksum engine fixes the stall. + * The wr, rr functions cannot be used since this hidden register + * is outside of the normal ag71xx register block. + */ + void __iomem *dam = ioremap_nocache(0xb90001bc, 0x4); + if (dam) { + __raw_writel(__raw_readl(dam) & ~BIT(27), dam); + (void)__raw_readl(dam); + iounmap(dam); + } + } + + ag71xx_hw_start(ag); + + netif_carrier_on(ag->dev); + if (update && netif_msg_link(ag)) + pr_info("%s: link up (%sMbps/%s duplex)\n", + ag->dev->name, + ag71xx_speed_str(ag), + (DUPLEX_FULL == ag->duplex) ? "Full" : "Half"); + + ag71xx_dump_regs(ag); +} + +void ag71xx_link_adjust(struct ag71xx *ag) +{ + __ag71xx_link_adjust(ag, true); +} + +static int ag71xx_hw_enable(struct ag71xx *ag) +{ + int ret; + + ret = ag71xx_rings_init(ag); + if (ret) + return ret; + + napi_enable(&ag->napi); + ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma); + ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma); + netif_start_queue(ag->dev); + + return 0; +} + +static void ag71xx_hw_disable(struct ag71xx *ag) +{ + netif_stop_queue(ag->dev); + + ag71xx_hw_stop(ag); + ag71xx_dma_reset(ag); + + napi_disable(&ag->napi); + del_timer_sync(&ag->oom_timer); + + ag71xx_rings_cleanup(ag); +} + +static int ag71xx_open(struct net_device *dev) +{ + struct ag71xx *ag = netdev_priv(dev); + unsigned int max_frame_len; + int ret; + + netif_carrier_off(dev); + max_frame_len = ag71xx_max_frame_len(dev->mtu); + ag->rx_buf_size = SKB_DATA_ALIGN(max_frame_len + NET_SKB_PAD + NET_IP_ALIGN); + + /* setup max frame length */ + ag71xx_wr(ag, AG71XX_REG_MAC_MFL, max_frame_len); + ag71xx_hw_set_macaddr(ag, dev->dev_addr); + + ret = ag71xx_hw_enable(ag); + if (ret) + goto err; + + phy_start(ag->phy_dev); + + return 0; + +err: + ag71xx_rings_cleanup(ag); + return ret; +} + +static int ag71xx_stop(struct net_device *dev) +{ + unsigned long flags; + struct ag71xx *ag = netdev_priv(dev); + + netif_carrier_off(dev); + phy_stop(ag->phy_dev); + + spin_lock_irqsave(&ag->lock, flags); + if (ag->link) { + ag->link = 0; + ag71xx_link_adjust(ag); + } + spin_unlock_irqrestore(&ag->lock, flags); + + ag71xx_hw_disable(ag); + + return 0; +} + +static int ag71xx_fill_dma_desc(struct ag71xx_ring *ring, u32 addr, int len) +{ + int i; + struct ag71xx_desc *desc; + int ring_mask = BIT(ring->order) - 1; + int ndesc = 0; + int split = ring->desc_split; + + if (!split) + split = len; + + while (len > 0) { + unsigned int cur_len = len; + + i = (ring->curr + ndesc) & ring_mask; + desc = ag71xx_ring_desc(ring, i); + + if (!ag71xx_desc_empty(desc)) + return -1; + + if (cur_len > split) { + cur_len = split; + + /* + * TX will hang if DMA transfers <= 4 bytes, + * make sure next segment is more than 4 bytes long. + */ + if (len <= split + 4) + cur_len -= 4; + } + + desc->data = addr; + addr += cur_len; + len -= cur_len; + + if (len > 0) + cur_len |= DESC_MORE; + + /* prevent early tx attempt of this descriptor */ + if (!ndesc) + cur_len |= DESC_EMPTY; + + desc->ctrl = cur_len; + ndesc++; + } + + return ndesc; +} + +static netdev_tx_t ag71xx_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ag71xx *ag = netdev_priv(dev); + struct ag71xx_ring *ring = &ag->tx_ring; + int ring_mask = BIT(ring->order) - 1; + int ring_size = BIT(ring->order); + struct ag71xx_desc *desc; + dma_addr_t dma_addr; + int i, n, ring_min; + + if (skb->len <= 4) { + DBG("%s: packet len is too small\n", ag->dev->name); + goto err_drop; + } + + dma_addr = dma_map_single(&ag->pdev->dev, skb->data, skb->len, + DMA_TO_DEVICE); + + i = ring->curr & ring_mask; + desc = ag71xx_ring_desc(ring, i); + + /* setup descriptor fields */ + n = ag71xx_fill_dma_desc(ring, (u32) dma_addr, skb->len & ag->desc_pktlen_mask); + if (n < 0) + goto err_drop_unmap; + + i = (ring->curr + n - 1) & ring_mask; + ring->buf[i].len = skb->len; + ring->buf[i].skb = skb; + + netdev_sent_queue(dev, skb->len); + + skb_tx_timestamp(skb); + + desc->ctrl &= ~DESC_EMPTY; + ring->curr += n; + + /* flush descriptor */ + wmb(); + + ring_min = 2; + if (ring->desc_split) + ring_min *= AG71XX_TX_RING_DS_PER_PKT; + + if (ring->curr - ring->dirty >= ring_size - ring_min) { + DBG("%s: tx queue full\n", dev->name); + netif_stop_queue(dev); + } + + DBG("%s: packet injected into TX queue\n", ag->dev->name); + + /* enable TX engine */ + ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE); + + return NETDEV_TX_OK; + +err_drop_unmap: + dma_unmap_single(&ag->pdev->dev, dma_addr, skb->len, DMA_TO_DEVICE); + +err_drop: + dev->stats.tx_dropped++; + + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ag71xx *ag = netdev_priv(dev); + + + switch (cmd) { + case SIOCSIFHWADDR: + if (copy_from_user + (dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr))) + return -EFAULT; + return 0; + + case SIOCGIFHWADDR: + if (copy_to_user + (ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr))) + return -EFAULT; + return 0; + + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + if (ag->phy_dev == NULL) + break; + + return phy_mii_ioctl(ag->phy_dev, ifr, cmd); + + default: + break; + } + + return -EOPNOTSUPP; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)) +static void ag71xx_oom_timer_handler(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct ag71xx *ag = netdev_priv(dev); +#else +static void ag71xx_oom_timer_handler(struct timer_list *t) +{ + struct ag71xx *ag = from_timer(ag, t, oom_timer); +#endif + + napi_schedule(&ag->napi); +} + +static void ag71xx_tx_timeout(struct net_device *dev) +{ + struct ag71xx *ag = netdev_priv(dev); + + if (netif_msg_tx_err(ag)) + pr_info("%s: tx timeout\n", ag->dev->name); + + schedule_delayed_work(&ag->restart_work, 1); +} + +static void ag71xx_restart_work_func(struct work_struct *work) +{ + struct ag71xx *ag = container_of(work, struct ag71xx, restart_work.work); + + rtnl_lock(); + ag71xx_hw_disable(ag); + ag71xx_hw_enable(ag); + if (ag->link) + __ag71xx_link_adjust(ag, false); + rtnl_unlock(); +} + +static bool ag71xx_check_dma_stuck(struct ag71xx *ag) +{ + unsigned long timestamp; + u32 rx_sm, tx_sm, rx_fd; + + timestamp = netdev_get_tx_queue(ag->dev, 0)->trans_start; + if (likely(time_before(jiffies, timestamp + HZ/10))) + return false; + + if (!netif_carrier_ok(ag->dev)) + return false; + + rx_sm = ag71xx_rr(ag, AG71XX_REG_RX_SM); + if ((rx_sm & 0x7) == 0x3 && ((rx_sm >> 4) & 0x7) == 0x6) + return true; + + tx_sm = ag71xx_rr(ag, AG71XX_REG_TX_SM); + rx_fd = ag71xx_rr(ag, AG71XX_REG_FIFO_DEPTH); + if (((tx_sm >> 4) & 0x7) == 0 && ((rx_sm & 0x7) == 0) && + ((rx_sm >> 4) & 0x7) == 0 && rx_fd == 0) + return true; + + return false; +} + +static int ag71xx_tx_packets(struct ag71xx *ag, bool flush) +{ + struct ag71xx_ring *ring = &ag->tx_ring; + bool dma_stuck = false; + int ring_mask = BIT(ring->order) - 1; + int ring_size = BIT(ring->order); + int sent = 0; + int bytes_compl = 0; + int n = 0; + + DBG("%s: processing TX ring\n", ag->dev->name); + + while (ring->dirty + n != ring->curr) { + unsigned int i = (ring->dirty + n) & ring_mask; + struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); + struct sk_buff *skb = ring->buf[i].skb; + + if (!flush && !ag71xx_desc_empty(desc)) { + if (ag->tx_hang_workaround && + ag71xx_check_dma_stuck(ag)) { + schedule_delayed_work(&ag->restart_work, HZ / 2); + dma_stuck = true; + } + break; + } + + if (flush) + desc->ctrl |= DESC_EMPTY; + + n++; + if (!skb) + continue; + + dev_kfree_skb_any(skb); + ring->buf[i].skb = NULL; + + bytes_compl += ring->buf[i].len; + + sent++; + ring->dirty += n; + + while (n > 0) { + ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); + n--; + } + } + + DBG("%s: %d packets sent out\n", ag->dev->name, sent); + + if (!sent) + return 0; + + ag->dev->stats.tx_bytes += bytes_compl; + ag->dev->stats.tx_packets += sent; + + netdev_completed_queue(ag->dev, sent, bytes_compl); + if ((ring->curr - ring->dirty) < (ring_size * 3) / 4) + netif_wake_queue(ag->dev); + + if (!dma_stuck) + cancel_delayed_work(&ag->restart_work); + + return sent; +} + +static int ag71xx_rx_packets(struct ag71xx *ag, int limit) +{ + struct net_device *dev = ag->dev; + struct ag71xx_ring *ring = &ag->rx_ring; + unsigned int pktlen_mask = ag->desc_pktlen_mask; + unsigned int offset = ag->rx_buf_offset; + int ring_mask = BIT(ring->order) - 1; + int ring_size = BIT(ring->order); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) + struct list_head rx_list; + struct sk_buff *next; +#else + struct sk_buff_head queue; +#endif + struct sk_buff *skb; + int done = 0; + + DBG("%s: rx packets, limit=%d, curr=%u, dirty=%u\n", + dev->name, limit, ring->curr, ring->dirty); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) + INIT_LIST_HEAD(&rx_list); +#else + skb_queue_head_init(&queue); +#endif + + while (done < limit) { + unsigned int i = ring->curr & ring_mask; + struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); + int pktlen; + int err = 0; + + if (ag71xx_desc_empty(desc)) + break; + + if ((ring->dirty + ring_size) == ring->curr) { + ag71xx_assert(0); + break; + } + + ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); + + pktlen = desc->ctrl & pktlen_mask; + pktlen -= ETH_FCS_LEN; + + dma_unmap_single(&ag->pdev->dev, ring->buf[i].dma_addr, + ag->rx_buf_size, DMA_FROM_DEVICE); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += pktlen; + + skb = build_skb(ring->buf[i].rx_buf, ag71xx_buffer_size(ag)); + if (!skb) { + skb_free_frag(ring->buf[i].rx_buf); + goto next; + } + + skb_reserve(skb, offset); + skb_put(skb, pktlen); + + if (err) { + dev->stats.rx_dropped++; + kfree_skb(skb); + } else { + skb->dev = dev; + skb->ip_summed = CHECKSUM_NONE; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) + list_add_tail(&skb->list, &rx_list); +#else + __skb_queue_tail(&queue, skb); +#endif + } + +next: + ring->buf[i].rx_buf = NULL; + done++; + + ring->curr++; + } + + ag71xx_ring_rx_refill(ag); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) + list_for_each_entry_safe(skb, next, &rx_list, list) + skb->protocol = eth_type_trans(skb, dev); + netif_receive_skb_list(&rx_list); +#else + while ((skb = __skb_dequeue(&queue)) != NULL) { + skb->protocol = eth_type_trans(skb, dev); + netif_receive_skb(skb); + } +#endif + + DBG("%s: rx finish, curr=%u, dirty=%u, done=%d\n", + dev->name, ring->curr, ring->dirty, done); + + return done; +} + +static int ag71xx_poll(struct napi_struct *napi, int limit) +{ + struct ag71xx *ag = container_of(napi, struct ag71xx, napi); + struct net_device *dev = ag->dev; + struct ag71xx_ring *rx_ring = &ag->rx_ring; + int rx_ring_size = BIT(rx_ring->order); + unsigned long flags; + u32 status; + int tx_done; + int rx_done; + + tx_done = ag71xx_tx_packets(ag, false); + + DBG("%s: processing RX ring\n", dev->name); + rx_done = ag71xx_rx_packets(ag, limit); + + ag71xx_debugfs_update_napi_stats(ag, rx_done, tx_done); + + if (rx_ring->buf[rx_ring->dirty % rx_ring_size].rx_buf == NULL) + goto oom; + + status = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); + if (unlikely(status & RX_STATUS_OF)) { + ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_OF); + dev->stats.rx_fifo_errors++; + + /* restart RX */ + ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); + } + + if (rx_done < limit) { + if (status & RX_STATUS_PR) + goto more; + + status = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); + if (status & TX_STATUS_PS) + goto more; + + DBG("%s: disable polling mode, rx=%d, tx=%d,limit=%d\n", + dev->name, rx_done, tx_done, limit); + + napi_complete(napi); + + /* enable interrupts */ + spin_lock_irqsave(&ag->lock, flags); + ag71xx_int_enable(ag, AG71XX_INT_POLL); + spin_unlock_irqrestore(&ag->lock, flags); + return rx_done; + } + +more: + DBG("%s: stay in polling mode, rx=%d, tx=%d, limit=%d\n", + dev->name, rx_done, tx_done, limit); + return limit; + +oom: + if (netif_msg_rx_err(ag)) + pr_info("%s: out of memory\n", dev->name); + + mod_timer(&ag->oom_timer, jiffies + AG71XX_OOM_REFILL); + napi_complete(napi); + return 0; +} + +static irqreturn_t ag71xx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct ag71xx *ag = netdev_priv(dev); + u32 status; + + status = ag71xx_rr(ag, AG71XX_REG_INT_STATUS); + ag71xx_dump_intr(ag, "raw", status); + + if (unlikely(!status)) + return IRQ_NONE; + + if (unlikely(status & AG71XX_INT_ERR)) { + if (status & AG71XX_INT_TX_BE) { + ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE); + dev_err(&dev->dev, "TX BUS error\n"); + } + if (status & AG71XX_INT_RX_BE) { + ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE); + dev_err(&dev->dev, "RX BUS error\n"); + } + } + + if (likely(status & AG71XX_INT_POLL)) { + ag71xx_int_disable(ag, AG71XX_INT_POLL); + DBG("%s: enable polling mode\n", dev->name); + napi_schedule(&ag->napi); + } + + ag71xx_debugfs_update_int_stats(ag, status); + + return IRQ_HANDLED; +} + +static int ag71xx_change_mtu(struct net_device *dev, int new_mtu) +{ + struct ag71xx *ag = netdev_priv(dev); + + dev->mtu = new_mtu; + ag71xx_wr(ag, AG71XX_REG_MAC_MFL, + ag71xx_max_frame_len(dev->mtu)); + + return 0; +} + +static const struct net_device_ops ag71xx_netdev_ops = { + .ndo_open = ag71xx_open, + .ndo_stop = ag71xx_stop, + .ndo_start_xmit = ag71xx_hard_start_xmit, + .ndo_do_ioctl = ag71xx_do_ioctl, + .ndo_tx_timeout = ag71xx_tx_timeout, + .ndo_change_mtu = ag71xx_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int ag71xx_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct net_device *dev; + struct resource *res; + struct ag71xx *ag; + const void *mac_addr; + u32 max_frame_len; + int tx_size, err; + + if (!np) + return -ENODEV; + + dev = devm_alloc_etherdev(&pdev->dev, sizeof(*ag)); + if (!dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + err = ag71xx_setup_gmac(np); + if (err) + return err; + + SET_NETDEV_DEV(dev, &pdev->dev); + + ag = netdev_priv(dev); + ag->pdev = pdev; + ag->dev = dev; + ag->msg_enable = netif_msg_init(ag71xx_msg_level, + AG71XX_DEFAULT_MSG_ENABLE); + spin_lock_init(&ag->lock); + + ag->mac_reset = devm_reset_control_get_exclusive(&pdev->dev, "mac"); + if (IS_ERR(ag->mac_reset)) { + dev_err(&pdev->dev, "missing mac reset\n"); + return PTR_ERR(ag->mac_reset); + } + + ag->mdio_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "mdio"); + + if (of_property_read_u32_array(np, "fifo-data", ag->fifodata, 3)) { + if (of_device_is_compatible(np, "qca,ar9130-eth") || + of_device_is_compatible(np, "qca,ar7100-eth")) { + ag->fifodata[0] = 0x0fff0000; + ag->fifodata[1] = 0x00001fff; + } else { + ag->fifodata[0] = 0x0010ffff; + ag->fifodata[1] = 0x015500aa; + ag->fifodata[2] = 0x01f00140; + } + if (of_device_is_compatible(np, "qca,ar9130-eth")) + ag->fifodata[2] = 0x00780fff; + else if (of_device_is_compatible(np, "qca,ar7100-eth")) + ag->fifodata[2] = 0x008001ff; + } + + if (of_property_read_u32_array(np, "pll-data", ag->plldata, 3)) + dev_dbg(&pdev->dev, "failed to read pll-data property\n"); + + if (of_property_read_u32_array(np, "pll-reg", ag->pllreg, 3)) + dev_dbg(&pdev->dev, "failed to read pll-reg property\n"); + + ag->pllregmap = syscon_regmap_lookup_by_phandle(np, "pll-handle"); + if (IS_ERR(ag->pllregmap)) { + dev_dbg(&pdev->dev, "failed to read pll-handle property\n"); + ag->pllregmap = NULL; + } + + ag->mac_base = devm_ioremap_nocache(&pdev->dev, res->start, + res->end - res->start + 1); + if (!ag->mac_base) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + ag->mii_base = devm_ioremap_nocache(&pdev->dev, res->start, + res->end - res->start + 1); + if (!ag->mii_base) + return -ENOMEM; + } + + dev->irq = platform_get_irq(pdev, 0); + err = devm_request_irq(&pdev->dev, dev->irq, ag71xx_interrupt, + 0x0, dev_name(&pdev->dev), dev); + if (err) { + dev_err(&pdev->dev, "unable to request IRQ %d\n", dev->irq); + return err; + } + + dev->netdev_ops = &ag71xx_netdev_ops; + dev->ethtool_ops = &ag71xx_ethtool_ops; + + INIT_DELAYED_WORK(&ag->restart_work, ag71xx_restart_work_func); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)) + init_timer(&ag->oom_timer); + ag->oom_timer.data = (unsigned long) dev; + ag->oom_timer.function = ag71xx_oom_timer_handler; +#else + timer_setup(&ag->oom_timer, ag71xx_oom_timer_handler, 0); +#endif + + tx_size = AG71XX_TX_RING_SIZE_DEFAULT; + ag->rx_ring.order = ag71xx_ring_size_order(AG71XX_RX_RING_SIZE_DEFAULT); + + if (of_device_is_compatible(np, "qca,ar9340-eth") || + of_device_is_compatible(np, "qca,qca9530-eth") || + of_device_is_compatible(np, "qca,qca9550-eth") || + of_device_is_compatible(np, "qca,qca9560-eth")) + ag->desc_pktlen_mask = SZ_16K - 1; + else + ag->desc_pktlen_mask = SZ_4K - 1; + + if (ag->desc_pktlen_mask == SZ_16K - 1 && + !of_device_is_compatible(np, "qca,qca9550-eth") && + !of_device_is_compatible(np, "qca,qca9560-eth")) + max_frame_len = ag->desc_pktlen_mask; + else + max_frame_len = 1540; + + dev->min_mtu = 68; + dev->max_mtu = max_frame_len - ag71xx_max_frame_len(0); + + if (of_device_is_compatible(np, "qca,ar7240-eth") || + of_device_is_compatible(np, "qca,ar7241-eth") || + of_device_is_compatible(np, "qca,ar7242-eth") || + of_device_is_compatible(np, "qca,ar9330-eth") || + of_device_is_compatible(np, "qca,ar9340-eth") || + of_device_is_compatible(np, "qca,qca9530-eth") || + of_device_is_compatible(np, "qca,qca9550-eth") || + of_device_is_compatible(np, "qca,qca9560-eth")) + ag->tx_hang_workaround = 1; + + ag->rx_buf_offset = NET_SKB_PAD; + if (!of_device_is_compatible(np, "qca,ar7100-eth") && + !of_device_is_compatible(np, "qca,ar9130-eth")) + ag->rx_buf_offset += NET_IP_ALIGN; + + if (of_device_is_compatible(np, "qca,ar7100-eth")) { + ag->tx_ring.desc_split = AG71XX_TX_RING_SPLIT; + tx_size *= AG71XX_TX_RING_DS_PER_PKT; + } + ag->tx_ring.order = ag71xx_ring_size_order(tx_size); + + ag->stop_desc = dmam_alloc_coherent(&pdev->dev, + sizeof(struct ag71xx_desc), + &ag->stop_desc_dma, GFP_KERNEL); + if (!ag->stop_desc) + return -ENOMEM; + + ag->stop_desc->data = 0; + ag->stop_desc->ctrl = 0; + ag->stop_desc->next = (u32) ag->stop_desc_dma; + + mac_addr = of_get_mac_address(np); + if (mac_addr) + memcpy(dev->dev_addr, mac_addr, ETH_ALEN); + if (!mac_addr || !is_valid_ether_addr(dev->dev_addr)) { + dev_err(&pdev->dev, "invalid MAC address, using random address\n"); + eth_random_addr(dev->dev_addr); + } + + ag->phy_if_mode = of_get_phy_mode(np); + if (ag->phy_if_mode < 0) { + dev_err(&pdev->dev, "missing phy-mode property in DT\n"); + return ag->phy_if_mode; + } + + if (of_property_read_u32(np, "qca,mac-idx", &ag->mac_idx)) + ag->mac_idx = -1; + if (ag->mii_base) + switch (ag->mac_idx) { + case 0: + ath79_mii0_ctrl_set_if(ag); + break; + case 1: + ath79_mii1_ctrl_set_if(ag); + break; + default: + break; + } + + netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT); + + ag71xx_dump_regs(ag); + + ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, 0); + + ag71xx_hw_init(ag); + + ag71xx_dump_regs(ag); + + /* + * populate current node to register mdio-bus as a subdevice. + * the mdio bus works independently on ar7241 and later chips + * and we need to load mdio1 before gmac0, which can be done + * by adding a "simple-mfd" compatible to gmac node. The + * following code checks OF_POPULATED_BUS flag before populating + * to avoid duplicated population. + */ + if (!of_node_check_flag(np, OF_POPULATED_BUS)) { + err = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (err) + return err; + } + + err = ag71xx_phy_connect(ag); + if (err) + return err; + + err = ag71xx_debugfs_init(ag); + if (err) + goto err_phy_disconnect; + + platform_set_drvdata(pdev, dev); + + err = register_netdev(dev); + if (err) { + dev_err(&pdev->dev, "unable to register net device\n"); + platform_set_drvdata(pdev, NULL); + ag71xx_debugfs_exit(ag); + goto err_phy_disconnect; + } + + pr_info("%s: Atheros AG71xx at 0x%08lx, irq %d, mode: %s\n", + dev->name, (unsigned long) ag->mac_base, dev->irq, + phy_modes(ag->phy_if_mode)); + + return 0; + +err_phy_disconnect: + ag71xx_phy_disconnect(ag); + return err; +} + +static int ag71xx_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ag71xx *ag; + + if (!dev) + return 0; + + ag = netdev_priv(dev); + ag71xx_debugfs_exit(ag); + ag71xx_phy_disconnect(ag); + unregister_netdev(dev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id ag71xx_match[] = { + { .compatible = "qca,ar7100-eth" }, + { .compatible = "qca,ar7240-eth" }, + { .compatible = "qca,ar7241-eth" }, + { .compatible = "qca,ar7242-eth" }, + { .compatible = "qca,ar9130-eth" }, + { .compatible = "qca,ar9330-eth" }, + { .compatible = "qca,ar9340-eth" }, + { .compatible = "qca,qca9530-eth" }, + { .compatible = "qca,qca9550-eth" }, + { .compatible = "qca,qca9560-eth" }, + {} +}; + +static struct platform_driver ag71xx_driver = { + .probe = ag71xx_probe, + .remove = ag71xx_remove, + .driver = { + .name = AG71XX_DRV_NAME, + .of_match_table = ag71xx_match, + } +}; + +static int __init ag71xx_module_init(void) +{ + int ret; + + ret = ag71xx_debugfs_root_init(); + if (ret) + goto err_out; + + ret = platform_driver_register(&ag71xx_driver); + if (ret) + goto err_debugfs_exit; + + return 0; + +err_debugfs_exit: + ag71xx_debugfs_root_exit(); +err_out: + return ret; +} + +static void __exit ag71xx_module_exit(void) +{ + platform_driver_unregister(&ag71xx_driver); + ag71xx_debugfs_root_exit(); +} + +module_init(ag71xx_module_init); +module_exit(ag71xx_module_exit); + +MODULE_AUTHOR("Gabor Juhos "); +MODULE_AUTHOR("Imre Kaloz "); +MODULE_AUTHOR("Felix Fietkau "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" AG71XX_DRV_NAME); diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c new file mode 100644 index 0000000000..a58ee3346b --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c @@ -0,0 +1,254 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Based on Atheros' AG7100 driver + * + * 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 "ag71xx.h" + +#define AG71XX_MDIO_RETRY 1000 +#define AG71XX_MDIO_DELAY 5 + +static int bus_count; + +static int ag71xx_mdio_wait_busy(struct ag71xx_mdio *am) +{ + int i; + + for (i = 0; i < AG71XX_MDIO_RETRY; i++) { + u32 busy; + + udelay(AG71XX_MDIO_DELAY); + + regmap_read(am->mii_regmap, AG71XX_REG_MII_IND, &busy); + if (!busy) + return 0; + + udelay(AG71XX_MDIO_DELAY); + } + + pr_err("%s: MDIO operation timed out\n", am->mii_bus->name); + + return -ETIMEDOUT; +} + +static int ag71xx_mdio_mii_read(struct mii_bus *bus, int addr, int reg) +{ + struct ag71xx_mdio *am = bus->priv; + int err; + int ret; + + err = ag71xx_mdio_wait_busy(am); + if (err) + return 0xffff; + + regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE); + regmap_write(am->mii_regmap, AG71XX_REG_MII_ADDR, + ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); + regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_READ); + + err = ag71xx_mdio_wait_busy(am); + if (err) + return 0xffff; + + regmap_read(am->mii_regmap, AG71XX_REG_MII_STATUS, &ret); + ret &= 0xffff; + regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE); + + DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret); + + return ret; +} + +static int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + struct ag71xx_mdio *am = bus->priv; + + DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val); + + regmap_write(am->mii_regmap, AG71XX_REG_MII_ADDR, + ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); + regmap_write(am->mii_regmap, AG71XX_REG_MII_CTRL, val); + + ag71xx_mdio_wait_busy(am); + + return 0; +} + +static const u32 ar71xx_mdio_div_table[] = { + 4, 4, 6, 8, 10, 14, 20, 28, +}; + +static const u32 ar7240_mdio_div_table[] = { + 2, 2, 4, 6, 8, 12, 18, 26, 32, 40, 48, 56, 62, 70, 78, 96, +}; + +static const u32 ar933x_mdio_div_table[] = { + 4, 4, 6, 8, 10, 14, 20, 28, 34, 42, 50, 58, 66, 74, 82, 98, +}; + +static int ag71xx_mdio_get_divider(struct device_node *np, u32 *div) +{ + struct clk *ref_clk = of_clk_get(np, 0); + unsigned long ref_clock; + u32 mdio_clock; + const u32 *table; + int ndivs, i; + + if (IS_ERR(ref_clk)) + return -EINVAL; + + ref_clock = clk_get_rate(ref_clk); + clk_put(ref_clk); + + if(of_property_read_u32(np, "qca,mdio-max-frequency", &mdio_clock)) { + if (of_property_read_bool(np, "builtin-switch")) + mdio_clock = 5000000; + else + mdio_clock = 2000000; + } + + if (of_device_is_compatible(np, "qca,ar9330-mdio") || + of_device_is_compatible(np, "qca,ar9340-mdio")) { + table = ar933x_mdio_div_table; + ndivs = ARRAY_SIZE(ar933x_mdio_div_table); + } else if (of_device_is_compatible(np, "qca,ar7240-mdio")) { + table = ar7240_mdio_div_table; + ndivs = ARRAY_SIZE(ar7240_mdio_div_table); + } else { + table = ar71xx_mdio_div_table; + ndivs = ARRAY_SIZE(ar71xx_mdio_div_table); + } + + for (i = 0; i < ndivs; i++) { + unsigned long t; + + t = ref_clock / table[i]; + if (t <= mdio_clock) { + *div = i; + return 0; + } + } + + return -ENOENT; +} + +static int ag71xx_mdio_reset(struct mii_bus *bus) +{ + struct device_node *np = bus->dev.of_node; + struct ag71xx_mdio *am = bus->priv; + bool builtin_switch; + u32 t; + + builtin_switch = of_property_read_bool(np, "builtin-switch"); + + if (ag71xx_mdio_get_divider(np, &t)) { + if (of_device_is_compatible(np, "qca,ar9340-mdio")) + t = MII_CFG_CLK_DIV_58; + else if (builtin_switch) + t = MII_CFG_CLK_DIV_10; + else + t = MII_CFG_CLK_DIV_28; + } + + regmap_write(am->mii_regmap, AG71XX_REG_MII_CFG, t | MII_CFG_RESET); + udelay(100); + + regmap_write(am->mii_regmap, AG71XX_REG_MII_CFG, t); + udelay(100); + + return 0; +} + +static int ag71xx_mdio_probe(struct platform_device *pdev) +{ + struct device *amdev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct ag71xx_mdio *am; + struct mii_bus *mii_bus; + bool builtin_switch; + int i, err; + + am = devm_kzalloc(amdev, sizeof(*am), GFP_KERNEL); + if (!am) + return -ENOMEM; + + am->mii_regmap = syscon_regmap_lookup_by_phandle(np, "regmap"); + if (IS_ERR(am->mii_regmap)) + return PTR_ERR(am->mii_regmap); + + mii_bus = devm_mdiobus_alloc(amdev); + if (!mii_bus) + return -ENOMEM; + + am->mdio_reset = devm_reset_control_get_exclusive(amdev, "mdio"); + builtin_switch = of_property_read_bool(np, "builtin-switch"); + + mii_bus->name = "ag71xx_mdio"; + mii_bus->read = ag71xx_mdio_mii_read; + mii_bus->write = ag71xx_mdio_mii_write; + mii_bus->reset = ag71xx_mdio_reset; + mii_bus->priv = am; + mii_bus->parent = amdev; + snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s.%d", np->name, bus_count++); + + if (!builtin_switch && + of_property_read_u32(np, "phy-mask", &mii_bus->phy_mask)) + mii_bus->phy_mask = 0; + + for (i = 0; i < PHY_MAX_ADDR; i++) + mii_bus->irq[i] = PHY_POLL; + + if (!IS_ERR(am->mdio_reset)) { + reset_control_assert(am->mdio_reset); + msleep(100); + reset_control_deassert(am->mdio_reset); + msleep(200); + } + + err = of_mdiobus_register(mii_bus, np); + if (err) + return err; + + am->mii_bus = mii_bus; + platform_set_drvdata(pdev, am); + + return 0; +} + +static int ag71xx_mdio_remove(struct platform_device *pdev) +{ + struct ag71xx_mdio *am = platform_get_drvdata(pdev); + + mdiobus_unregister(am->mii_bus); + return 0; +} + +static const struct of_device_id ag71xx_mdio_match[] = { + { .compatible = "qca,ar7240-mdio" }, + { .compatible = "qca,ar9330-mdio" }, + { .compatible = "qca,ar9340-mdio" }, + { .compatible = "qca,ath79-mdio" }, + {} +}; + +static struct platform_driver ag71xx_mdio_driver = { + .probe = ag71xx_mdio_probe, + .remove = ag71xx_mdio_remove, + .driver = { + .name = "ag71xx-mdio", + .of_match_table = ag71xx_mdio_match, + } +}; + +module_platform_driver(ag71xx_mdio_driver); +MODULE_LICENSE("GPL"); diff --git a/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c new file mode 100644 index 0000000000..ac1af26860 --- /dev/null +++ b/target/linux/ath79/files-4.19/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c @@ -0,0 +1,92 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Based on Atheros' AG7100 driver + * + * 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 "ag71xx.h" + +static void ag71xx_phy_link_adjust(struct net_device *dev) +{ + struct ag71xx *ag = netdev_priv(dev); + struct phy_device *phydev = ag->phy_dev; + unsigned long flags; + int status_change = 0; + + spin_lock_irqsave(&ag->lock, flags); + + if (phydev->link) { + if (ag->duplex != phydev->duplex + || ag->speed != phydev->speed) { + status_change = 1; + } + } + + if (phydev->link != ag->link) + status_change = 1; + + ag->link = phydev->link; + ag->duplex = phydev->duplex; + ag->speed = phydev->speed; + + if (status_change) + ag71xx_link_adjust(ag); + + spin_unlock_irqrestore(&ag->lock, flags); +} + +int ag71xx_phy_connect(struct ag71xx *ag) +{ + struct device_node *np = ag->pdev->dev.of_node; + struct device_node *phy_node; + int ret; + + if (of_phy_is_fixed_link(np)) { + ret = of_phy_register_fixed_link(np); + if (ret < 0) { + dev_err(&ag->pdev->dev, + "Failed to register fixed PHY link: %d\n", ret); + return ret; + } + + phy_node = of_node_get(np); + } else { + phy_node = of_parse_phandle(np, "phy-handle", 0); + } + + if (!phy_node) { + dev_err(&ag->pdev->dev, + "Could not find valid phy node\n"); + return -ENODEV; + } + + ag->phy_dev = of_phy_connect(ag->dev, phy_node, ag71xx_phy_link_adjust, + 0, ag->phy_if_mode); + + of_node_put(phy_node); + + if (!ag->phy_dev) { + dev_err(&ag->pdev->dev, + "Could not connect to PHY device. Deferring probe.\n"); + return -EPROBE_DEFER; + } + + dev_info(&ag->pdev->dev, "connected to PHY at %s [uid=%08x, driver=%s]\n", + phydev_name(ag->phy_dev), + ag->phy_dev->phy_id, ag->phy_dev->drv->name); + + return 0; +} + +void ag71xx_phy_disconnect(struct ag71xx *ag) +{ + phy_disconnect(ag->phy_dev); +} diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Kconfig b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Kconfig deleted file mode 100644 index 4df2d21e34..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -config AG71XX - tristate "Atheros AR7XXX/AR9XXX built-in ethernet mac support" - depends on ATH79 - select PHYLIB - help - If you wish to compile a kernel for AR7XXX/91XXX and enable - ethernet support, then you should always answer Y to this. - -if AG71XX - -config AG71XX_DEBUG - bool "Atheros AR71xx built-in ethernet driver debugging" - default n - help - Atheros AR71xx built-in ethernet driver debugging messages. - -config AG71XX_DEBUG_FS - bool "Atheros AR71xx built-in ethernet driver debugfs support" - depends on DEBUG_FS - default n - help - Say Y, if you need access to various statistics provided by - the ag71xx driver. - -endif diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile deleted file mode 100644 index 87add0d208..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# -# Makefile for the Atheros AR71xx built-in ethernet macs -# - -ag71xx-y += ag71xx_main.o -ag71xx-y += ag71xx_gmac.o -ag71xx-y += ag71xx_ethtool.o -ag71xx-y += ag71xx_phy.o - -ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o - -obj-$(CONFIG_AG71XX) += ag71xx_mdio.o -obj-$(CONFIG_AG71XX) += ag71xx.o diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h deleted file mode 100644 index fde9db3745..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Atheros AR71xx built-in ethernet mac driver - * - * Copyright (C) 2008-2010 Gabor Juhos - * Copyright (C) 2008 Imre Kaloz - * - * Based on Atheros' AG7100 driver - * - * 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. - */ - -#ifndef __AG71XX_H -#define __AG71XX_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#define AG71XX_DRV_NAME "ag71xx" - -/* - * For our NAPI weight bigger does *NOT* mean better - it means more - * D-cache misses and lots more wasted cycles than we'll ever - * possibly gain from saving instructions. - */ -#define AG71XX_NAPI_WEIGHT 32 -#define AG71XX_OOM_REFILL (1 + HZ/10) - -#define AG71XX_INT_ERR (AG71XX_INT_RX_BE | AG71XX_INT_TX_BE) -#define AG71XX_INT_TX (AG71XX_INT_TX_PS) -#define AG71XX_INT_RX (AG71XX_INT_RX_PR | AG71XX_INT_RX_OF) - -#define AG71XX_INT_POLL (AG71XX_INT_RX | AG71XX_INT_TX) -#define AG71XX_INT_INIT (AG71XX_INT_ERR | AG71XX_INT_POLL) - -#define AG71XX_TX_MTU_LEN 1540 - -#define AG71XX_TX_RING_SPLIT 512 -#define AG71XX_TX_RING_DS_PER_PKT DIV_ROUND_UP(AG71XX_TX_MTU_LEN, \ - AG71XX_TX_RING_SPLIT) -#define AG71XX_TX_RING_SIZE_DEFAULT 128 -#define AG71XX_RX_RING_SIZE_DEFAULT 256 - -#define AG71XX_TX_RING_SIZE_MAX 128 -#define AG71XX_RX_RING_SIZE_MAX 256 - -#ifdef CONFIG_AG71XX_DEBUG -#define DBG(fmt, args...) pr_debug(fmt, ## args) -#else -#define DBG(fmt, args...) do {} while (0) -#endif - -#define ag71xx_assert(_cond) \ -do { \ - if (_cond) \ - break; \ - printk("%s,%d: assertion failed\n", __FILE__, __LINE__); \ - BUG(); \ -} while (0) - -struct ag71xx_desc { - u32 data; - u32 ctrl; -#define DESC_EMPTY BIT(31) -#define DESC_MORE BIT(24) -#define DESC_PKTLEN_M 0xfff - u32 next; - u32 pad; -} __attribute__((aligned(4))); - -#define AG71XX_DESC_SIZE roundup(sizeof(struct ag71xx_desc), \ - L1_CACHE_BYTES) - -struct ag71xx_buf { - union { - struct sk_buff *skb; - void *rx_buf; - }; - union { - dma_addr_t dma_addr; - unsigned int len; - }; -}; - -struct ag71xx_ring { - struct ag71xx_buf *buf; - u8 *descs_cpu; - dma_addr_t descs_dma; - u16 desc_split; - u16 order; - unsigned int curr; - unsigned int dirty; -}; - -struct ag71xx_int_stats { - unsigned long rx_pr; - unsigned long rx_be; - unsigned long rx_of; - unsigned long tx_ps; - unsigned long tx_be; - unsigned long tx_ur; - unsigned long total; -}; - -struct ag71xx_napi_stats { - unsigned long napi_calls; - unsigned long rx_count; - unsigned long rx_packets; - unsigned long rx_packets_max; - unsigned long tx_count; - unsigned long tx_packets; - unsigned long tx_packets_max; - - unsigned long rx[AG71XX_NAPI_WEIGHT + 1]; - unsigned long tx[AG71XX_NAPI_WEIGHT + 1]; -}; - -struct ag71xx_debug { - struct dentry *debugfs_dir; - - struct ag71xx_int_stats int_stats; - struct ag71xx_napi_stats napi_stats; -}; - -struct ag71xx { - /* - * Critical data related to the per-packet data path are clustered - * early in this structure to help improve the D-cache footprint. - */ - struct ag71xx_ring rx_ring ____cacheline_aligned; - struct ag71xx_ring tx_ring ____cacheline_aligned; - - int mac_idx; - - u16 desc_pktlen_mask; - u16 rx_buf_size; - u8 rx_buf_offset; - u8 tx_hang_workaround:1; - - struct net_device *dev; - struct platform_device *pdev; - spinlock_t lock; - struct napi_struct napi; - u32 msg_enable; - - /* - * From this point onwards we're not looking at per-packet fields. - */ - void __iomem *mac_base; - void __iomem *mii_base; - - struct ag71xx_desc *stop_desc; - dma_addr_t stop_desc_dma; - - struct phy_device *phy_dev; - void *phy_priv; - int phy_if_mode; - - unsigned int link; - unsigned int speed; - int duplex; - - struct delayed_work restart_work; - struct timer_list oom_timer; - - struct reset_control *mac_reset; - struct reset_control *mdio_reset; - - u32 fifodata[3]; - u32 plldata[3]; - u32 pllreg[3]; - struct regmap *pllregmap; - -#ifdef CONFIG_AG71XX_DEBUG_FS - struct ag71xx_debug debug; -#endif -}; - -struct ag71xx_mdio { - struct reset_control *mdio_reset; - struct mii_bus *mii_bus; - struct regmap *mii_regmap; -}; - -extern struct ethtool_ops ag71xx_ethtool_ops; -void ag71xx_link_adjust(struct ag71xx *ag); - -int ag71xx_phy_connect(struct ag71xx *ag); -void ag71xx_phy_disconnect(struct ag71xx *ag); - -static inline int ag71xx_desc_empty(struct ag71xx_desc *desc) -{ - return (desc->ctrl & DESC_EMPTY) != 0; -} - -static inline struct ag71xx_desc * -ag71xx_ring_desc(struct ag71xx_ring *ring, int idx) -{ - return (struct ag71xx_desc *) &ring->descs_cpu[idx * AG71XX_DESC_SIZE]; -} - -static inline int -ag71xx_ring_size_order(int size) -{ - return fls(size - 1); -} - -/* Register offsets */ -#define AG71XX_REG_MAC_CFG1 0x0000 -#define AG71XX_REG_MAC_CFG2 0x0004 -#define AG71XX_REG_MAC_IPG 0x0008 -#define AG71XX_REG_MAC_HDX 0x000c -#define AG71XX_REG_MAC_MFL 0x0010 -#define AG71XX_REG_MII_CFG 0x0020 -#define AG71XX_REG_MII_CMD 0x0024 -#define AG71XX_REG_MII_ADDR 0x0028 -#define AG71XX_REG_MII_CTRL 0x002c -#define AG71XX_REG_MII_STATUS 0x0030 -#define AG71XX_REG_MII_IND 0x0034 -#define AG71XX_REG_MAC_IFCTL 0x0038 -#define AG71XX_REG_MAC_ADDR1 0x0040 -#define AG71XX_REG_MAC_ADDR2 0x0044 -#define AG71XX_REG_FIFO_CFG0 0x0048 -#define AG71XX_REG_FIFO_CFG1 0x004c -#define AG71XX_REG_FIFO_CFG2 0x0050 -#define AG71XX_REG_FIFO_CFG3 0x0054 -#define AG71XX_REG_FIFO_CFG4 0x0058 -#define AG71XX_REG_FIFO_CFG5 0x005c -#define AG71XX_REG_FIFO_RAM0 0x0060 -#define AG71XX_REG_FIFO_RAM1 0x0064 -#define AG71XX_REG_FIFO_RAM2 0x0068 -#define AG71XX_REG_FIFO_RAM3 0x006c -#define AG71XX_REG_FIFO_RAM4 0x0070 -#define AG71XX_REG_FIFO_RAM5 0x0074 -#define AG71XX_REG_FIFO_RAM6 0x0078 -#define AG71XX_REG_FIFO_RAM7 0x007c - -#define AG71XX_REG_TX_CTRL 0x0180 -#define AG71XX_REG_TX_DESC 0x0184 -#define AG71XX_REG_TX_STATUS 0x0188 -#define AG71XX_REG_RX_CTRL 0x018c -#define AG71XX_REG_RX_DESC 0x0190 -#define AG71XX_REG_RX_STATUS 0x0194 -#define AG71XX_REG_INT_ENABLE 0x0198 -#define AG71XX_REG_INT_STATUS 0x019c - -#define AG71XX_REG_FIFO_DEPTH 0x01a8 -#define AG71XX_REG_RX_SM 0x01b0 -#define AG71XX_REG_TX_SM 0x01b4 - -#define MAC_CFG1_TXE BIT(0) /* Tx Enable */ -#define MAC_CFG1_STX BIT(1) /* Synchronize Tx Enable */ -#define MAC_CFG1_RXE BIT(2) /* Rx Enable */ -#define MAC_CFG1_SRX BIT(3) /* Synchronize Rx Enable */ -#define MAC_CFG1_TFC BIT(4) /* Tx Flow Control Enable */ -#define MAC_CFG1_RFC BIT(5) /* Rx Flow Control Enable */ -#define MAC_CFG1_LB BIT(8) /* Loopback mode */ -#define MAC_CFG1_SR BIT(31) /* Soft Reset */ - -#define MAC_CFG2_FDX BIT(0) -#define MAC_CFG2_CRC_EN BIT(1) -#define MAC_CFG2_PAD_CRC_EN BIT(2) -#define MAC_CFG2_LEN_CHECK BIT(4) -#define MAC_CFG2_HUGE_FRAME_EN BIT(5) -#define MAC_CFG2_IF_1000 BIT(9) -#define MAC_CFG2_IF_10_100 BIT(8) - -#define FIFO_CFG0_WTM BIT(0) /* Watermark Module */ -#define FIFO_CFG0_RXS BIT(1) /* Rx System Module */ -#define FIFO_CFG0_RXF BIT(2) /* Rx Fabric Module */ -#define FIFO_CFG0_TXS BIT(3) /* Tx System Module */ -#define FIFO_CFG0_TXF BIT(4) /* Tx Fabric Module */ -#define FIFO_CFG0_ALL (FIFO_CFG0_WTM | FIFO_CFG0_RXS | FIFO_CFG0_RXF \ - | FIFO_CFG0_TXS | FIFO_CFG0_TXF) - -#define FIFO_CFG0_ENABLE_SHIFT 8 - -#define FIFO_CFG4_DE BIT(0) /* Drop Event */ -#define FIFO_CFG4_DV BIT(1) /* RX_DV Event */ -#define FIFO_CFG4_FC BIT(2) /* False Carrier */ -#define FIFO_CFG4_CE BIT(3) /* Code Error */ -#define FIFO_CFG4_CR BIT(4) /* CRC error */ -#define FIFO_CFG4_LM BIT(5) /* Length Mismatch */ -#define FIFO_CFG4_LO BIT(6) /* Length out of range */ -#define FIFO_CFG4_OK BIT(7) /* Packet is OK */ -#define FIFO_CFG4_MC BIT(8) /* Multicast Packet */ -#define FIFO_CFG4_BC BIT(9) /* Broadcast Packet */ -#define FIFO_CFG4_DR BIT(10) /* Dribble */ -#define FIFO_CFG4_LE BIT(11) /* Long Event */ -#define FIFO_CFG4_CF BIT(12) /* Control Frame */ -#define FIFO_CFG4_PF BIT(13) /* Pause Frame */ -#define FIFO_CFG4_UO BIT(14) /* Unsupported Opcode */ -#define FIFO_CFG4_VT BIT(15) /* VLAN tag detected */ -#define FIFO_CFG4_FT BIT(16) /* Frame Truncated */ -#define FIFO_CFG4_UC BIT(17) /* Unicast Packet */ - -#define FIFO_CFG5_DE BIT(0) /* Drop Event */ -#define FIFO_CFG5_DV BIT(1) /* RX_DV Event */ -#define FIFO_CFG5_FC BIT(2) /* False Carrier */ -#define FIFO_CFG5_CE BIT(3) /* Code Error */ -#define FIFO_CFG5_LM BIT(4) /* Length Mismatch */ -#define FIFO_CFG5_LO BIT(5) /* Length Out of Range */ -#define FIFO_CFG5_OK BIT(6) /* Packet is OK */ -#define FIFO_CFG5_MC BIT(7) /* Multicast Packet */ -#define FIFO_CFG5_BC BIT(8) /* Broadcast Packet */ -#define FIFO_CFG5_DR BIT(9) /* Dribble */ -#define FIFO_CFG5_CF BIT(10) /* Control Frame */ -#define FIFO_CFG5_PF BIT(11) /* Pause Frame */ -#define FIFO_CFG5_UO BIT(12) /* Unsupported Opcode */ -#define FIFO_CFG5_VT BIT(13) /* VLAN tag detected */ -#define FIFO_CFG5_LE BIT(14) /* Long Event */ -#define FIFO_CFG5_FT BIT(15) /* Frame Truncated */ -#define FIFO_CFG5_16 BIT(16) /* unknown */ -#define FIFO_CFG5_17 BIT(17) /* unknown */ -#define FIFO_CFG5_SF BIT(18) /* Short Frame */ -#define FIFO_CFG5_BM BIT(19) /* Byte Mode */ - -#define AG71XX_INT_TX_PS BIT(0) -#define AG71XX_INT_TX_UR BIT(1) -#define AG71XX_INT_TX_BE BIT(3) -#define AG71XX_INT_RX_PR BIT(4) -#define AG71XX_INT_RX_OF BIT(6) -#define AG71XX_INT_RX_BE BIT(7) - -#define MAC_IFCTL_SPEED BIT(16) - -#define MII_CFG_CLK_DIV_4 0 -#define MII_CFG_CLK_DIV_6 2 -#define MII_CFG_CLK_DIV_8 3 -#define MII_CFG_CLK_DIV_10 4 -#define MII_CFG_CLK_DIV_14 5 -#define MII_CFG_CLK_DIV_20 6 -#define MII_CFG_CLK_DIV_28 7 -#define MII_CFG_CLK_DIV_34 8 -#define MII_CFG_CLK_DIV_42 9 -#define MII_CFG_CLK_DIV_50 10 -#define MII_CFG_CLK_DIV_58 11 -#define MII_CFG_CLK_DIV_66 12 -#define MII_CFG_CLK_DIV_74 13 -#define MII_CFG_CLK_DIV_82 14 -#define MII_CFG_CLK_DIV_98 15 -#define MII_CFG_RESET BIT(31) - -#define MII_CMD_WRITE 0x0 -#define MII_CMD_READ 0x1 -#define MII_ADDR_SHIFT 8 -#define MII_IND_BUSY BIT(0) -#define MII_IND_INVALID BIT(2) - -#define TX_CTRL_TXE BIT(0) /* Tx Enable */ - -#define TX_STATUS_PS BIT(0) /* Packet Sent */ -#define TX_STATUS_UR BIT(1) /* Tx Underrun */ -#define TX_STATUS_BE BIT(3) /* Bus Error */ - -#define RX_CTRL_RXE BIT(0) /* Rx Enable */ - -#define RX_STATUS_PR BIT(0) /* Packet Received */ -#define RX_STATUS_OF BIT(2) /* Rx Overflow */ -#define RX_STATUS_BE BIT(3) /* Bus Error */ - -static inline void ag71xx_wr(struct ag71xx *ag, unsigned reg, u32 value) -{ - __raw_writel(value, ag->mac_base + reg); - /* flush write */ - (void) __raw_readl(ag->mac_base + reg); -} - -static inline u32 ag71xx_rr(struct ag71xx *ag, unsigned reg) -{ - return __raw_readl(ag->mac_base + reg); -} - -static inline void ag71xx_sb(struct ag71xx *ag, unsigned reg, u32 mask) -{ - void __iomem *r; - - r = ag->mac_base + reg; - __raw_writel(__raw_readl(r) | mask, r); - /* flush write */ - (void) __raw_readl(r); -} - -static inline void ag71xx_cb(struct ag71xx *ag, unsigned reg, u32 mask) -{ - void __iomem *r; - - r = ag->mac_base + reg; - __raw_writel(__raw_readl(r) & ~mask, r); - /* flush write */ - (void) __raw_readl(r); -} - -static inline void ag71xx_int_enable(struct ag71xx *ag, u32 ints) -{ - ag71xx_sb(ag, AG71XX_REG_INT_ENABLE, ints); -} - -static inline void ag71xx_int_disable(struct ag71xx *ag, u32 ints) -{ - ag71xx_cb(ag, AG71XX_REG_INT_ENABLE, ints); -} - -#ifdef CONFIG_AG71XX_DEBUG_FS -int ag71xx_debugfs_root_init(void); -void ag71xx_debugfs_root_exit(void); -int ag71xx_debugfs_init(struct ag71xx *ag); -void ag71xx_debugfs_exit(struct ag71xx *ag); -void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, u32 status); -void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, int rx, int tx); -#else -static inline int ag71xx_debugfs_root_init(void) { return 0; } -static inline void ag71xx_debugfs_root_exit(void) {} -static inline int ag71xx_debugfs_init(struct ag71xx *ag) { return 0; } -static inline void ag71xx_debugfs_exit(struct ag71xx *ag) {} -static inline void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, - u32 status) {} -static inline void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, - int rx, int tx) {} -#endif /* CONFIG_AG71XX_DEBUG_FS */ - -int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np); -void ag71xx_ar7240_cleanup(struct ag71xx *ag); - -int ag71xx_setup_gmac(struct device_node *np); - -int ar7240sw_phy_read(struct mii_bus *mii, int addr, int reg); -int ar7240sw_phy_write(struct mii_bus *mii, int addr, int reg, u16 val); - -#endif /* _AG71XX_H */ diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c deleted file mode 100644 index 20cf1c15c8..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Atheros AR71xx built-in ethernet mac driver - * - * Copyright (C) 2008-2010 Gabor Juhos - * Copyright (C) 2008 Imre Kaloz - * - * Based on Atheros' AG7100 driver - * - * 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 "ag71xx.h" - -static struct dentry *ag71xx_debugfs_root; - -static int ag71xx_debugfs_generic_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, u32 status) -{ - if (status) - ag->debug.int_stats.total++; - if (status & AG71XX_INT_TX_PS) - ag->debug.int_stats.tx_ps++; - if (status & AG71XX_INT_TX_UR) - ag->debug.int_stats.tx_ur++; - if (status & AG71XX_INT_TX_BE) - ag->debug.int_stats.tx_be++; - if (status & AG71XX_INT_RX_PR) - ag->debug.int_stats.rx_pr++; - if (status & AG71XX_INT_RX_OF) - ag->debug.int_stats.rx_of++; - if (status & AG71XX_INT_RX_BE) - ag->debug.int_stats.rx_be++; -} - -static ssize_t read_file_int_stats(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PR_INT_STAT(_label, _field) \ - len += snprintf(buf + len, sizeof(buf) - len, \ - "%20s: %10lu\n", _label, ag->debug.int_stats._field); - - struct ag71xx *ag = file->private_data; - char buf[256]; - unsigned int len = 0; - - PR_INT_STAT("TX Packet Sent", tx_ps); - PR_INT_STAT("TX Underrun", tx_ur); - PR_INT_STAT("TX Bus Error", tx_be); - PR_INT_STAT("RX Packet Received", rx_pr); - PR_INT_STAT("RX Overflow", rx_of); - PR_INT_STAT("RX Bus Error", rx_be); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); - PR_INT_STAT("Total", total); - - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -#undef PR_INT_STAT -} - -static const struct file_operations ag71xx_fops_int_stats = { - .open = ag71xx_debugfs_generic_open, - .read = read_file_int_stats, - .owner = THIS_MODULE -}; - -void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, int rx, int tx) -{ - struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; - - if (rx) { - stats->rx_count++; - stats->rx_packets += rx; - if (rx <= AG71XX_NAPI_WEIGHT) - stats->rx[rx]++; - if (rx > stats->rx_packets_max) - stats->rx_packets_max = rx; - } - - if (tx) { - stats->tx_count++; - stats->tx_packets += tx; - if (tx <= AG71XX_NAPI_WEIGHT) - stats->tx[tx]++; - if (tx > stats->tx_packets_max) - stats->tx_packets_max = tx; - } -} - -static ssize_t read_file_napi_stats(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ag71xx *ag = file->private_data; - struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; - char *buf; - unsigned int buflen; - unsigned int len = 0; - unsigned long rx_avg = 0; - unsigned long tx_avg = 0; - int ret; - int i; - - buflen = 2048; - buf = kmalloc(buflen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (stats->rx_count) - rx_avg = stats->rx_packets / stats->rx_count; - - if (stats->tx_count) - tx_avg = stats->tx_packets / stats->tx_count; - - len += snprintf(buf + len, buflen - len, "%3s %10s %10s\n", - "len", "rx", "tx"); - - for (i = 1; i <= AG71XX_NAPI_WEIGHT; i++) - len += snprintf(buf + len, buflen - len, - "%3d: %10lu %10lu\n", - i, stats->rx[i], stats->tx[i]); - - len += snprintf(buf + len, buflen - len, "\n"); - - len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", - "sum", stats->rx_count, stats->tx_count); - len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", - "avg", rx_avg, tx_avg); - len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", - "max", stats->rx_packets_max, stats->tx_packets_max); - len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", - "pkt", stats->rx_packets, stats->tx_packets); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return ret; -} - -static const struct file_operations ag71xx_fops_napi_stats = { - .open = ag71xx_debugfs_generic_open, - .read = read_file_napi_stats, - .owner = THIS_MODULE -}; - -#define DESC_PRINT_LEN 64 - -static ssize_t read_file_ring(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos, - struct ag71xx *ag, - struct ag71xx_ring *ring, - unsigned desc_reg) -{ - int ring_size = BIT(ring->order); - int ring_mask = ring_size - 1; - char *buf; - unsigned int buflen; - unsigned int len = 0; - unsigned long flags; - ssize_t ret; - int curr; - int dirty; - u32 desc_hw; - int i; - - buflen = (ring_size * DESC_PRINT_LEN); - buf = kmalloc(buflen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - len += snprintf(buf + len, buflen - len, - "Idx ... %-8s %-8s %-8s %-8s .\n", - "desc", "next", "data", "ctrl"); - - spin_lock_irqsave(&ag->lock, flags); - - curr = (ring->curr & ring_mask); - dirty = (ring->dirty & ring_mask); - desc_hw = ag71xx_rr(ag, desc_reg); - for (i = 0; i < ring_size; i++) { - struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); - u32 desc_dma = ((u32) ring->descs_dma) + i * AG71XX_DESC_SIZE; - - len += snprintf(buf + len, buflen - len, - "%3d %c%c%c %08x %08x %08x %08x %c\n", - i, - (i == curr) ? 'C' : ' ', - (i == dirty) ? 'D' : ' ', - (desc_hw == desc_dma) ? 'H' : ' ', - desc_dma, - desc->next, - desc->data, - desc->ctrl, - (desc->ctrl & DESC_EMPTY) ? 'E' : '*'); - } - - spin_unlock_irqrestore(&ag->lock, flags); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return ret; -} - -static ssize_t read_file_tx_ring(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ag71xx *ag = file->private_data; - - return read_file_ring(file, user_buf, count, ppos, ag, &ag->tx_ring, - AG71XX_REG_TX_DESC); -} - -static const struct file_operations ag71xx_fops_tx_ring = { - .open = ag71xx_debugfs_generic_open, - .read = read_file_tx_ring, - .owner = THIS_MODULE -}; - -static ssize_t read_file_rx_ring(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ag71xx *ag = file->private_data; - - return read_file_ring(file, user_buf, count, ppos, ag, &ag->rx_ring, - AG71XX_REG_RX_DESC); -} - -static const struct file_operations ag71xx_fops_rx_ring = { - .open = ag71xx_debugfs_generic_open, - .read = read_file_rx_ring, - .owner = THIS_MODULE -}; - -void ag71xx_debugfs_exit(struct ag71xx *ag) -{ - debugfs_remove_recursive(ag->debug.debugfs_dir); -} - -int ag71xx_debugfs_init(struct ag71xx *ag) -{ - struct device *dev = &ag->pdev->dev; - - ag->debug.debugfs_dir = debugfs_create_dir(dev_name(dev), - ag71xx_debugfs_root); - if (!ag->debug.debugfs_dir) { - dev_err(dev, "unable to create debugfs directory\n"); - return -ENOENT; - } - - debugfs_create_file("int_stats", S_IRUGO, ag->debug.debugfs_dir, - ag, &ag71xx_fops_int_stats); - debugfs_create_file("napi_stats", S_IRUGO, ag->debug.debugfs_dir, - ag, &ag71xx_fops_napi_stats); - debugfs_create_file("tx_ring", S_IRUGO, ag->debug.debugfs_dir, - ag, &ag71xx_fops_tx_ring); - debugfs_create_file("rx_ring", S_IRUGO, ag->debug.debugfs_dir, - ag, &ag71xx_fops_rx_ring); - - return 0; -} - -int ag71xx_debugfs_root_init(void) -{ - if (ag71xx_debugfs_root) - return -EBUSY; - - ag71xx_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (!ag71xx_debugfs_root) - return -ENOENT; - - return 0; -} - -void ag71xx_debugfs_root_exit(void) -{ - debugfs_remove(ag71xx_debugfs_root); - ag71xx_debugfs_root = NULL; -} diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c deleted file mode 100644 index 2cd7b1be83..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Atheros AR71xx built-in ethernet mac driver - * - * Copyright (C) 2008-2010 Gabor Juhos - * Copyright (C) 2008 Imre Kaloz - * - * Based on Atheros' AG7100 driver - * - * 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 "ag71xx.h" - -static u32 ag71xx_ethtool_get_msglevel(struct net_device *dev) -{ - struct ag71xx *ag = netdev_priv(dev); - - return ag->msg_enable; -} - -static void ag71xx_ethtool_set_msglevel(struct net_device *dev, u32 msg_level) -{ - struct ag71xx *ag = netdev_priv(dev); - - ag->msg_enable = msg_level; -} - -static void ag71xx_ethtool_get_ringparam(struct net_device *dev, - struct ethtool_ringparam *er) -{ - struct ag71xx *ag = netdev_priv(dev); - - er->tx_max_pending = AG71XX_TX_RING_SIZE_MAX; - er->rx_max_pending = AG71XX_RX_RING_SIZE_MAX; - er->rx_mini_max_pending = 0; - er->rx_jumbo_max_pending = 0; - - er->tx_pending = BIT(ag->tx_ring.order); - er->rx_pending = BIT(ag->rx_ring.order); - er->rx_mini_pending = 0; - er->rx_jumbo_pending = 0; - - if (ag->tx_ring.desc_split) - er->tx_pending /= AG71XX_TX_RING_DS_PER_PKT; -} - -static int ag71xx_ethtool_set_ringparam(struct net_device *dev, - struct ethtool_ringparam *er) -{ - struct ag71xx *ag = netdev_priv(dev); - unsigned tx_size; - unsigned rx_size; - int err = 0; - - if (er->rx_mini_pending != 0|| - er->rx_jumbo_pending != 0 || - er->rx_pending == 0 || - er->tx_pending == 0) - return -EINVAL; - - tx_size = er->tx_pending < AG71XX_TX_RING_SIZE_MAX ? - er->tx_pending : AG71XX_TX_RING_SIZE_MAX; - - rx_size = er->rx_pending < AG71XX_RX_RING_SIZE_MAX ? - er->rx_pending : AG71XX_RX_RING_SIZE_MAX; - - if (netif_running(dev)) { - err = dev->netdev_ops->ndo_stop(dev); - if (err) - return err; - } - - if (ag->tx_ring.desc_split) - tx_size *= AG71XX_TX_RING_DS_PER_PKT; - - ag->tx_ring.order = ag71xx_ring_size_order(tx_size); - ag->rx_ring.order = ag71xx_ring_size_order(rx_size); - - if (netif_running(dev)) - err = dev->netdev_ops->ndo_open(dev); - - return err; -} - -static int ag71xx_ethtool_nway_reset(struct net_device *dev) -{ - struct ag71xx *ag = netdev_priv(dev); - struct phy_device *phydev = ag->phy_dev; - - if (!phydev) - return -ENODEV; - - return genphy_restart_aneg(phydev); -} - -struct ethtool_ops ag71xx_ethtool_ops = { - .get_msglevel = ag71xx_ethtool_get_msglevel, - .set_msglevel = ag71xx_ethtool_set_msglevel, - .get_ringparam = ag71xx_ethtool_get_ringparam, - .set_ringparam = ag71xx_ethtool_set_ringparam, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, - .get_link = ethtool_op_get_link, - .get_ts_info = ethtool_op_get_ts_info, - .nway_reset = ag71xx_ethtool_nway_reset, -}; diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c deleted file mode 100644 index cc0a15d3a4..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_gmac.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Atheros AR71xx built-in ethernet mac driver - * - * 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 "ag71xx.h" - -static void ag71xx_of_set(struct device_node *np, const char *prop, - u32 *reg, u32 shift, u32 mask) -{ - u32 val; - - if (of_property_read_u32(np, prop, &val)) - return; - - *reg &= ~(mask << shift); - *reg |= ((val & mask) << shift); -} - -static void ag71xx_of_bit(struct device_node *np, const char *prop, - u32 *reg, u32 mask) -{ - u32 val; - - if (of_property_read_u32(np, prop, &val)) - return; - - if (val) - *reg |= mask; - else - *reg &= ~mask; -} - -static void ag71xx_setup_gmac_933x(struct device_node *np, void __iomem *base) -{ - u32 val = __raw_readl(base + AR933X_GMAC_REG_ETH_CFG); - - ag71xx_of_bit(np, "switch-phy-swap", &val, AR933X_ETH_CFG_SW_PHY_SWAP); - ag71xx_of_bit(np, "switch-phy-addr-swap", &val, - AR933X_ETH_CFG_SW_PHY_ADDR_SWAP); - - __raw_writel(val, base + AR933X_GMAC_REG_ETH_CFG); -} - -static void ag71xx_setup_gmac_934x(struct device_node *np, void __iomem *base) -{ - u32 val = __raw_readl(base + AR934X_GMAC_REG_ETH_CFG); - - ag71xx_of_bit(np, "rgmii-gmac0", &val, AR934X_ETH_CFG_RGMII_GMAC0); - ag71xx_of_bit(np, "mii-gmac0", &val, AR934X_ETH_CFG_MII_GMAC0); - ag71xx_of_bit(np, "mii-gmac0-slave", &val, AR934X_ETH_CFG_MII_GMAC0_SLAVE); - ag71xx_of_bit(np, "gmii-gmac0", &val, AR934X_ETH_CFG_GMII_GMAC0); - ag71xx_of_bit(np, "switch-phy-swap", &val, AR934X_ETH_CFG_SW_PHY_SWAP); - ag71xx_of_bit(np, "switch-only-mode", &val, - AR934X_ETH_CFG_SW_ONLY_MODE); - ag71xx_of_set(np, "rxdv-delay", &val, - AR934X_ETH_CFG_RDV_DELAY_SHIFT, 0x3); - ag71xx_of_set(np, "rxd-delay", &val, - AR934X_ETH_CFG_RXD_DELAY_SHIFT, 0x3); - ag71xx_of_set(np, "txd-delay", &val, - AR934X_ETH_CFG_TXD_DELAY_SHIFT, 0x3); - ag71xx_of_set(np, "txen-delay", &val, - AR934X_ETH_CFG_TXE_DELAY_SHIFT, 0x3); - - __raw_writel(val, base + AR934X_GMAC_REG_ETH_CFG); -} - -static void ag71xx_setup_gmac_955x(struct device_node *np, void __iomem *base) -{ - u32 val = __raw_readl(base + QCA955X_GMAC_REG_ETH_CFG); - - ag71xx_of_bit(np, "rgmii-enabled", &val, QCA955X_ETH_CFG_RGMII_EN); - ag71xx_of_bit(np, "ge0-sgmii", &val, QCA955X_ETH_CFG_GE0_SGMII); - ag71xx_of_set(np, "txen-delay", &val, QCA955X_ETH_CFG_TXE_DELAY_SHIFT, 0x3); - ag71xx_of_set(np, "txd-delay", &val, QCA955X_ETH_CFG_TXD_DELAY_SHIFT, 0x3); - ag71xx_of_set(np, "rxdv-delay", &val, QCA955X_ETH_CFG_RDV_DELAY_SHIFT, 0x3); - ag71xx_of_set(np, "rxd-delay", &val, QCA955X_ETH_CFG_RXD_DELAY_SHIFT, 0x3); - - __raw_writel(val, base + QCA955X_GMAC_REG_ETH_CFG); -} - -static void ag71xx_setup_gmac_956x(struct device_node *np, void __iomem *base) -{ - u32 val = __raw_readl(base + QCA956X_GMAC_REG_ETH_CFG); - - ag71xx_of_bit(np, "switch-phy-swap", &val, QCA956X_ETH_CFG_SW_PHY_SWAP); - ag71xx_of_bit(np, "switch-phy-addr-swap", &val, - QCA956X_ETH_CFG_SW_PHY_ADDR_SWAP); - - __raw_writel(val, base + QCA956X_GMAC_REG_ETH_CFG); -} - -int ag71xx_setup_gmac(struct device_node *np) -{ - struct device_node *np_dev; - void __iomem *base; - int err = 0; - - np = of_get_child_by_name(np, "gmac-config"); - if (!np) - return 0; - - np_dev = of_parse_phandle(np, "device", 0); - if (!np_dev) - goto out; - - base = of_iomap(np_dev, 0); - if (!base) { - pr_err("%pOF: can't map GMAC registers\n", np_dev); - err = -ENOMEM; - goto err_iomap; - } - - if (of_device_is_compatible(np_dev, "qca,ar9330-gmac")) - ag71xx_setup_gmac_933x(np, base); - else if (of_device_is_compatible(np_dev, "qca,ar9340-gmac")) - ag71xx_setup_gmac_934x(np, base); - else if (of_device_is_compatible(np_dev, "qca,qca9550-gmac")) - ag71xx_setup_gmac_955x(np, base); - else if (of_device_is_compatible(np_dev, "qca,qca9560-gmac")) - ag71xx_setup_gmac_956x(np, base); - - iounmap(base); - -err_iomap: - of_node_put(np_dev); -out: - of_node_put(np); - return err; -} diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c deleted file mode 100644 index 2394ccc90d..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c +++ /dev/null @@ -1,1736 +0,0 @@ -/* - * Atheros AR71xx built-in ethernet mac driver - * - * Copyright (C) 2008-2010 Gabor Juhos - * Copyright (C) 2008 Imre Kaloz - * - * Based on Atheros' AG7100 driver - * - * 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 "ag71xx.h" - -#define AG71XX_DEFAULT_MSG_ENABLE \ - (NETIF_MSG_DRV \ - | NETIF_MSG_PROBE \ - | NETIF_MSG_LINK \ - | NETIF_MSG_TIMER \ - | NETIF_MSG_IFDOWN \ - | NETIF_MSG_IFUP \ - | NETIF_MSG_RX_ERR \ - | NETIF_MSG_TX_ERR) - -static int ag71xx_msg_level = -1; - -module_param_named(msg_level, ag71xx_msg_level, int, 0); -MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)"); - -#define ETH_SWITCH_HEADER_LEN 2 - -static int ag71xx_tx_packets(struct ag71xx *ag, bool flush); - -static inline unsigned int ag71xx_max_frame_len(unsigned int mtu) -{ - return ETH_SWITCH_HEADER_LEN + ETH_HLEN + VLAN_HLEN + mtu + ETH_FCS_LEN; -} - -static void ag71xx_dump_dma_regs(struct ag71xx *ag) -{ - DBG("%s: dma_tx_ctrl=%08x, dma_tx_desc=%08x, dma_tx_status=%08x\n", - ag->dev->name, - ag71xx_rr(ag, AG71XX_REG_TX_CTRL), - ag71xx_rr(ag, AG71XX_REG_TX_DESC), - ag71xx_rr(ag, AG71XX_REG_TX_STATUS)); - - DBG("%s: dma_rx_ctrl=%08x, dma_rx_desc=%08x, dma_rx_status=%08x\n", - ag->dev->name, - ag71xx_rr(ag, AG71XX_REG_RX_CTRL), - ag71xx_rr(ag, AG71XX_REG_RX_DESC), - ag71xx_rr(ag, AG71XX_REG_RX_STATUS)); -} - -static void ag71xx_dump_regs(struct ag71xx *ag) -{ - DBG("%s: mac_cfg1=%08x, mac_cfg2=%08x, ipg=%08x, hdx=%08x, mfl=%08x\n", - ag->dev->name, - ag71xx_rr(ag, AG71XX_REG_MAC_CFG1), - ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), - ag71xx_rr(ag, AG71XX_REG_MAC_IPG), - ag71xx_rr(ag, AG71XX_REG_MAC_HDX), - ag71xx_rr(ag, AG71XX_REG_MAC_MFL)); - DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n", - ag->dev->name, - ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL), - ag71xx_rr(ag, AG71XX_REG_MAC_ADDR1), - ag71xx_rr(ag, AG71XX_REG_MAC_ADDR2)); - DBG("%s: fifo_cfg0=%08x, fifo_cfg1=%08x, fifo_cfg2=%08x\n", - ag->dev->name, - ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), - ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), - ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); - DBG("%s: fifo_cfg3=%08x, fifo_cfg4=%08x, fifo_cfg5=%08x\n", - ag->dev->name, - ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), - ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), - ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); -} - -static inline void ag71xx_dump_intr(struct ag71xx *ag, char *label, u32 intr) -{ - DBG("%s: %s intr=%08x %s%s%s%s%s%s\n", - ag->dev->name, label, intr, - (intr & AG71XX_INT_TX_PS) ? "TXPS " : "", - (intr & AG71XX_INT_TX_UR) ? "TXUR " : "", - (intr & AG71XX_INT_TX_BE) ? "TXBE " : "", - (intr & AG71XX_INT_RX_PR) ? "RXPR " : "", - (intr & AG71XX_INT_RX_OF) ? "RXOF " : "", - (intr & AG71XX_INT_RX_BE) ? "RXBE " : ""); -} - -static void ag71xx_ring_tx_clean(struct ag71xx *ag) -{ - struct ag71xx_ring *ring = &ag->tx_ring; - struct net_device *dev = ag->dev; - int ring_mask = BIT(ring->order) - 1; - u32 bytes_compl = 0, pkts_compl = 0; - - while (ring->curr != ring->dirty) { - struct ag71xx_desc *desc; - u32 i = ring->dirty & ring_mask; - - desc = ag71xx_ring_desc(ring, i); - if (!ag71xx_desc_empty(desc)) { - desc->ctrl = 0; - dev->stats.tx_errors++; - } - - if (ring->buf[i].skb) { - bytes_compl += ring->buf[i].len; - pkts_compl++; - dev_kfree_skb_any(ring->buf[i].skb); - } - ring->buf[i].skb = NULL; - ring->dirty++; - } - - /* flush descriptors */ - wmb(); - - netdev_completed_queue(dev, pkts_compl, bytes_compl); -} - -static void ag71xx_ring_tx_init(struct ag71xx *ag) -{ - struct ag71xx_ring *ring = &ag->tx_ring; - int ring_size = BIT(ring->order); - int ring_mask = BIT(ring->order) - 1; - int i; - - for (i = 0; i < ring_size; i++) { - struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); - - desc->next = (u32) (ring->descs_dma + - AG71XX_DESC_SIZE * ((i + 1) & ring_mask)); - - desc->ctrl = DESC_EMPTY; - ring->buf[i].skb = NULL; - } - - /* flush descriptors */ - wmb(); - - ring->curr = 0; - ring->dirty = 0; - netdev_reset_queue(ag->dev); -} - -static void ag71xx_ring_rx_clean(struct ag71xx *ag) -{ - struct ag71xx_ring *ring = &ag->rx_ring; - int ring_size = BIT(ring->order); - int i; - - if (!ring->buf) - return; - - for (i = 0; i < ring_size; i++) - if (ring->buf[i].rx_buf) { - dma_unmap_single(&ag->pdev->dev, ring->buf[i].dma_addr, - ag->rx_buf_size, DMA_FROM_DEVICE); - skb_free_frag(ring->buf[i].rx_buf); - } -} - -static int ag71xx_buffer_size(struct ag71xx *ag) -{ - return ag->rx_buf_size + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); -} - -static bool ag71xx_fill_rx_buf(struct ag71xx *ag, struct ag71xx_buf *buf, - int offset, - void *(*alloc)(unsigned int size)) -{ - struct ag71xx_ring *ring = &ag->rx_ring; - struct ag71xx_desc *desc = ag71xx_ring_desc(ring, buf - &ring->buf[0]); - void *data; - - data = alloc(ag71xx_buffer_size(ag)); - if (!data) - return false; - - buf->rx_buf = data; - buf->dma_addr = dma_map_single(&ag->pdev->dev, data, ag->rx_buf_size, - DMA_FROM_DEVICE); - desc->data = (u32) buf->dma_addr + offset; - return true; -} - -static int ag71xx_ring_rx_init(struct ag71xx *ag) -{ - struct ag71xx_ring *ring = &ag->rx_ring; - int ring_size = BIT(ring->order); - int ring_mask = BIT(ring->order) - 1; - unsigned int i; - int ret; - - ret = 0; - for (i = 0; i < ring_size; i++) { - struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); - - desc->next = (u32) (ring->descs_dma + - AG71XX_DESC_SIZE * ((i + 1) & ring_mask)); - - DBG("ag71xx: RX desc at %p, next is %08x\n", - desc, desc->next); - } - - for (i = 0; i < ring_size; i++) { - struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); - - if (!ag71xx_fill_rx_buf(ag, &ring->buf[i], ag->rx_buf_offset, - netdev_alloc_frag)) { - ret = -ENOMEM; - break; - } - - desc->ctrl = DESC_EMPTY; - } - - /* flush descriptors */ - wmb(); - - ring->curr = 0; - ring->dirty = 0; - - return ret; -} - -static int ag71xx_ring_rx_refill(struct ag71xx *ag) -{ - struct ag71xx_ring *ring = &ag->rx_ring; - int ring_mask = BIT(ring->order) - 1; - unsigned int count; - int offset = ag->rx_buf_offset; - - count = 0; - for (; ring->curr - ring->dirty > 0; ring->dirty++) { - struct ag71xx_desc *desc; - unsigned int i; - - i = ring->dirty & ring_mask; - desc = ag71xx_ring_desc(ring, i); - - if (!ring->buf[i].rx_buf && - !ag71xx_fill_rx_buf(ag, &ring->buf[i], offset, - napi_alloc_frag)) - break; - - desc->ctrl = DESC_EMPTY; - count++; - } - - /* flush descriptors */ - wmb(); - - DBG("%s: %u rx descriptors refilled\n", ag->dev->name, count); - - return count; -} - -static int ag71xx_rings_init(struct ag71xx *ag) -{ - struct ag71xx_ring *tx = &ag->tx_ring; - struct ag71xx_ring *rx = &ag->rx_ring; - int ring_size = BIT(tx->order) + BIT(rx->order); - int tx_size = BIT(tx->order); - - tx->buf = kzalloc(ring_size * sizeof(*tx->buf), GFP_KERNEL); - if (!tx->buf) - return -ENOMEM; - - tx->descs_cpu = dma_alloc_coherent(&ag->pdev->dev, ring_size * AG71XX_DESC_SIZE, - &tx->descs_dma, GFP_KERNEL); - if (!tx->descs_cpu) { - kfree(tx->buf); - tx->buf = NULL; - return -ENOMEM; - } - - rx->buf = &tx->buf[tx_size]; - rx->descs_cpu = ((void *)tx->descs_cpu) + tx_size * AG71XX_DESC_SIZE; - rx->descs_dma = tx->descs_dma + tx_size * AG71XX_DESC_SIZE; - - ag71xx_ring_tx_init(ag); - return ag71xx_ring_rx_init(ag); -} - -static void ag71xx_rings_free(struct ag71xx *ag) -{ - struct ag71xx_ring *tx = &ag->tx_ring; - struct ag71xx_ring *rx = &ag->rx_ring; - int ring_size = BIT(tx->order) + BIT(rx->order); - - if (tx->descs_cpu) - dma_free_coherent(&ag->pdev->dev, ring_size * AG71XX_DESC_SIZE, - tx->descs_cpu, tx->descs_dma); - - kfree(tx->buf); - - tx->descs_cpu = NULL; - rx->descs_cpu = NULL; - tx->buf = NULL; - rx->buf = NULL; -} - -static void ag71xx_rings_cleanup(struct ag71xx *ag) -{ - ag71xx_ring_rx_clean(ag); - ag71xx_ring_tx_clean(ag); - ag71xx_rings_free(ag); - - netdev_reset_queue(ag->dev); -} - -static unsigned char *ag71xx_speed_str(struct ag71xx *ag) -{ - switch (ag->speed) { - case SPEED_1000: - return "1000"; - case SPEED_100: - return "100"; - case SPEED_10: - return "10"; - } - - return "?"; -} - -static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac) -{ - u32 t; - - t = (((u32) mac[5]) << 24) | (((u32) mac[4]) << 16) - | (((u32) mac[3]) << 8) | ((u32) mac[2]); - - ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t); - - t = (((u32) mac[1]) << 24) | (((u32) mac[0]) << 16); - ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t); -} - -static void ag71xx_dma_reset(struct ag71xx *ag) -{ - u32 val; - int i; - - ag71xx_dump_dma_regs(ag); - - /* stop RX and TX */ - ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); - ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); - - /* - * give the hardware some time to really stop all rx/tx activity - * clearing the descriptors too early causes random memory corruption - */ - mdelay(1); - - /* clear descriptor addresses */ - ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->stop_desc_dma); - ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->stop_desc_dma); - - /* clear pending RX/TX interrupts */ - for (i = 0; i < 256; i++) { - ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); - ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); - } - - /* clear pending errors */ - ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF); - ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR); - - val = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); - if (val) - pr_alert("%s: unable to clear DMA Rx status: %08x\n", - ag->dev->name, val); - - val = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); - - /* mask out reserved bits */ - val &= ~0xff000000; - - if (val) - pr_alert("%s: unable to clear DMA Tx status: %08x\n", - ag->dev->name, val); - - ag71xx_dump_dma_regs(ag); -} - -#define MAC_CFG1_INIT (MAC_CFG1_RXE | MAC_CFG1_TXE | \ - MAC_CFG1_SRX | MAC_CFG1_STX) - -#define FIFO_CFG0_INIT (FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT) - -#define FIFO_CFG4_INIT (FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \ - FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \ - FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \ - FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \ - FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \ - FIFO_CFG4_VT) - -#define FIFO_CFG5_INIT (FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \ - FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \ - FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \ - FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \ - FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \ - FIFO_CFG5_17 | FIFO_CFG5_SF) - -static void ag71xx_hw_stop(struct ag71xx *ag) -{ - /* disable all interrupts and stop the rx/tx engine */ - ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, 0); - ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); - ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); -} - -static void ag71xx_hw_setup(struct ag71xx *ag) -{ - struct device_node *np = ag->pdev->dev.of_node; - u32 init = MAC_CFG1_INIT; - - /* setup MAC configuration registers */ - if (of_property_read_bool(np, "flow-control")) - init |= MAC_CFG1_TFC | MAC_CFG1_RFC; - ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, init); - - ag71xx_sb(ag, AG71XX_REG_MAC_CFG2, - MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK); - - /* setup max frame length to zero */ - ag71xx_wr(ag, AG71XX_REG_MAC_MFL, 0); - - /* setup FIFO configuration registers */ - ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT); - ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, ag->fifodata[0]); - ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, ag->fifodata[1]); - ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT); - ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT); -} - -static void ag71xx_hw_init(struct ag71xx *ag) -{ - ag71xx_hw_stop(ag); - - ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR); - udelay(20); - - reset_control_assert(ag->mac_reset); - if (ag->mdio_reset) - reset_control_assert(ag->mdio_reset); - msleep(100); - reset_control_deassert(ag->mac_reset); - if (ag->mdio_reset) - reset_control_deassert(ag->mdio_reset); - msleep(200); - - ag71xx_hw_setup(ag); - - ag71xx_dma_reset(ag); -} - -static void ag71xx_fast_reset(struct ag71xx *ag) -{ - struct net_device *dev = ag->dev; - u32 rx_ds; - u32 mii_reg; - - ag71xx_hw_stop(ag); - wmb(); - - mii_reg = ag71xx_rr(ag, AG71XX_REG_MII_CFG); - rx_ds = ag71xx_rr(ag, AG71XX_REG_RX_DESC); - - ag71xx_tx_packets(ag, true); - - reset_control_assert(ag->mac_reset); - udelay(10); - reset_control_deassert(ag->mac_reset); - udelay(10); - - ag71xx_dma_reset(ag); - ag71xx_hw_setup(ag); - ag->tx_ring.curr = 0; - ag->tx_ring.dirty = 0; - netdev_reset_queue(ag->dev); - - /* setup max frame length */ - ag71xx_wr(ag, AG71XX_REG_MAC_MFL, - ag71xx_max_frame_len(ag->dev->mtu)); - - ag71xx_wr(ag, AG71XX_REG_RX_DESC, rx_ds); - ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma); - ag71xx_wr(ag, AG71XX_REG_MII_CFG, mii_reg); - - ag71xx_hw_set_macaddr(ag, dev->dev_addr); -} - -static void ag71xx_hw_start(struct ag71xx *ag) -{ - /* start RX engine */ - ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); - - /* enable interrupts */ - ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, AG71XX_INT_INIT); - - netif_wake_queue(ag->dev); -} - -static void ath79_set_pllval(struct ag71xx *ag) -{ - u32 pll_reg = ag->pllreg[1]; - u32 pll_val; - - if (!ag->pllregmap) - return; - - switch (ag->speed) { - case SPEED_10: - pll_val = ag->plldata[2]; - break; - case SPEED_100: - pll_val = ag->plldata[1]; - break; - case SPEED_1000: - pll_val = ag->plldata[0]; - break; - default: - BUG(); - } - - if (pll_val) - regmap_write(ag->pllregmap, pll_reg, pll_val); -} - -static void ath79_set_pll(struct ag71xx *ag) -{ - u32 pll_cfg = ag->pllreg[0]; - u32 pll_shift = ag->pllreg[2]; - - if (!ag->pllregmap) - return; - - regmap_update_bits(ag->pllregmap, pll_cfg, 3 << pll_shift, 2 << pll_shift); - udelay(100); - - ath79_set_pllval(ag); - - regmap_update_bits(ag->pllregmap, pll_cfg, 3 << pll_shift, 3 << pll_shift); - udelay(100); - - regmap_update_bits(ag->pllregmap, pll_cfg, 3 << pll_shift, 0); - udelay(100); -} - -static void ag71xx_bit_set(void __iomem *reg, u32 bit) -{ - u32 val; - - val = __raw_readl(reg) | bit; - __raw_writel(val, reg); - __raw_readl(reg); -} - -static void ag71xx_bit_clear(void __iomem *reg, u32 bit) -{ - u32 val; - - val = __raw_readl(reg) & ~bit; - __raw_writel(val, reg); - __raw_readl(reg); -} - -static void ag71xx_sgmii_init_qca955x(struct device_node *np) -{ - struct device_node *np_dev; - void __iomem *gmac_base; - u32 mr_an_status; - u32 sgmii_status; - u8 tries = 0; - int err = 0; - - np = of_get_child_by_name(np, "gmac-config"); - if (!np) - return; - - np_dev = of_parse_phandle(np, "device", 0); - if (!np_dev) - goto out; - - gmac_base = of_iomap(np_dev, 0); - if (!gmac_base) { - pr_err("%pOF: can't map GMAC registers\n", np_dev); - err = -ENOMEM; - goto err_iomap; - } - - mr_an_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_MR_AN_STATUS); - if (!(mr_an_status & QCA955X_MR_AN_STATUS_AN_ABILITY)) - goto sgmii_out; - - /* SGMII reset sequence */ - __raw_writel(QCA955X_SGMII_RESET_RX_CLK_N_RESET, - gmac_base + QCA955X_GMAC_REG_SGMII_RESET); - __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_RESET); - udelay(10); - - ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, - QCA955X_SGMII_RESET_HW_RX_125M_N); - udelay(10); - - ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, - QCA955X_SGMII_RESET_RX_125M_N); - udelay(10); - - ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, - QCA955X_SGMII_RESET_TX_125M_N); - udelay(10); - - ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, - QCA955X_SGMII_RESET_RX_CLK_N); - udelay(10); - - ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, - QCA955X_SGMII_RESET_TX_CLK_N); - udelay(10); - - /* - * The following is what QCA has to say about what happens here: - * - * Across resets SGMII link status goes to weird state. - * If SGMII_DEBUG register reads other than 0x1f or 0x10, - * we are for sure in a bad state. - * - * Issue a PHY reset in MR_AN_CONTROL to keep going. - */ - do { - ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL, - QCA955X_MR_AN_CONTROL_PHY_RESET | - QCA955X_MR_AN_CONTROL_AN_ENABLE); - udelay(200); - ag71xx_bit_clear(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL, - QCA955X_MR_AN_CONTROL_PHY_RESET); - mdelay(300); - sgmii_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_DEBUG) & - QCA955X_SGMII_DEBUG_TX_STATE_MASK; - - if (tries++ >= 20) { - pr_err("ag71xx: max retries for SGMII fixup exceeded\n"); - break; - } - } while (!(sgmii_status == 0xf || sgmii_status == 0x10)); - -sgmii_out: - iounmap(gmac_base); -err_iomap: - of_node_put(np_dev); -out: - of_node_put(np); -} - -static void ath79_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if) -{ - u32 t; - - t = __raw_readl(ag->mii_base); - t &= ~(AR71XX_MII_CTRL_IF_MASK); - t |= (mii_if & AR71XX_MII_CTRL_IF_MASK); - __raw_writel(t, ag->mii_base); -} - -static void ath79_mii0_ctrl_set_if(struct ag71xx *ag) -{ - unsigned int mii_if; - - switch (ag->phy_if_mode) { - case PHY_INTERFACE_MODE_MII: - mii_if = AR71XX_MII0_CTRL_IF_MII; - break; - case PHY_INTERFACE_MODE_GMII: - mii_if = AR71XX_MII0_CTRL_IF_GMII; - break; - case PHY_INTERFACE_MODE_RGMII: - case PHY_INTERFACE_MODE_RGMII_ID: - mii_if = AR71XX_MII0_CTRL_IF_RGMII; - break; - case PHY_INTERFACE_MODE_RMII: - mii_if = AR71XX_MII0_CTRL_IF_RMII; - break; - default: - WARN(1, "Impossible PHY mode defined.\n"); - return; - } - - ath79_mii_ctrl_set_if(ag, mii_if); -} - -static void ath79_mii1_ctrl_set_if(struct ag71xx *ag) -{ - unsigned int mii_if; - - switch (ag->phy_if_mode) { - case PHY_INTERFACE_MODE_RMII: - mii_if = AR71XX_MII1_CTRL_IF_RMII; - break; - case PHY_INTERFACE_MODE_RGMII: - case PHY_INTERFACE_MODE_RGMII_ID: - mii_if = AR71XX_MII1_CTRL_IF_RGMII; - break; - default: - WARN(1, "Impossible PHY mode defined.\n"); - return; - } - - ath79_mii_ctrl_set_if(ag, mii_if); -} - -static void ath79_mii_ctrl_set_speed(struct ag71xx *ag) -{ - unsigned int mii_speed; - u32 t; - - if (!ag->mii_base) - return; - - switch (ag->speed) { - case SPEED_10: - mii_speed = AR71XX_MII_CTRL_SPEED_10; - break; - case SPEED_100: - mii_speed = AR71XX_MII_CTRL_SPEED_100; - break; - case SPEED_1000: - mii_speed = AR71XX_MII_CTRL_SPEED_1000; - break; - default: - BUG(); - } - - t = __raw_readl(ag->mii_base); - t &= ~(AR71XX_MII_CTRL_SPEED_MASK << AR71XX_MII_CTRL_SPEED_SHIFT); - t |= mii_speed << AR71XX_MII_CTRL_SPEED_SHIFT; - __raw_writel(t, ag->mii_base); -} - -static void -__ag71xx_link_adjust(struct ag71xx *ag, bool update) -{ - struct device_node *np = ag->pdev->dev.of_node; - u32 cfg2; - u32 ifctl; - u32 fifo5; - - if (!ag->link && update) { - ag71xx_hw_stop(ag); - netif_carrier_off(ag->dev); - if (netif_msg_link(ag)) - pr_info("%s: link down\n", ag->dev->name); - return; - } - - if (!of_device_is_compatible(np, "qca,ar9130-eth") && - !of_device_is_compatible(np, "qca,ar7100-eth")) - ag71xx_fast_reset(ag); - - cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2); - cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX); - cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0; - - ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL); - ifctl &= ~(MAC_IFCTL_SPEED); - - fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5); - fifo5 &= ~FIFO_CFG5_BM; - - switch (ag->speed) { - case SPEED_1000: - cfg2 |= MAC_CFG2_IF_1000; - fifo5 |= FIFO_CFG5_BM; - break; - case SPEED_100: - cfg2 |= MAC_CFG2_IF_10_100; - ifctl |= MAC_IFCTL_SPEED; - break; - case SPEED_10: - cfg2 |= MAC_CFG2_IF_10_100; - break; - default: - BUG(); - return; - } - - if (ag->tx_ring.desc_split) { - ag->fifodata[2] &= 0xffff; - ag->fifodata[2] |= ((2048 - ag->tx_ring.desc_split) / 4) << 16; - } - - ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, ag->fifodata[2]); - - if (update) { - if (of_device_is_compatible(np, "qca,ar7100-eth") || - of_device_is_compatible(np, "qca,ar9130-eth")) { - ath79_set_pll(ag); - ath79_mii_ctrl_set_speed(ag); - } else if (of_device_is_compatible(np, "qca,ar7242-eth") || - of_device_is_compatible(np, "qca,ar9340-eth") || - of_device_is_compatible(np, "qca,qca9550-eth") || - of_device_is_compatible(np, "qca,qca9560-eth")) { - ath79_set_pllval(ag); - if (of_property_read_bool(np, "qca955x-sgmii-fixup")) - ag71xx_sgmii_init_qca955x(np); - } - } - - ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2); - ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5); - ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl); - - if (of_device_is_compatible(np, "qca,qca9530-eth") || - of_device_is_compatible(np, "qca,qca9560-eth")) { - /* - * The rx ring buffer can stall on small packets on QCA953x and - * QCA956x. Disabling the inline checksum engine fixes the stall. - * The wr, rr functions cannot be used since this hidden register - * is outside of the normal ag71xx register block. - */ - void __iomem *dam = ioremap_nocache(0xb90001bc, 0x4); - if (dam) { - __raw_writel(__raw_readl(dam) & ~BIT(27), dam); - (void)__raw_readl(dam); - iounmap(dam); - } - } - - ag71xx_hw_start(ag); - - netif_carrier_on(ag->dev); - if (update && netif_msg_link(ag)) - pr_info("%s: link up (%sMbps/%s duplex)\n", - ag->dev->name, - ag71xx_speed_str(ag), - (DUPLEX_FULL == ag->duplex) ? "Full" : "Half"); - - ag71xx_dump_regs(ag); -} - -void ag71xx_link_adjust(struct ag71xx *ag) -{ - __ag71xx_link_adjust(ag, true); -} - -static int ag71xx_hw_enable(struct ag71xx *ag) -{ - int ret; - - ret = ag71xx_rings_init(ag); - if (ret) - return ret; - - napi_enable(&ag->napi); - ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma); - ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma); - netif_start_queue(ag->dev); - - return 0; -} - -static void ag71xx_hw_disable(struct ag71xx *ag) -{ - netif_stop_queue(ag->dev); - - ag71xx_hw_stop(ag); - ag71xx_dma_reset(ag); - - napi_disable(&ag->napi); - del_timer_sync(&ag->oom_timer); - - ag71xx_rings_cleanup(ag); -} - -static int ag71xx_open(struct net_device *dev) -{ - struct ag71xx *ag = netdev_priv(dev); - unsigned int max_frame_len; - int ret; - - netif_carrier_off(dev); - max_frame_len = ag71xx_max_frame_len(dev->mtu); - ag->rx_buf_size = SKB_DATA_ALIGN(max_frame_len + NET_SKB_PAD + NET_IP_ALIGN); - - /* setup max frame length */ - ag71xx_wr(ag, AG71XX_REG_MAC_MFL, max_frame_len); - ag71xx_hw_set_macaddr(ag, dev->dev_addr); - - ret = ag71xx_hw_enable(ag); - if (ret) - goto err; - - phy_start(ag->phy_dev); - - return 0; - -err: - ag71xx_rings_cleanup(ag); - return ret; -} - -static int ag71xx_stop(struct net_device *dev) -{ - unsigned long flags; - struct ag71xx *ag = netdev_priv(dev); - - netif_carrier_off(dev); - phy_stop(ag->phy_dev); - - spin_lock_irqsave(&ag->lock, flags); - if (ag->link) { - ag->link = 0; - ag71xx_link_adjust(ag); - } - spin_unlock_irqrestore(&ag->lock, flags); - - ag71xx_hw_disable(ag); - - return 0; -} - -static int ag71xx_fill_dma_desc(struct ag71xx_ring *ring, u32 addr, int len) -{ - int i; - struct ag71xx_desc *desc; - int ring_mask = BIT(ring->order) - 1; - int ndesc = 0; - int split = ring->desc_split; - - if (!split) - split = len; - - while (len > 0) { - unsigned int cur_len = len; - - i = (ring->curr + ndesc) & ring_mask; - desc = ag71xx_ring_desc(ring, i); - - if (!ag71xx_desc_empty(desc)) - return -1; - - if (cur_len > split) { - cur_len = split; - - /* - * TX will hang if DMA transfers <= 4 bytes, - * make sure next segment is more than 4 bytes long. - */ - if (len <= split + 4) - cur_len -= 4; - } - - desc->data = addr; - addr += cur_len; - len -= cur_len; - - if (len > 0) - cur_len |= DESC_MORE; - - /* prevent early tx attempt of this descriptor */ - if (!ndesc) - cur_len |= DESC_EMPTY; - - desc->ctrl = cur_len; - ndesc++; - } - - return ndesc; -} - -static netdev_tx_t ag71xx_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct ag71xx *ag = netdev_priv(dev); - struct ag71xx_ring *ring = &ag->tx_ring; - int ring_mask = BIT(ring->order) - 1; - int ring_size = BIT(ring->order); - struct ag71xx_desc *desc; - dma_addr_t dma_addr; - int i, n, ring_min; - - if (skb->len <= 4) { - DBG("%s: packet len is too small\n", ag->dev->name); - goto err_drop; - } - - dma_addr = dma_map_single(&ag->pdev->dev, skb->data, skb->len, - DMA_TO_DEVICE); - - i = ring->curr & ring_mask; - desc = ag71xx_ring_desc(ring, i); - - /* setup descriptor fields */ - n = ag71xx_fill_dma_desc(ring, (u32) dma_addr, skb->len & ag->desc_pktlen_mask); - if (n < 0) - goto err_drop_unmap; - - i = (ring->curr + n - 1) & ring_mask; - ring->buf[i].len = skb->len; - ring->buf[i].skb = skb; - - netdev_sent_queue(dev, skb->len); - - skb_tx_timestamp(skb); - - desc->ctrl &= ~DESC_EMPTY; - ring->curr += n; - - /* flush descriptor */ - wmb(); - - ring_min = 2; - if (ring->desc_split) - ring_min *= AG71XX_TX_RING_DS_PER_PKT; - - if (ring->curr - ring->dirty >= ring_size - ring_min) { - DBG("%s: tx queue full\n", dev->name); - netif_stop_queue(dev); - } - - DBG("%s: packet injected into TX queue\n", ag->dev->name); - - /* enable TX engine */ - ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE); - - return NETDEV_TX_OK; - -err_drop_unmap: - dma_unmap_single(&ag->pdev->dev, dma_addr, skb->len, DMA_TO_DEVICE); - -err_drop: - dev->stats.tx_dropped++; - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct ag71xx *ag = netdev_priv(dev); - - - switch (cmd) { - case SIOCSIFHWADDR: - if (copy_from_user - (dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr))) - return -EFAULT; - return 0; - - case SIOCGIFHWADDR: - if (copy_to_user - (ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr))) - return -EFAULT; - return 0; - - case SIOCGMIIPHY: - case SIOCGMIIREG: - case SIOCSMIIREG: - if (ag->phy_dev == NULL) - break; - - return phy_mii_ioctl(ag->phy_dev, ifr, cmd); - - default: - break; - } - - return -EOPNOTSUPP; -} - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)) -static void ag71xx_oom_timer_handler(unsigned long data) -{ - struct net_device *dev = (struct net_device *) data; - struct ag71xx *ag = netdev_priv(dev); -#else -static void ag71xx_oom_timer_handler(struct timer_list *t) -{ - struct ag71xx *ag = from_timer(ag, t, oom_timer); -#endif - - napi_schedule(&ag->napi); -} - -static void ag71xx_tx_timeout(struct net_device *dev) -{ - struct ag71xx *ag = netdev_priv(dev); - - if (netif_msg_tx_err(ag)) - pr_info("%s: tx timeout\n", ag->dev->name); - - schedule_delayed_work(&ag->restart_work, 1); -} - -static void ag71xx_restart_work_func(struct work_struct *work) -{ - struct ag71xx *ag = container_of(work, struct ag71xx, restart_work.work); - - rtnl_lock(); - ag71xx_hw_disable(ag); - ag71xx_hw_enable(ag); - if (ag->link) - __ag71xx_link_adjust(ag, false); - rtnl_unlock(); -} - -static bool ag71xx_check_dma_stuck(struct ag71xx *ag) -{ - unsigned long timestamp; - u32 rx_sm, tx_sm, rx_fd; - - timestamp = netdev_get_tx_queue(ag->dev, 0)->trans_start; - if (likely(time_before(jiffies, timestamp + HZ/10))) - return false; - - if (!netif_carrier_ok(ag->dev)) - return false; - - rx_sm = ag71xx_rr(ag, AG71XX_REG_RX_SM); - if ((rx_sm & 0x7) == 0x3 && ((rx_sm >> 4) & 0x7) == 0x6) - return true; - - tx_sm = ag71xx_rr(ag, AG71XX_REG_TX_SM); - rx_fd = ag71xx_rr(ag, AG71XX_REG_FIFO_DEPTH); - if (((tx_sm >> 4) & 0x7) == 0 && ((rx_sm & 0x7) == 0) && - ((rx_sm >> 4) & 0x7) == 0 && rx_fd == 0) - return true; - - return false; -} - -static int ag71xx_tx_packets(struct ag71xx *ag, bool flush) -{ - struct ag71xx_ring *ring = &ag->tx_ring; - bool dma_stuck = false; - int ring_mask = BIT(ring->order) - 1; - int ring_size = BIT(ring->order); - int sent = 0; - int bytes_compl = 0; - int n = 0; - - DBG("%s: processing TX ring\n", ag->dev->name); - - while (ring->dirty + n != ring->curr) { - unsigned int i = (ring->dirty + n) & ring_mask; - struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); - struct sk_buff *skb = ring->buf[i].skb; - - if (!flush && !ag71xx_desc_empty(desc)) { - if (ag->tx_hang_workaround && - ag71xx_check_dma_stuck(ag)) { - schedule_delayed_work(&ag->restart_work, HZ / 2); - dma_stuck = true; - } - break; - } - - if (flush) - desc->ctrl |= DESC_EMPTY; - - n++; - if (!skb) - continue; - - dev_kfree_skb_any(skb); - ring->buf[i].skb = NULL; - - bytes_compl += ring->buf[i].len; - - sent++; - ring->dirty += n; - - while (n > 0) { - ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); - n--; - } - } - - DBG("%s: %d packets sent out\n", ag->dev->name, sent); - - if (!sent) - return 0; - - ag->dev->stats.tx_bytes += bytes_compl; - ag->dev->stats.tx_packets += sent; - - netdev_completed_queue(ag->dev, sent, bytes_compl); - if ((ring->curr - ring->dirty) < (ring_size * 3) / 4) - netif_wake_queue(ag->dev); - - if (!dma_stuck) - cancel_delayed_work(&ag->restart_work); - - return sent; -} - -static int ag71xx_rx_packets(struct ag71xx *ag, int limit) -{ - struct net_device *dev = ag->dev; - struct ag71xx_ring *ring = &ag->rx_ring; - unsigned int pktlen_mask = ag->desc_pktlen_mask; - unsigned int offset = ag->rx_buf_offset; - int ring_mask = BIT(ring->order) - 1; - int ring_size = BIT(ring->order); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) - struct list_head rx_list; - struct sk_buff *next; -#else - struct sk_buff_head queue; -#endif - struct sk_buff *skb; - int done = 0; - - DBG("%s: rx packets, limit=%d, curr=%u, dirty=%u\n", - dev->name, limit, ring->curr, ring->dirty); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) - INIT_LIST_HEAD(&rx_list); -#else - skb_queue_head_init(&queue); -#endif - - while (done < limit) { - unsigned int i = ring->curr & ring_mask; - struct ag71xx_desc *desc = ag71xx_ring_desc(ring, i); - int pktlen; - int err = 0; - - if (ag71xx_desc_empty(desc)) - break; - - if ((ring->dirty + ring_size) == ring->curr) { - ag71xx_assert(0); - break; - } - - ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); - - pktlen = desc->ctrl & pktlen_mask; - pktlen -= ETH_FCS_LEN; - - dma_unmap_single(&ag->pdev->dev, ring->buf[i].dma_addr, - ag->rx_buf_size, DMA_FROM_DEVICE); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += pktlen; - - skb = build_skb(ring->buf[i].rx_buf, ag71xx_buffer_size(ag)); - if (!skb) { - skb_free_frag(ring->buf[i].rx_buf); - goto next; - } - - skb_reserve(skb, offset); - skb_put(skb, pktlen); - - if (err) { - dev->stats.rx_dropped++; - kfree_skb(skb); - } else { - skb->dev = dev; - skb->ip_summed = CHECKSUM_NONE; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) - list_add_tail(&skb->list, &rx_list); -#else - __skb_queue_tail(&queue, skb); -#endif - } - -next: - ring->buf[i].rx_buf = NULL; - done++; - - ring->curr++; - } - - ag71xx_ring_rx_refill(ag); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)) - list_for_each_entry_safe(skb, next, &rx_list, list) - skb->protocol = eth_type_trans(skb, dev); - netif_receive_skb_list(&rx_list); -#else - while ((skb = __skb_dequeue(&queue)) != NULL) { - skb->protocol = eth_type_trans(skb, dev); - netif_receive_skb(skb); - } -#endif - - DBG("%s: rx finish, curr=%u, dirty=%u, done=%d\n", - dev->name, ring->curr, ring->dirty, done); - - return done; -} - -static int ag71xx_poll(struct napi_struct *napi, int limit) -{ - struct ag71xx *ag = container_of(napi, struct ag71xx, napi); - struct net_device *dev = ag->dev; - struct ag71xx_ring *rx_ring = &ag->rx_ring; - int rx_ring_size = BIT(rx_ring->order); - unsigned long flags; - u32 status; - int tx_done; - int rx_done; - - tx_done = ag71xx_tx_packets(ag, false); - - DBG("%s: processing RX ring\n", dev->name); - rx_done = ag71xx_rx_packets(ag, limit); - - ag71xx_debugfs_update_napi_stats(ag, rx_done, tx_done); - - if (rx_ring->buf[rx_ring->dirty % rx_ring_size].rx_buf == NULL) - goto oom; - - status = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); - if (unlikely(status & RX_STATUS_OF)) { - ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_OF); - dev->stats.rx_fifo_errors++; - - /* restart RX */ - ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); - } - - if (rx_done < limit) { - if (status & RX_STATUS_PR) - goto more; - - status = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); - if (status & TX_STATUS_PS) - goto more; - - DBG("%s: disable polling mode, rx=%d, tx=%d,limit=%d\n", - dev->name, rx_done, tx_done, limit); - - napi_complete(napi); - - /* enable interrupts */ - spin_lock_irqsave(&ag->lock, flags); - ag71xx_int_enable(ag, AG71XX_INT_POLL); - spin_unlock_irqrestore(&ag->lock, flags); - return rx_done; - } - -more: - DBG("%s: stay in polling mode, rx=%d, tx=%d, limit=%d\n", - dev->name, rx_done, tx_done, limit); - return limit; - -oom: - if (netif_msg_rx_err(ag)) - pr_info("%s: out of memory\n", dev->name); - - mod_timer(&ag->oom_timer, jiffies + AG71XX_OOM_REFILL); - napi_complete(napi); - return 0; -} - -static irqreturn_t ag71xx_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct ag71xx *ag = netdev_priv(dev); - u32 status; - - status = ag71xx_rr(ag, AG71XX_REG_INT_STATUS); - ag71xx_dump_intr(ag, "raw", status); - - if (unlikely(!status)) - return IRQ_NONE; - - if (unlikely(status & AG71XX_INT_ERR)) { - if (status & AG71XX_INT_TX_BE) { - ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE); - dev_err(&dev->dev, "TX BUS error\n"); - } - if (status & AG71XX_INT_RX_BE) { - ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE); - dev_err(&dev->dev, "RX BUS error\n"); - } - } - - if (likely(status & AG71XX_INT_POLL)) { - ag71xx_int_disable(ag, AG71XX_INT_POLL); - DBG("%s: enable polling mode\n", dev->name); - napi_schedule(&ag->napi); - } - - ag71xx_debugfs_update_int_stats(ag, status); - - return IRQ_HANDLED; -} - -static int ag71xx_change_mtu(struct net_device *dev, int new_mtu) -{ - struct ag71xx *ag = netdev_priv(dev); - - dev->mtu = new_mtu; - ag71xx_wr(ag, AG71XX_REG_MAC_MFL, - ag71xx_max_frame_len(dev->mtu)); - - return 0; -} - -static const struct net_device_ops ag71xx_netdev_ops = { - .ndo_open = ag71xx_open, - .ndo_stop = ag71xx_stop, - .ndo_start_xmit = ag71xx_hard_start_xmit, - .ndo_do_ioctl = ag71xx_do_ioctl, - .ndo_tx_timeout = ag71xx_tx_timeout, - .ndo_change_mtu = ag71xx_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int ag71xx_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct net_device *dev; - struct resource *res; - struct ag71xx *ag; - const void *mac_addr; - u32 max_frame_len; - int tx_size, err; - - if (!np) - return -ENODEV; - - dev = devm_alloc_etherdev(&pdev->dev, sizeof(*ag)); - if (!dev) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - err = ag71xx_setup_gmac(np); - if (err) - return err; - - SET_NETDEV_DEV(dev, &pdev->dev); - - ag = netdev_priv(dev); - ag->pdev = pdev; - ag->dev = dev; - ag->msg_enable = netif_msg_init(ag71xx_msg_level, - AG71XX_DEFAULT_MSG_ENABLE); - spin_lock_init(&ag->lock); - - ag->mac_reset = devm_reset_control_get_exclusive(&pdev->dev, "mac"); - if (IS_ERR(ag->mac_reset)) { - dev_err(&pdev->dev, "missing mac reset\n"); - return PTR_ERR(ag->mac_reset); - } - - ag->mdio_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "mdio"); - - if (of_property_read_u32_array(np, "fifo-data", ag->fifodata, 3)) { - if (of_device_is_compatible(np, "qca,ar9130-eth") || - of_device_is_compatible(np, "qca,ar7100-eth")) { - ag->fifodata[0] = 0x0fff0000; - ag->fifodata[1] = 0x00001fff; - } else { - ag->fifodata[0] = 0x0010ffff; - ag->fifodata[1] = 0x015500aa; - ag->fifodata[2] = 0x01f00140; - } - if (of_device_is_compatible(np, "qca,ar9130-eth")) - ag->fifodata[2] = 0x00780fff; - else if (of_device_is_compatible(np, "qca,ar7100-eth")) - ag->fifodata[2] = 0x008001ff; - } - - if (of_property_read_u32_array(np, "pll-data", ag->plldata, 3)) - dev_dbg(&pdev->dev, "failed to read pll-data property\n"); - - if (of_property_read_u32_array(np, "pll-reg", ag->pllreg, 3)) - dev_dbg(&pdev->dev, "failed to read pll-reg property\n"); - - ag->pllregmap = syscon_regmap_lookup_by_phandle(np, "pll-handle"); - if (IS_ERR(ag->pllregmap)) { - dev_dbg(&pdev->dev, "failed to read pll-handle property\n"); - ag->pllregmap = NULL; - } - - ag->mac_base = devm_ioremap_nocache(&pdev->dev, res->start, - res->end - res->start + 1); - if (!ag->mac_base) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - ag->mii_base = devm_ioremap_nocache(&pdev->dev, res->start, - res->end - res->start + 1); - if (!ag->mii_base) - return -ENOMEM; - } - - dev->irq = platform_get_irq(pdev, 0); - err = devm_request_irq(&pdev->dev, dev->irq, ag71xx_interrupt, - 0x0, dev_name(&pdev->dev), dev); - if (err) { - dev_err(&pdev->dev, "unable to request IRQ %d\n", dev->irq); - return err; - } - - dev->netdev_ops = &ag71xx_netdev_ops; - dev->ethtool_ops = &ag71xx_ethtool_ops; - - INIT_DELAYED_WORK(&ag->restart_work, ag71xx_restart_work_func); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)) - init_timer(&ag->oom_timer); - ag->oom_timer.data = (unsigned long) dev; - ag->oom_timer.function = ag71xx_oom_timer_handler; -#else - timer_setup(&ag->oom_timer, ag71xx_oom_timer_handler, 0); -#endif - - tx_size = AG71XX_TX_RING_SIZE_DEFAULT; - ag->rx_ring.order = ag71xx_ring_size_order(AG71XX_RX_RING_SIZE_DEFAULT); - - if (of_device_is_compatible(np, "qca,ar9340-eth") || - of_device_is_compatible(np, "qca,qca9530-eth") || - of_device_is_compatible(np, "qca,qca9550-eth") || - of_device_is_compatible(np, "qca,qca9560-eth")) - ag->desc_pktlen_mask = SZ_16K - 1; - else - ag->desc_pktlen_mask = SZ_4K - 1; - - if (ag->desc_pktlen_mask == SZ_16K - 1 && - !of_device_is_compatible(np, "qca,qca9550-eth") && - !of_device_is_compatible(np, "qca,qca9560-eth")) - max_frame_len = ag->desc_pktlen_mask; - else - max_frame_len = 1540; - - dev->min_mtu = 68; - dev->max_mtu = max_frame_len - ag71xx_max_frame_len(0); - - if (of_device_is_compatible(np, "qca,ar7240-eth") || - of_device_is_compatible(np, "qca,ar7241-eth") || - of_device_is_compatible(np, "qca,ar7242-eth") || - of_device_is_compatible(np, "qca,ar9330-eth") || - of_device_is_compatible(np, "qca,ar9340-eth") || - of_device_is_compatible(np, "qca,qca9530-eth") || - of_device_is_compatible(np, "qca,qca9550-eth") || - of_device_is_compatible(np, "qca,qca9560-eth")) - ag->tx_hang_workaround = 1; - - ag->rx_buf_offset = NET_SKB_PAD; - if (!of_device_is_compatible(np, "qca,ar7100-eth") && - !of_device_is_compatible(np, "qca,ar9130-eth")) - ag->rx_buf_offset += NET_IP_ALIGN; - - if (of_device_is_compatible(np, "qca,ar7100-eth")) { - ag->tx_ring.desc_split = AG71XX_TX_RING_SPLIT; - tx_size *= AG71XX_TX_RING_DS_PER_PKT; - } - ag->tx_ring.order = ag71xx_ring_size_order(tx_size); - - ag->stop_desc = dmam_alloc_coherent(&pdev->dev, - sizeof(struct ag71xx_desc), - &ag->stop_desc_dma, GFP_KERNEL); - if (!ag->stop_desc) - return -ENOMEM; - - ag->stop_desc->data = 0; - ag->stop_desc->ctrl = 0; - ag->stop_desc->next = (u32) ag->stop_desc_dma; - - mac_addr = of_get_mac_address(np); - if (mac_addr) - memcpy(dev->dev_addr, mac_addr, ETH_ALEN); - if (!mac_addr || !is_valid_ether_addr(dev->dev_addr)) { - dev_err(&pdev->dev, "invalid MAC address, using random address\n"); - eth_random_addr(dev->dev_addr); - } - - ag->phy_if_mode = of_get_phy_mode(np); - if (ag->phy_if_mode < 0) { - dev_err(&pdev->dev, "missing phy-mode property in DT\n"); - return ag->phy_if_mode; - } - - if (of_property_read_u32(np, "qca,mac-idx", &ag->mac_idx)) - ag->mac_idx = -1; - if (ag->mii_base) - switch (ag->mac_idx) { - case 0: - ath79_mii0_ctrl_set_if(ag); - break; - case 1: - ath79_mii1_ctrl_set_if(ag); - break; - default: - break; - } - - netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT); - - ag71xx_dump_regs(ag); - - ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, 0); - - ag71xx_hw_init(ag); - - ag71xx_dump_regs(ag); - - /* - * populate current node to register mdio-bus as a subdevice. - * the mdio bus works independently on ar7241 and later chips - * and we need to load mdio1 before gmac0, which can be done - * by adding a "simple-mfd" compatible to gmac node. The - * following code checks OF_POPULATED_BUS flag before populating - * to avoid duplicated population. - */ - if (!of_node_check_flag(np, OF_POPULATED_BUS)) { - err = of_platform_populate(np, NULL, NULL, &pdev->dev); - if (err) - return err; - } - - err = ag71xx_phy_connect(ag); - if (err) - return err; - - err = ag71xx_debugfs_init(ag); - if (err) - goto err_phy_disconnect; - - platform_set_drvdata(pdev, dev); - - err = register_netdev(dev); - if (err) { - dev_err(&pdev->dev, "unable to register net device\n"); - platform_set_drvdata(pdev, NULL); - ag71xx_debugfs_exit(ag); - goto err_phy_disconnect; - } - - pr_info("%s: Atheros AG71xx at 0x%08lx, irq %d, mode: %s\n", - dev->name, (unsigned long) ag->mac_base, dev->irq, - phy_modes(ag->phy_if_mode)); - - return 0; - -err_phy_disconnect: - ag71xx_phy_disconnect(ag); - return err; -} - -static int ag71xx_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct ag71xx *ag; - - if (!dev) - return 0; - - ag = netdev_priv(dev); - ag71xx_debugfs_exit(ag); - ag71xx_phy_disconnect(ag); - unregister_netdev(dev); - platform_set_drvdata(pdev, NULL); - return 0; -} - -static const struct of_device_id ag71xx_match[] = { - { .compatible = "qca,ar7100-eth" }, - { .compatible = "qca,ar7240-eth" }, - { .compatible = "qca,ar7241-eth" }, - { .compatible = "qca,ar7242-eth" }, - { .compatible = "qca,ar9130-eth" }, - { .compatible = "qca,ar9330-eth" }, - { .compatible = "qca,ar9340-eth" }, - { .compatible = "qca,qca9530-eth" }, - { .compatible = "qca,qca9550-eth" }, - { .compatible = "qca,qca9560-eth" }, - {} -}; - -static struct platform_driver ag71xx_driver = { - .probe = ag71xx_probe, - .remove = ag71xx_remove, - .driver = { - .name = AG71XX_DRV_NAME, - .of_match_table = ag71xx_match, - } -}; - -static int __init ag71xx_module_init(void) -{ - int ret; - - ret = ag71xx_debugfs_root_init(); - if (ret) - goto err_out; - - ret = platform_driver_register(&ag71xx_driver); - if (ret) - goto err_debugfs_exit; - - return 0; - -err_debugfs_exit: - ag71xx_debugfs_root_exit(); -err_out: - return ret; -} - -static void __exit ag71xx_module_exit(void) -{ - platform_driver_unregister(&ag71xx_driver); - ag71xx_debugfs_root_exit(); -} - -module_init(ag71xx_module_init); -module_exit(ag71xx_module_exit); - -MODULE_AUTHOR("Gabor Juhos "); -MODULE_AUTHOR("Imre Kaloz "); -MODULE_AUTHOR("Felix Fietkau "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" AG71XX_DRV_NAME); diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c deleted file mode 100644 index a58ee3346b..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Atheros AR71xx built-in ethernet mac driver - * - * Copyright (C) 2008-2010 Gabor Juhos - * Copyright (C) 2008 Imre Kaloz - * - * Based on Atheros' AG7100 driver - * - * 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 "ag71xx.h" - -#define AG71XX_MDIO_RETRY 1000 -#define AG71XX_MDIO_DELAY 5 - -static int bus_count; - -static int ag71xx_mdio_wait_busy(struct ag71xx_mdio *am) -{ - int i; - - for (i = 0; i < AG71XX_MDIO_RETRY; i++) { - u32 busy; - - udelay(AG71XX_MDIO_DELAY); - - regmap_read(am->mii_regmap, AG71XX_REG_MII_IND, &busy); - if (!busy) - return 0; - - udelay(AG71XX_MDIO_DELAY); - } - - pr_err("%s: MDIO operation timed out\n", am->mii_bus->name); - - return -ETIMEDOUT; -} - -static int ag71xx_mdio_mii_read(struct mii_bus *bus, int addr, int reg) -{ - struct ag71xx_mdio *am = bus->priv; - int err; - int ret; - - err = ag71xx_mdio_wait_busy(am); - if (err) - return 0xffff; - - regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE); - regmap_write(am->mii_regmap, AG71XX_REG_MII_ADDR, - ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); - regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_READ); - - err = ag71xx_mdio_wait_busy(am); - if (err) - return 0xffff; - - regmap_read(am->mii_regmap, AG71XX_REG_MII_STATUS, &ret); - ret &= 0xffff; - regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE); - - DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret); - - return ret; -} - -static int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val) -{ - struct ag71xx_mdio *am = bus->priv; - - DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val); - - regmap_write(am->mii_regmap, AG71XX_REG_MII_ADDR, - ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); - regmap_write(am->mii_regmap, AG71XX_REG_MII_CTRL, val); - - ag71xx_mdio_wait_busy(am); - - return 0; -} - -static const u32 ar71xx_mdio_div_table[] = { - 4, 4, 6, 8, 10, 14, 20, 28, -}; - -static const u32 ar7240_mdio_div_table[] = { - 2, 2, 4, 6, 8, 12, 18, 26, 32, 40, 48, 56, 62, 70, 78, 96, -}; - -static const u32 ar933x_mdio_div_table[] = { - 4, 4, 6, 8, 10, 14, 20, 28, 34, 42, 50, 58, 66, 74, 82, 98, -}; - -static int ag71xx_mdio_get_divider(struct device_node *np, u32 *div) -{ - struct clk *ref_clk = of_clk_get(np, 0); - unsigned long ref_clock; - u32 mdio_clock; - const u32 *table; - int ndivs, i; - - if (IS_ERR(ref_clk)) - return -EINVAL; - - ref_clock = clk_get_rate(ref_clk); - clk_put(ref_clk); - - if(of_property_read_u32(np, "qca,mdio-max-frequency", &mdio_clock)) { - if (of_property_read_bool(np, "builtin-switch")) - mdio_clock = 5000000; - else - mdio_clock = 2000000; - } - - if (of_device_is_compatible(np, "qca,ar9330-mdio") || - of_device_is_compatible(np, "qca,ar9340-mdio")) { - table = ar933x_mdio_div_table; - ndivs = ARRAY_SIZE(ar933x_mdio_div_table); - } else if (of_device_is_compatible(np, "qca,ar7240-mdio")) { - table = ar7240_mdio_div_table; - ndivs = ARRAY_SIZE(ar7240_mdio_div_table); - } else { - table = ar71xx_mdio_div_table; - ndivs = ARRAY_SIZE(ar71xx_mdio_div_table); - } - - for (i = 0; i < ndivs; i++) { - unsigned long t; - - t = ref_clock / table[i]; - if (t <= mdio_clock) { - *div = i; - return 0; - } - } - - return -ENOENT; -} - -static int ag71xx_mdio_reset(struct mii_bus *bus) -{ - struct device_node *np = bus->dev.of_node; - struct ag71xx_mdio *am = bus->priv; - bool builtin_switch; - u32 t; - - builtin_switch = of_property_read_bool(np, "builtin-switch"); - - if (ag71xx_mdio_get_divider(np, &t)) { - if (of_device_is_compatible(np, "qca,ar9340-mdio")) - t = MII_CFG_CLK_DIV_58; - else if (builtin_switch) - t = MII_CFG_CLK_DIV_10; - else - t = MII_CFG_CLK_DIV_28; - } - - regmap_write(am->mii_regmap, AG71XX_REG_MII_CFG, t | MII_CFG_RESET); - udelay(100); - - regmap_write(am->mii_regmap, AG71XX_REG_MII_CFG, t); - udelay(100); - - return 0; -} - -static int ag71xx_mdio_probe(struct platform_device *pdev) -{ - struct device *amdev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; - struct ag71xx_mdio *am; - struct mii_bus *mii_bus; - bool builtin_switch; - int i, err; - - am = devm_kzalloc(amdev, sizeof(*am), GFP_KERNEL); - if (!am) - return -ENOMEM; - - am->mii_regmap = syscon_regmap_lookup_by_phandle(np, "regmap"); - if (IS_ERR(am->mii_regmap)) - return PTR_ERR(am->mii_regmap); - - mii_bus = devm_mdiobus_alloc(amdev); - if (!mii_bus) - return -ENOMEM; - - am->mdio_reset = devm_reset_control_get_exclusive(amdev, "mdio"); - builtin_switch = of_property_read_bool(np, "builtin-switch"); - - mii_bus->name = "ag71xx_mdio"; - mii_bus->read = ag71xx_mdio_mii_read; - mii_bus->write = ag71xx_mdio_mii_write; - mii_bus->reset = ag71xx_mdio_reset; - mii_bus->priv = am; - mii_bus->parent = amdev; - snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s.%d", np->name, bus_count++); - - if (!builtin_switch && - of_property_read_u32(np, "phy-mask", &mii_bus->phy_mask)) - mii_bus->phy_mask = 0; - - for (i = 0; i < PHY_MAX_ADDR; i++) - mii_bus->irq[i] = PHY_POLL; - - if (!IS_ERR(am->mdio_reset)) { - reset_control_assert(am->mdio_reset); - msleep(100); - reset_control_deassert(am->mdio_reset); - msleep(200); - } - - err = of_mdiobus_register(mii_bus, np); - if (err) - return err; - - am->mii_bus = mii_bus; - platform_set_drvdata(pdev, am); - - return 0; -} - -static int ag71xx_mdio_remove(struct platform_device *pdev) -{ - struct ag71xx_mdio *am = platform_get_drvdata(pdev); - - mdiobus_unregister(am->mii_bus); - return 0; -} - -static const struct of_device_id ag71xx_mdio_match[] = { - { .compatible = "qca,ar7240-mdio" }, - { .compatible = "qca,ar9330-mdio" }, - { .compatible = "qca,ar9340-mdio" }, - { .compatible = "qca,ath79-mdio" }, - {} -}; - -static struct platform_driver ag71xx_mdio_driver = { - .probe = ag71xx_mdio_probe, - .remove = ag71xx_mdio_remove, - .driver = { - .name = "ag71xx-mdio", - .of_match_table = ag71xx_mdio_match, - } -}; - -module_platform_driver(ag71xx_mdio_driver); -MODULE_LICENSE("GPL"); diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c deleted file mode 100644 index ac1af26860..0000000000 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Atheros AR71xx built-in ethernet mac driver - * - * Copyright (C) 2008-2010 Gabor Juhos - * Copyright (C) 2008 Imre Kaloz - * - * Based on Atheros' AG7100 driver - * - * 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 "ag71xx.h" - -static void ag71xx_phy_link_adjust(struct net_device *dev) -{ - struct ag71xx *ag = netdev_priv(dev); - struct phy_device *phydev = ag->phy_dev; - unsigned long flags; - int status_change = 0; - - spin_lock_irqsave(&ag->lock, flags); - - if (phydev->link) { - if (ag->duplex != phydev->duplex - || ag->speed != phydev->speed) { - status_change = 1; - } - } - - if (phydev->link != ag->link) - status_change = 1; - - ag->link = phydev->link; - ag->duplex = phydev->duplex; - ag->speed = phydev->speed; - - if (status_change) - ag71xx_link_adjust(ag); - - spin_unlock_irqrestore(&ag->lock, flags); -} - -int ag71xx_phy_connect(struct ag71xx *ag) -{ - struct device_node *np = ag->pdev->dev.of_node; - struct device_node *phy_node; - int ret; - - if (of_phy_is_fixed_link(np)) { - ret = of_phy_register_fixed_link(np); - if (ret < 0) { - dev_err(&ag->pdev->dev, - "Failed to register fixed PHY link: %d\n", ret); - return ret; - } - - phy_node = of_node_get(np); - } else { - phy_node = of_parse_phandle(np, "phy-handle", 0); - } - - if (!phy_node) { - dev_err(&ag->pdev->dev, - "Could not find valid phy node\n"); - return -ENODEV; - } - - ag->phy_dev = of_phy_connect(ag->dev, phy_node, ag71xx_phy_link_adjust, - 0, ag->phy_if_mode); - - of_node_put(phy_node); - - if (!ag->phy_dev) { - dev_err(&ag->pdev->dev, - "Could not connect to PHY device. Deferring probe.\n"); - return -EPROBE_DEFER; - } - - dev_info(&ag->pdev->dev, "connected to PHY at %s [uid=%08x, driver=%s]\n", - phydev_name(ag->phy_dev), - ag->phy_dev->phy_id, ag->phy_dev->drv->name); - - return 0; -} - -void ag71xx_phy_disconnect(struct ag71xx *ag) -{ - phy_disconnect(ag->phy_dev); -} diff --git a/target/linux/ath79/generic/config-default b/target/linux/ath79/generic/config-default index 6cd1518e3a..a15a1d8972 100644 --- a/target/linux/ath79/generic/config-default +++ b/target/linux/ath79/generic/config-default @@ -1,4 +1,5 @@ CONFIG_AT803X_PHY=y +CONFIG_BCM_NET_PHYLIB=y CONFIG_BROADCOM_PHY=y CONFIG_GPIO_WATCHDOG=y CONFIG_GPIO_WATCHDOG_ARCH_INITCALL=y @@ -7,8 +8,8 @@ CONFIG_IP17XX_PHY=y CONFIG_LEDS_RESET=y CONFIG_MARVELL_PHY=y CONFIG_MICREL_PHY=y -CONFIG_MTD_REDBOOT_PARTS=y CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-3 +CONFIG_MTD_REDBOOT_PARTS=y CONFIG_MTD_SPLIT_EVA_FW=y CONFIG_MTD_SPLIT_MINOR_FW=y CONFIG_PHY_AR7100_USB=y diff --git a/target/linux/ath79/nand/config-default b/target/linux/ath79/nand/config-default index b03f977a07..50bc550794 100644 --- a/target/linux/ath79/nand/config-default +++ b/target/linux/ath79/nand/config-default @@ -1,19 +1,26 @@ -CONFIG_MTD_NAND=y -CONFIG_MTD_NAND_AR934X=y +CONFIG_CRC16=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LZO=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y CONFIG_MTD_NAND_CORE=y -CONFIG_MTD_NAND_ECC=y CONFIG_MTD_SPI_NAND=y CONFIG_MTD_SPLIT_MINOR_FW=y CONFIG_MTD_UBI=y -CONFIG_MTD_UBI_BLOCK=y -CONFIG_MTD_UBI_WL_THRESHOLD=4096 CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y # CONFIG_MTD_UBI_FASTMAP is not set # CONFIG_MTD_UBI_GLUEBI is not set +CONFIG_MTD_UBI_WL_THRESHOLD=4096 # CONFIG_PCI_AR71XX is not set -# CONFIG_PHY_AR7100_USB is not set CONFIG_PHY_AR7200_USB=y +CONFIG_SGL_ALLOC=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y CONFIG_UBIFS_FS_LZO=y CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_ZSTD is not set +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/ath79/patches-5.4/0002-watchdog-ath79-fix-maximum-timeout.patch b/target/linux/ath79/patches-5.4/0002-watchdog-ath79-fix-maximum-timeout.patch new file mode 100644 index 0000000000..495721f609 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0002-watchdog-ath79-fix-maximum-timeout.patch @@ -0,0 +1,32 @@ +From 5f5c9858af167f842ee8df053920b98387a71af1 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 5 Mar 2018 11:41:25 +0100 +Subject: [PATCH 02/27] watchdog: ath79: fix maximum timeout + +If the userland tries to set a timeout higher than the max_timeout, +then we should fallback to max_timeout. + +Signed-off-by: John Crispin +--- + drivers/watchdog/ath79_wdt.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/watchdog/ath79_wdt.c ++++ b/drivers/watchdog/ath79_wdt.c +@@ -111,10 +111,14 @@ static inline void ath79_wdt_disable(voi + + static int ath79_wdt_set_timeout(int val) + { +- if (val < 1 || val > max_timeout) ++ if (val < 1) + return -EINVAL; + +- timeout = val; ++ if (val > max_timeout) ++ timeout = max_timeout; ++ else ++ timeout = val; ++ + ath79_wdt_keepalive(); + + return 0; diff --git a/target/linux/ath79/patches-5.4/0003-leds-add-reset-controller-based-driver.patch b/target/linux/ath79/patches-5.4/0003-leds-add-reset-controller-based-driver.patch new file mode 100644 index 0000000000..3249e1e82b --- /dev/null +++ b/target/linux/ath79/patches-5.4/0003-leds-add-reset-controller-based-driver.patch @@ -0,0 +1,186 @@ +From ecbd9c87f073f097d9fe56390353e64e963e866a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 6 Mar 2018 10:03:03 +0100 +Subject: [PATCH 03/27] leds: add reset-controller based driver + +Signed-off-by: John Crispin +--- + drivers/leds/Kconfig | 11 ++++ + drivers/leds/Makefile | 1 + + drivers/leds/leds-reset.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 149 insertions(+) + create mode 100644 drivers/leds/leds-reset.c + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -823,6 +823,17 @@ config LEDS_LM36274 + Say Y to enable the LM36274 LED driver for TI LMU devices. + This supports the LED device LM36274. + ++config LEDS_RESET ++ tristate "LED support for reset-controller API" ++ depends on LEDS_CLASS ++ depends on RESET_CONTROLLER ++ help ++ This option enables support for LEDs connected to pins driven by reset ++ controllers. Yes, DNI actual built HW like that. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called leds-reset. ++ + comment "LED Triggers" + source "drivers/leds/trigger/Kconfig" + +--- /dev/null ++++ b/drivers/leds/leds-reset.c +@@ -0,0 +1,140 @@ ++/* ++ * Copyright (C) 2018 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 ++#include ++#include ++#include ++ ++struct reset_led_data { ++ struct led_classdev cdev; ++ struct reset_control *rst; ++}; ++ ++static inline struct reset_led_data * ++ cdev_to_reset_led_data(struct led_classdev *led_cdev) ++{ ++ return container_of(led_cdev, struct reset_led_data, cdev); ++} ++ ++static void reset_led_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ struct reset_led_data *led_dat = cdev_to_reset_led_data(led_cdev); ++ ++ if (value == LED_OFF) ++ reset_control_assert(led_dat->rst); ++ else ++ reset_control_deassert(led_dat->rst); ++} ++ ++struct reset_leds_priv { ++ int num_leds; ++ struct reset_led_data leds[]; ++}; ++ ++static inline int sizeof_reset_leds_priv(int num_leds) ++{ ++ return sizeof(struct reset_leds_priv) + ++ (sizeof(struct reset_led_data) * num_leds); ++} ++ ++static struct reset_leds_priv *reset_leds_create(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct fwnode_handle *child; ++ struct reset_leds_priv *priv; ++ int count, ret; ++ ++ count = device_get_child_node_count(dev); ++ if (!count) ++ return ERR_PTR(-ENODEV); ++ ++ priv = devm_kzalloc(dev, sizeof_reset_leds_priv(count), GFP_KERNEL); ++ if (!priv) ++ return ERR_PTR(-ENOMEM); ++ ++ device_for_each_child_node(dev, child) { ++ struct reset_led_data *led = &priv->leds[priv->num_leds]; ++ struct device_node *np = to_of_node(child); ++ ++ ret = fwnode_property_read_string(child, "label", &led->cdev.name); ++ if (!led->cdev.name) { ++ fwnode_handle_put(child); ++ return ERR_PTR(-EINVAL); ++ } ++ led->rst = __of_reset_control_get(np, NULL, 0, 0, 0, true); ++ if (IS_ERR(led->rst)) ++ return ERR_PTR(-EINVAL); ++ ++ fwnode_property_read_string(child, "linux,default-trigger", ++ &led->cdev.default_trigger); ++ ++ led->cdev.brightness_set = reset_led_set; ++ ret = devm_led_classdev_register(&pdev->dev, &led->cdev); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ led->cdev.dev->of_node = np; ++ priv->num_leds++; ++ } ++ ++ return priv; ++} ++ ++static const struct of_device_id of_reset_leds_match[] = { ++ { .compatible = "reset-leds", }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_reset_leds_match); ++ ++static int reset_led_probe(struct platform_device *pdev) ++{ ++ struct reset_leds_priv *priv; ++ ++ priv = reset_leds_create(pdev); ++ if (IS_ERR(priv)) ++ return PTR_ERR(priv); ++ ++ platform_set_drvdata(pdev, priv); ++ ++ return 0; ++} ++ ++static void reset_led_shutdown(struct platform_device *pdev) ++{ ++ struct reset_leds_priv *priv = platform_get_drvdata(pdev); ++ int i; ++ ++ for (i = 0; i < priv->num_leds; i++) { ++ struct reset_led_data *led = &priv->leds[i]; ++ ++ if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN)) ++ reset_led_set(&led->cdev, LED_OFF); ++ } ++} ++ ++static struct platform_driver reset_led_driver = { ++ .probe = reset_led_probe, ++ .shutdown = reset_led_shutdown, ++ .driver = { ++ .name = "leds-reset", ++ .of_match_table = of_reset_leds_match, ++ }, ++}; ++ ++module_platform_driver(reset_led_driver); ++ ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("reset controller LED driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:leds-reset"); +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -85,6 +85,7 @@ obj-$(CONFIG_LEDS_LM3601X) += leds-lm36 + obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o + obj-$(CONFIG_LEDS_LM3697) += leds-lm3697.o + obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o ++obj-$(CONFIG_LEDS_RESET) += leds-reset.o + + # LED SPI Drivers + obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o diff --git a/target/linux/ath79/patches-5.4/0004-phy-add-ath79-usb-phys.patch b/target/linux/ath79/patches-5.4/0004-phy-add-ath79-usb-phys.patch new file mode 100644 index 0000000000..7956edb937 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0004-phy-add-ath79-usb-phys.patch @@ -0,0 +1,332 @@ +From 08c9d6ceef01893678a5d2e8a15517c745417f21 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 6 Mar 2018 10:04:05 +0100 +Subject: [PATCH 04/27] phy: add ath79 usb phys + +Signed-off-by: John Crispin +--- + drivers/phy/Kconfig | 16 ++++++ + drivers/phy/Makefile | 2 + + drivers/phy/phy-ar7100-usb.c | 124 +++++++++++++++++++++++++++++++++++++++++++ + drivers/phy/phy-ar7200-usb.c | 108 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 250 insertions(+) + create mode 100644 drivers/phy/phy-ar7100-usb.c + create mode 100644 drivers/phy/phy-ar7200-usb.c + +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -24,6 +24,22 @@ config GENERIC_PHY_MIPI_DPHY + Provides a number of helpers a core functions for MIPI D-PHY + drivers to us. + ++config PHY_AR7100_USB ++ tristate "Atheros AR7100 USB PHY driver" ++ depends on ATH79 || COMPILE_TEST ++ default y if USB_EHCI_HCD_PLATFORM ++ select PHY_SIMPLE ++ help ++ Enable this to support the USB PHY on Atheros AR7100 SoCs. ++ ++config PHY_AR7200_USB ++ tristate "Atheros AR7200 USB PHY driver" ++ depends on ATH79 || COMPILE_TEST ++ default y if USB_EHCI_HCD_PLATFORM ++ select PHY_SIMPLE ++ help ++ Enable this to support the USB PHY on Atheros AR7200 SoCs. ++ + config PHY_LPC18XX_USB_OTG + tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -4,6 +4,8 @@ + # + + obj-$(CONFIG_GENERIC_PHY) += phy-core.o ++obj-$(CONFIG_PHY_AR7100_USB) += phy-ar7100-usb.o ++obj-$(CONFIG_PHY_AR7200_USB) += phy-ar7200-usb.o + obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o + obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o + obj-$(CONFIG_PHY_XGENE) += phy-xgene.o +--- /dev/null ++++ b/drivers/phy/phy-ar7100-usb.c +@@ -0,0 +1,140 @@ ++/* ++ * Copyright (C) 2018 John Crispin ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++struct ar7100_usb_phy { ++ struct reset_control *rst_phy; ++ struct reset_control *rst_host; ++ struct reset_control *rst_ohci_dll; ++ void __iomem *io_base; ++ struct phy *phy; ++ int gpio; ++}; ++ ++static int ar7100_usb_phy_power_off(struct phy *phy) ++{ ++ struct ar7100_usb_phy *priv = phy_get_drvdata(phy); ++ int err = 0; ++ ++ err |= reset_control_assert(priv->rst_host); ++ err |= reset_control_assert(priv->rst_phy); ++ err |= reset_control_assert(priv->rst_ohci_dll); ++ ++ return err; ++} ++ ++static int ar7100_usb_phy_power_on(struct phy *phy) ++{ ++ struct ar7100_usb_phy *priv = phy_get_drvdata(phy); ++ int err = 0; ++ ++ err |= ar7100_usb_phy_power_off(phy); ++ mdelay(100); ++ err |= reset_control_deassert(priv->rst_ohci_dll); ++ err |= reset_control_deassert(priv->rst_phy); ++ err |= reset_control_deassert(priv->rst_host); ++ mdelay(500); ++ iowrite32(0xf0000, priv->io_base + AR71XX_USB_CTRL_REG_CONFIG); ++ iowrite32(0x20c00, priv->io_base + AR71XX_USB_CTRL_REG_FLADJ); ++ ++ return err; ++} ++ ++static const struct phy_ops ar7100_usb_phy_ops = { ++ .power_on = ar7100_usb_phy_power_on, ++ .power_off = ar7100_usb_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int ar7100_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ struct ar7100_usb_phy *priv; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->io_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->io_base)) ++ return PTR_ERR(priv->io_base); ++ ++ priv->rst_phy = devm_reset_control_get(&pdev->dev, "usb-phy"); ++ if (IS_ERR(priv->rst_phy)) { ++ dev_err(&pdev->dev, "phy reset is missing\n"); ++ return PTR_ERR(priv->rst_phy); ++ } ++ ++ priv->rst_host = devm_reset_control_get(&pdev->dev, "usb-host"); ++ if (IS_ERR(priv->rst_host)) { ++ dev_err(&pdev->dev, "host reset is missing\n"); ++ return PTR_ERR(priv->rst_host); ++ } ++ ++ priv->rst_ohci_dll = devm_reset_control_get(&pdev->dev, "usb-ohci-dll"); ++ if (IS_ERR(priv->rst_ohci_dll)) { ++ dev_err(&pdev->dev, "ohci-dll reset is missing\n"); ++ return PTR_ERR(priv->rst_host); ++ } ++ ++ priv->phy = devm_phy_create(&pdev->dev, NULL, &ar7100_usb_phy_ops); ++ if (IS_ERR(priv->phy)) { ++ dev_err(&pdev->dev, "failed to create PHY\n"); ++ return PTR_ERR(priv->phy); ++ } ++ ++ priv->gpio = of_get_gpio(pdev->dev.of_node, 0); ++ if (priv->gpio >= 0) { ++ int ret = devm_gpio_request(&pdev->dev, priv->gpio, dev_name(&pdev->dev)); ++ ++ if (ret) { ++ dev_err(&pdev->dev, "failed to request gpio\n"); ++ return ret; ++ } ++ gpio_export_with_name(priv->gpio, 0, dev_name(&pdev->dev)); ++ gpio_set_value(priv->gpio, 1); ++ } ++ ++ phy_set_drvdata(priv->phy, priv); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); ++ ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id ar7100_usb_phy_of_match[] = { ++ { .compatible = "qca,ar7100-usb-phy" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, ar7100_usb_phy_of_match); ++ ++static struct platform_driver ar7100_usb_phy_driver = { ++ .probe = ar7100_usb_phy_probe, ++ .driver = { ++ .of_match_table = ar7100_usb_phy_of_match, ++ .name = "ar7100-usb-phy", ++ } ++}; ++module_platform_driver(ar7100_usb_phy_driver); ++ ++MODULE_DESCRIPTION("ATH79 USB PHY driver"); ++MODULE_AUTHOR("Alban Bedel "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/phy/phy-ar7200-usb.c +@@ -0,0 +1,135 @@ ++/* ++ * Copyright (C) 2015 Alban Bedel ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct ar7200_usb_phy { ++ struct reset_control *rst_phy; ++ struct reset_control *rst_phy_analog; ++ struct reset_control *suspend_override; ++ struct phy *phy; ++ int gpio; ++}; ++ ++static int ar7200_usb_phy_power_on(struct phy *phy) ++{ ++ struct ar7200_usb_phy *priv = phy_get_drvdata(phy); ++ int err = 0; ++ ++ if (priv->suspend_override) ++ err = reset_control_assert(priv->suspend_override); ++ if (priv->rst_phy) ++ err |= reset_control_deassert(priv->rst_phy); ++ if (priv->rst_phy_analog) ++ err |= reset_control_deassert(priv->rst_phy_analog); ++ ++ return err; ++} ++ ++static int ar7200_usb_phy_power_off(struct phy *phy) ++{ ++ struct ar7200_usb_phy *priv = phy_get_drvdata(phy); ++ int err = 0; ++ ++ if (priv->suspend_override) ++ err = reset_control_deassert(priv->suspend_override); ++ if (priv->rst_phy) ++ err |= reset_control_assert(priv->rst_phy); ++ if (priv->rst_phy_analog) ++ err |= reset_control_assert(priv->rst_phy_analog); ++ ++ return err; ++} ++ ++static const struct phy_ops ar7200_usb_phy_ops = { ++ .power_on = ar7200_usb_phy_power_on, ++ .power_off = ar7200_usb_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int ar7200_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct ar7200_usb_phy *priv; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->rst_phy = devm_reset_control_get(&pdev->dev, "usb-phy"); ++ if (IS_ERR(priv->rst_phy)) { ++ dev_err(&pdev->dev, "phy reset is missing\n"); ++ return PTR_ERR(priv->rst_phy); ++ } ++ ++ priv->rst_phy_analog = devm_reset_control_get_optional( ++ &pdev->dev, "usb-phy-analog"); ++ if (IS_ERR(priv->rst_phy_analog)) { ++ if (PTR_ERR(priv->rst_phy_analog) == -ENOENT) ++ priv->rst_phy_analog = NULL; ++ else ++ return PTR_ERR(priv->rst_phy_analog); ++ } ++ ++ priv->suspend_override = devm_reset_control_get_optional( ++ &pdev->dev, "usb-suspend-override"); ++ if (IS_ERR(priv->suspend_override)) { ++ if (PTR_ERR(priv->suspend_override) == -ENOENT) ++ priv->suspend_override = NULL; ++ else ++ return PTR_ERR(priv->suspend_override); ++ } ++ ++ priv->phy = devm_phy_create(&pdev->dev, NULL, &ar7200_usb_phy_ops); ++ if (IS_ERR(priv->phy)) { ++ dev_err(&pdev->dev, "failed to create PHY\n"); ++ return PTR_ERR(priv->phy); ++ } ++ ++ priv->gpio = of_get_gpio(pdev->dev.of_node, 0); ++ if (priv->gpio >= 0) { ++ int ret = devm_gpio_request(&pdev->dev, priv->gpio, dev_name(&pdev->dev)); ++ ++ if (ret) { ++ dev_err(&pdev->dev, "failed to request gpio\n"); ++ return ret; ++ } ++ gpio_export_with_name(priv->gpio, 0, dev_name(&pdev->dev)); ++ gpio_set_value(priv->gpio, 1); ++ } ++ ++ phy_set_drvdata(priv->phy, priv); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id ar7200_usb_phy_of_match[] = { ++ { .compatible = "qca,ar7200-usb-phy" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, ar7200_usb_phy_of_match); ++ ++static struct platform_driver ar7200_usb_phy_driver = { ++ .probe = ar7200_usb_phy_probe, ++ .driver = { ++ .of_match_table = ar7200_usb_phy_of_match, ++ .name = "ar7200-usb-phy", ++ } ++}; ++module_platform_driver(ar7200_usb_phy_driver); ++ ++MODULE_DESCRIPTION("ATH79 USB PHY driver"); ++MODULE_AUTHOR("Alban Bedel "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/ath79/patches-5.4/0005-usb-add-more-OF-quirk-properties.patch b/target/linux/ath79/patches-5.4/0005-usb-add-more-OF-quirk-properties.patch new file mode 100644 index 0000000000..83033a117b --- /dev/null +++ b/target/linux/ath79/patches-5.4/0005-usb-add-more-OF-quirk-properties.patch @@ -0,0 +1,24 @@ +From 2201818e5bd33f389beceb3943fdfcf5a698fc5b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 6 Mar 2018 10:01:43 +0100 +Subject: [PATCH 05/27] usb: add more OF/quirk properties + +Signed-off-by: John Crispin +--- + drivers/usb/host/ehci-platform.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -159,6 +159,11 @@ static int ehci_platform_probe(struct pl + ehci = hcd_to_ehci(hcd); + + if (pdata == &ehci_platform_defaults && dev->dev.of_node) { ++ of_property_read_u32(dev->dev.of_node, "caps-offset", &pdata->caps_offset); ++ ++ if (of_property_read_bool(dev->dev.of_node, "has-synopsys-hc-bug")) ++ pdata->has_synopsys_hc_bug = 1; ++ + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) + ehci->big_endian_mmio = 1; + diff --git a/target/linux/ath79/patches-5.4/0007-irqchip-irq-ath79-intc-add-irq-cascade-driver-for-QC.patch b/target/linux/ath79/patches-5.4/0007-irqchip-irq-ath79-intc-add-irq-cascade-driver-for-QC.patch new file mode 100644 index 0000000000..ceda511c21 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0007-irqchip-irq-ath79-intc-add-irq-cascade-driver-for-QC.patch @@ -0,0 +1,168 @@ +From f3eacff2310a60348a755c50a8da6fc251fc8587 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 6 Mar 2018 09:55:13 +0100 +Subject: [PATCH 07/33] irqchip/irq-ath79-intc: add irq cascade driver for + QCA9556 SoCs + +Signed-off-by: John Crispin +--- + drivers/irqchip/Makefile | 1 + + drivers/irqchip/irq-ath79-intc.c | 142 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 143 insertions(+) + create mode 100644 drivers/irqchip/irq-ath79-intc.c + +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -4,6 +4,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o + obj-$(CONFIG_AL_FIC) += irq-al-fic.o + obj-$(CONFIG_ALPINE_MSI) += irq-alpine-msi.o + obj-$(CONFIG_ATH79) += irq-ath79-cpu.o ++obj-$(CONFIG_ATH79) += irq-ath79-intc.o + obj-$(CONFIG_ATH79) += irq-ath79-misc.o + obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o + obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o +--- /dev/null ++++ b/drivers/irqchip/irq-ath79-intc.c +@@ -0,0 +1,142 @@ ++/* ++ * Atheros AR71xx/AR724x/AR913x specific interrupt handling ++ * ++ * Copyright (C) 2018 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 ++ ++#include ++#include ++#include ++ ++#define ATH79_MAX_INTC_CASCADE 3 ++ ++struct ath79_intc { ++ struct irq_chip chip; ++ u32 irq; ++ u32 pending_mask; ++ u32 int_status; ++ u32 irq_mask[ATH79_MAX_INTC_CASCADE]; ++ u32 irq_wb_chan[ATH79_MAX_INTC_CASCADE]; ++}; ++ ++static void ath79_intc_irq_handler(struct irq_desc *desc) ++{ ++ struct irq_domain *domain = irq_desc_get_handler_data(desc); ++ struct ath79_intc *intc = domain->host_data; ++ u32 pending; ++ ++ pending = ath79_reset_rr(intc->int_status); ++ pending &= intc->pending_mask; ++ ++ if (pending) { ++ int i; ++ ++ for (i = 0; i < domain->hwirq_max; i++) ++ if (pending & intc->irq_mask[i]) { ++ if (intc->irq_wb_chan[i] != 0xffffffff) ++ ath79_ddr_wb_flush(intc->irq_wb_chan[i]); ++ generic_handle_irq(irq_find_mapping(domain, i)); ++ } ++ } else { ++ spurious_interrupt(); ++ } ++} ++ ++static void ath79_intc_irq_enable(struct irq_data *d) ++{ ++ struct ath79_intc *intc = d->domain->host_data; ++ enable_irq(intc->irq); ++} ++ ++static void ath79_intc_irq_disable(struct irq_data *d) ++{ ++ struct ath79_intc *intc = d->domain->host_data; ++ disable_irq(intc->irq); ++} ++ ++static int ath79_intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) ++{ ++ struct ath79_intc *intc = d->host_data; ++ ++ irq_set_chip_and_handler(irq, &intc->chip, handle_level_irq); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops ath79_irq_domain_ops = { ++ .xlate = irq_domain_xlate_onecell, ++ .map = ath79_intc_map, ++}; ++ ++static int __init ath79_intc_of_init( ++ struct device_node *node, struct device_node *parent) ++{ ++ struct irq_domain *domain; ++ struct ath79_intc *intc; ++ int cnt, cntwb, i, err; ++ ++ cnt = of_property_count_u32_elems(node, "qca,pending-bits"); ++ if (cnt > ATH79_MAX_INTC_CASCADE) ++ panic("Too many INTC pending bits\n"); ++ ++ intc = kzalloc(sizeof(*intc), GFP_KERNEL); ++ if (!intc) ++ panic("Failed to allocate INTC memory\n"); ++ intc->chip = dummy_irq_chip; ++ intc->chip.name = "INTC"; ++ intc->chip.irq_disable = ath79_intc_irq_disable; ++ intc->chip.irq_enable = ath79_intc_irq_enable; ++ ++ if (of_property_read_u32(node, "qca,int-status-addr", &intc->int_status) < 0) { ++ panic("Missing address of interrupt status register\n"); ++ } ++ ++ of_property_read_u32_array(node, "qca,pending-bits", intc->irq_mask, cnt); ++ for (i = 0; i < cnt; i++) { ++ intc->pending_mask |= intc->irq_mask[i]; ++ intc->irq_wb_chan[i] = 0xffffffff; ++ } ++ ++ cntwb = of_count_phandle_with_args( ++ node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells"); ++ ++ for (i = 0; i < cntwb; i++) { ++ struct of_phandle_args args; ++ u32 irq = i; ++ ++ of_property_read_u32_index( ++ node, "qca,ddr-wb-channel-interrupts", i, &irq); ++ if (irq >= ATH79_MAX_INTC_CASCADE) ++ continue; ++ ++ err = of_parse_phandle_with_args( ++ node, "qca,ddr-wb-channels", ++ "#qca,ddr-wb-channel-cells", ++ i, &args); ++ if (err) ++ return err; ++ ++ intc->irq_wb_chan[irq] = args.args[0]; ++ } ++ ++ intc->irq = irq_of_parse_and_map(node, 0); ++ if (!intc->irq) ++ panic("Failed to get INTC IRQ"); ++ ++ domain = irq_domain_add_linear(node, cnt, &ath79_irq_domain_ops, intc); ++ irq_set_chained_handler_and_data(intc->irq, ath79_intc_irq_handler, domain); ++ ++ return 0; ++} ++IRQCHIP_DECLARE(ath79_intc, "qca,ar9340-intc", ++ ath79_intc_of_init); diff --git a/target/linux/ath79/patches-5.4/0008-irqchip-irq-ath79-cpu-drop-OF-init-helper.patch b/target/linux/ath79/patches-5.4/0008-irqchip-irq-ath79-cpu-drop-OF-init-helper.patch new file mode 100644 index 0000000000..13117d9a8e --- /dev/null +++ b/target/linux/ath79/patches-5.4/0008-irqchip-irq-ath79-cpu-drop-OF-init-helper.patch @@ -0,0 +1,23 @@ +From e029f998594f151008ecbfa024e2957edd2a5189 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 6 Mar 2018 09:58:19 +0100 +Subject: [PATCH 08/33] irqchip/irq-ath79-cpu: drop !OF init helper + +Signed-off-by: John Crispin +--- + drivers/irqchip/irq-ath79-cpu.c | 7 ------- + 1 file changed, 7 deletions(-) + +--- a/drivers/irqchip/irq-ath79-cpu.c ++++ b/drivers/irqchip/irq-ath79-cpu.c +@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( + } + IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); +- +-void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +-{ +- irq_wb_chan[2] = irq_wb_chan2; +- irq_wb_chan[3] = irq_wb_chan3; +- mips_cpu_irq_init(); +-} diff --git a/target/linux/ath79/patches-5.4/0011-MIPS-ath79-select-the-PINCTRL-subsystem.patch b/target/linux/ath79/patches-5.4/0011-MIPS-ath79-select-the-PINCTRL-subsystem.patch new file mode 100644 index 0000000000..4ac58d10c4 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0011-MIPS-ath79-select-the-PINCTRL-subsystem.patch @@ -0,0 +1,24 @@ +From 0c8856211d26f84277f7fcb0b9595e5c646bc464 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 6 Mar 2018 10:00:55 +0100 +Subject: [PATCH 11/33] MIPS: ath79: select the PINCTRL subsystem + +The pinmux on QCA SoCs is controlled by a single register. The +"pinctrl-single" driver can be used but requires the target +to select PINCTRL. + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -295,6 +295,7 @@ config BCM63XX + select SYS_HAS_EARLY_PRINTK + select SWAP_IO_SPACE + select GPIOLIB ++ select PINCTRL + select HAVE_CLK + select MIPS_L1_CACHE_SHIFT_4 + select CLKDEV_LOOKUP diff --git a/target/linux/ath79/patches-5.4/0017-dt-bindings-PCI-qcom-ar7100-adds-binding-doc.patch b/target/linux/ath79/patches-5.4/0017-dt-bindings-PCI-qcom-ar7100-adds-binding-doc.patch new file mode 100644 index 0000000000..bf7eb691a5 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0017-dt-bindings-PCI-qcom-ar7100-adds-binding-doc.patch @@ -0,0 +1,57 @@ +From 4a4f869ec58ed8910b9b2e68d0eee50957e9bb20 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 25 Jun 2018 15:52:10 +0200 +Subject: [PATCH 17/33] dt-bindings: PCI: qcom,ar7100: adds binding doc + +With the driver being converted from platform_data to pure OF, we need to +also add some docs. + +Cc: Rob Herring +Cc: devicetree@vger.kernel.org +Signed-off-by: John Crispin +--- + .../devicetree/bindings/pci/qcom,ar7100-pci.txt | 38 ++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pci/qcom,ar7100-pci.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/qcom,ar7100-pci.txt +@@ -0,0 +1,38 @@ ++* Qualcomm Atheros AR7100 PCI express root complex ++ ++Required properties: ++- compatible: should contain "qcom,ar7100-pci" to identify the core. ++- reg: Should contain the register ranges as listed in the reg-names property. ++- reg-names: Definition: Must include the following entries ++ - "cfg_base" IO Memory ++- #address-cells: set to <3> ++- #size-cells: set to <2> ++- ranges: ranges for the PCI memory and I/O regions ++- interrupt-map-mask and interrupt-map: standard PCI ++ properties to define the mapping of the PCIe interface to interrupt ++ numbers. ++- #interrupt-cells: set to <1> ++- interrupt-controller: define to enable the builtin IRQ cascade. ++ ++Optional properties: ++- interrupt-parent: phandle to the MIPS IRQ controller ++ ++* Example for ar7100 ++ pcie-controller@180c0000 { ++ compatible = "qca,ar7100-pci"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ bus-range = <0x0 0x0>; ++ reg = <0x17010000 0x100>; ++ reg-names = "cfg_base"; ++ ranges = <0x2000000 0 0x10000000 0x10000000 0 0x07000000 ++ 0x1000000 0 0x00000000 0x00000000 0 0x00000001>; ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-map-mask = <0 0 0 1>; ++ interrupt-map = <0 0 0 0 &pcie0 0>; ++ }; diff --git a/target/linux/ath79/patches-5.4/0018-MIPS-pci-ar71xx-convert-to-OF.patch b/target/linux/ath79/patches-5.4/0018-MIPS-pci-ar71xx-convert-to-OF.patch new file mode 100644 index 0000000000..e600a4f0d9 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0018-MIPS-pci-ar71xx-convert-to-OF.patch @@ -0,0 +1,202 @@ +From 1855ab6b1d27f5b38a648baf57ff6a534afec26d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 23 Jun 2018 15:07:23 +0200 +Subject: [PATCH 18/33] MIPS: pci-ar71xx: convert to OF + +With the ath79 target getting converted to pure OF, we can drop all the +platform data code and add the missing OF bits to the driver. We also add +a irq domain for the PCI/e controllers cascade, thus making it usable from +dts files. + +Signed-off-by: John Crispin +--- + arch/mips/pci/pci-ar71xx.c | 82 +++++++++++++++++++++++----------------------- + 1 file changed, 41 insertions(+), 41 deletions(-) + +--- a/arch/mips/pci/pci-ar71xx.c ++++ b/arch/mips/pci/pci-ar71xx.c +@@ -15,8 +15,11 @@ + #include + #include + #include ++#include + #include + #include ++#include ++#include + + #include + #include +@@ -46,12 +49,13 @@ + #define AR71XX_PCI_IRQ_COUNT 5 + + struct ar71xx_pci_controller { ++ struct device_node *np; + void __iomem *cfg_base; + int irq; +- int irq_base; + struct pci_controller pci_ctrl; + struct resource io_res; + struct resource mem_res; ++ struct irq_domain *domain; + }; + + /* Byte lane enable bits */ +@@ -225,29 +229,30 @@ static struct pci_ops ar71xx_pci_ops = { + + static void ar71xx_pci_irq_handler(struct irq_desc *desc) + { +- struct ar71xx_pci_controller *apc; + void __iomem *base = ath79_reset_base; ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct ar71xx_pci_controller *apc = irq_desc_get_handler_data(desc); + u32 pending; + +- apc = irq_desc_get_handler_data(desc); +- ++ chained_irq_enter(chip, desc); + pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) & + __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); + + if (pending & AR71XX_PCI_INT_DEV0) +- generic_handle_irq(apc->irq_base + 0); ++ generic_handle_irq(irq_linear_revmap(apc->domain, 1)); + + else if (pending & AR71XX_PCI_INT_DEV1) +- generic_handle_irq(apc->irq_base + 1); ++ generic_handle_irq(irq_linear_revmap(apc->domain, 2)); + + else if (pending & AR71XX_PCI_INT_DEV2) +- generic_handle_irq(apc->irq_base + 2); ++ generic_handle_irq(irq_linear_revmap(apc->domain, 3)); + + else if (pending & AR71XX_PCI_INT_CORE) +- generic_handle_irq(apc->irq_base + 4); ++ generic_handle_irq(irq_linear_revmap(apc->domain, 4)); + + else + spurious_interrupt(); ++ chained_irq_exit(chip, desc); + } + + static void ar71xx_pci_irq_unmask(struct irq_data *d) +@@ -258,7 +263,7 @@ static void ar71xx_pci_irq_unmask(struct + u32 t; + + apc = irq_data_get_irq_chip_data(d); +- irq = d->irq - apc->irq_base; ++ irq = irq_linear_revmap(apc->domain, d->irq); + + t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); + __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); +@@ -275,7 +280,7 @@ static void ar71xx_pci_irq_mask(struct i + u32 t; + + apc = irq_data_get_irq_chip_data(d); +- irq = d->irq - apc->irq_base; ++ irq = irq_linear_revmap(apc->domain, d->irq); + + t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); + __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); +@@ -291,24 +296,31 @@ static struct irq_chip ar71xx_pci_irq_ch + .irq_mask_ack = ar71xx_pci_irq_mask, + }; + ++static int ar71xx_pci_irq_map(struct irq_domain *d, ++ unsigned int irq, irq_hw_number_t hw) ++{ ++ struct ar71xx_pci_controller *apc = d->host_data; ++ ++ irq_set_chip_and_handler(irq, &ar71xx_pci_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, apc); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops ar71xx_pci_domain_ops = { ++ .xlate = irq_domain_xlate_onecell, ++ .map = ar71xx_pci_irq_map, ++}; ++ + static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc) + { + void __iomem *base = ath79_reset_base; +- int i; + + __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE); + __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS); + +- BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR71XX_PCI_IRQ_COUNT); +- +- apc->irq_base = ATH79_PCI_IRQ_BASE; +- for (i = apc->irq_base; +- i < apc->irq_base + AR71XX_PCI_IRQ_COUNT; i++) { +- irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip, +- handle_level_irq); +- irq_set_chip_data(i, apc); +- } +- ++ apc->domain = irq_domain_add_linear(apc->np, AR71XX_PCI_IRQ_COUNT, ++ &ar71xx_pci_domain_ops, apc); + irq_set_chained_handler_and_data(apc->irq, ar71xx_pci_irq_handler, + apc); + } +@@ -325,6 +337,11 @@ static void ar71xx_pci_reset(void) + mdelay(100); + } + ++static const struct of_device_id ar71xx_pci_ids[] = { ++ { .compatible = "qca,ar7100-pci" }, ++ {}, ++}; ++ + static int ar71xx_pci_probe(struct platform_device *pdev) + { + struct ar71xx_pci_controller *apc; +@@ -345,26 +362,6 @@ static int ar71xx_pci_probe(struct platf + if (apc->irq < 0) + return -EINVAL; + +- res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base"); +- if (!res) +- return -EINVAL; +- +- apc->io_res.parent = res; +- apc->io_res.name = "PCI IO space"; +- apc->io_res.start = res->start; +- apc->io_res.end = res->end; +- apc->io_res.flags = IORESOURCE_IO; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base"); +- if (!res) +- return -EINVAL; +- +- apc->mem_res.parent = res; +- apc->mem_res.name = "PCI memory space"; +- apc->mem_res.start = res->start; +- apc->mem_res.end = res->end; +- apc->mem_res.flags = IORESOURCE_MEM; +- + ar71xx_pci_reset(); + + /* setup COMMAND register */ +@@ -377,9 +374,11 @@ static int ar71xx_pci_probe(struct platf + + ar71xx_pci_irq_init(apc); + ++ apc->np = pdev->dev.of_node; + apc->pci_ctrl.pci_ops = &ar71xx_pci_ops; + apc->pci_ctrl.mem_resource = &apc->mem_res; + apc->pci_ctrl.io_resource = &apc->io_res; ++ pci_load_of_ranges(&apc->pci_ctrl, pdev->dev.of_node); + + register_pci_controller(&apc->pci_ctrl); + +@@ -390,6 +389,7 @@ static struct platform_driver ar71xx_pci + .probe = ar71xx_pci_probe, + .driver = { + .name = "ar71xx-pci", ++ .of_match_table = of_match_ptr(ar71xx_pci_ids), + }, + }; + diff --git a/target/linux/ath79/patches-5.4/0019-dt-bindings-PCI-qcom-ar7240-adds-binding-doc.patch b/target/linux/ath79/patches-5.4/0019-dt-bindings-PCI-qcom-ar7240-adds-binding-doc.patch new file mode 100644 index 0000000000..a0af79cb4d --- /dev/null +++ b/target/linux/ath79/patches-5.4/0019-dt-bindings-PCI-qcom-ar7240-adds-binding-doc.patch @@ -0,0 +1,61 @@ +From ea27764bc3ef2a05decf3ae05edffc289cd0d93c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 25 Jun 2018 15:52:02 +0200 +Subject: [PATCH 19/33] dt-bindings: PCI: qcom,ar7240: adds binding doc + +With the driver being converted from platform_data to pure OF, we need to +also add some docs. + +Cc: Rob Herring +Cc: devicetree@vger.kernel.org +Signed-off-by: John Crispin +--- + .../devicetree/bindings/pci/qcom,ar7240-pci.txt | 42 ++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pci/qcom,ar7240-pci.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/qcom,ar7240-pci.txt +@@ -0,0 +1,42 @@ ++* Qualcomm Atheros AR724X PCI express root complex ++ ++Required properties: ++- compatible: should contain "qcom,ar7240-pci" to identify the core. ++- reg: Should contain the register ranges as listed in the reg-names property. ++- reg-names: Definition: Must include the following entries ++ - "crp_base" Configuration registers ++ - "ctrl_base" Control registers ++ - "cfg_base" IO Memory ++- #address-cells: set to <3> ++- #size-cells: set to <2> ++- ranges: ranges for the PCI memory and I/O regions ++- interrupt-map-mask and interrupt-map: standard PCI ++ properties to define the mapping of the PCIe interface to interrupt ++ numbers. ++- #interrupt-cells: set to <1> ++- interrupt-parent: phandle to the MIPS IRQ controller ++ ++Optional properties: ++- interrupt-controller: define to enable the builtin IRQ cascade. ++ ++* Example for qca9557 ++ pcie-controller@180c0000 { ++ compatible = "qcom,ar7240-pci"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ bus-range = <0x0 0x0>; ++ reg = <0x180c0000 0x1000>, ++ <0x180f0000 0x100>, ++ <0x14000000 0x1000>; ++ reg-names = "crp_base", "ctrl_base", "cfg_base"; ++ ranges = <0x2000000 0 0x10000000 0x10000000 0 0x04000000 ++ 0x1000000 0 0x00000000 0x00000000 0 0x00000001>; ++ interrupt-parent = <&intc2>; ++ interrupts = <1>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-map-mask = <0 0 0 1>; ++ interrupt-map = <0 0 0 0 &pcie0 0>; ++ }; diff --git a/target/linux/ath79/patches-5.4/0020-MIPS-pci-ar724x-convert-to-OF.patch b/target/linux/ath79/patches-5.4/0020-MIPS-pci-ar724x-convert-to-OF.patch new file mode 100644 index 0000000000..b8a877c63d --- /dev/null +++ b/target/linux/ath79/patches-5.4/0020-MIPS-pci-ar724x-convert-to-OF.patch @@ -0,0 +1,205 @@ +From a522ee0199d5d3ea114ca2e211f6ac398d3e8e0b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 23 Jun 2018 15:07:37 +0200 +Subject: [PATCH 20/33] MIPS: pci-ar724x: convert to OF + +With the ath79 target getting converted to pure OF, we can drop all the +platform data code and add the missing OF bits to the driver. We also add +a irq domain for the PCI/e controllers cascade, thus making it usable from +dts files. + +Signed-off-by: John Crispin +--- + arch/mips/pci/pci-ar724x.c | 88 ++++++++++++++++++++++------------------------ + 1 file changed, 42 insertions(+), 46 deletions(-) + +--- a/arch/mips/pci/pci-ar724x.c ++++ b/arch/mips/pci/pci-ar724x.c +@@ -11,8 +11,11 @@ + #include + #include + #include ++#include + #include + #include ++#include ++#include + + #define AR724X_PCI_REG_APP 0x00 + #define AR724X_PCI_REG_RESET 0x18 +@@ -42,17 +45,20 @@ struct ar724x_pci_controller { + void __iomem *crp_base; + + int irq; +- int irq_base; + + bool link_up; + bool bar0_is_cached; + u32 bar0_value; + ++ struct device_node *np; + struct pci_controller pci_controller; ++ struct irq_domain *domain; + struct resource io_res; + struct resource mem_res; + }; + ++static struct irq_chip ar724x_pci_irq_chip; ++ + static inline bool ar724x_pci_check_link(struct ar724x_pci_controller *apc) + { + u32 reset; +@@ -228,35 +234,31 @@ static struct pci_ops ar724x_pci_ops = { + + static void ar724x_pci_irq_handler(struct irq_desc *desc) + { +- struct ar724x_pci_controller *apc; +- void __iomem *base; ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct ar724x_pci_controller *apc = irq_desc_get_handler_data(desc); + u32 pending; + +- apc = irq_desc_get_handler_data(desc); +- base = apc->ctrl_base; +- +- pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) & +- __raw_readl(base + AR724X_PCI_REG_INT_MASK); ++ chained_irq_enter(chip, desc); ++ pending = __raw_readl(apc->ctrl_base + AR724X_PCI_REG_INT_STATUS) & ++ __raw_readl(apc->ctrl_base + AR724X_PCI_REG_INT_MASK); + + if (pending & AR724X_PCI_INT_DEV0) +- generic_handle_irq(apc->irq_base + 0); +- ++ generic_handle_irq(irq_linear_revmap(apc->domain, 1)); + else + spurious_interrupt(); ++ chained_irq_exit(chip, desc); + } + + static void ar724x_pci_irq_unmask(struct irq_data *d) + { + struct ar724x_pci_controller *apc; + void __iomem *base; +- int offset; + u32 t; + + apc = irq_data_get_irq_chip_data(d); + base = apc->ctrl_base; +- offset = apc->irq_base - d->irq; + +- switch (offset) { ++ switch (irq_linear_revmap(apc->domain, d->irq)) { + case 0: + t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); + __raw_writel(t | AR724X_PCI_INT_DEV0, +@@ -270,14 +272,12 @@ static void ar724x_pci_irq_mask(struct i + { + struct ar724x_pci_controller *apc; + void __iomem *base; +- int offset; + u32 t; + + apc = irq_data_get_irq_chip_data(d); + base = apc->ctrl_base; +- offset = apc->irq_base - d->irq; + +- switch (offset) { ++ switch (irq_linear_revmap(apc->domain, d->irq)) { + case 0: + t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); + __raw_writel(t & ~AR724X_PCI_INT_DEV0, +@@ -302,26 +302,34 @@ static struct irq_chip ar724x_pci_irq_ch + .irq_mask_ack = ar724x_pci_irq_mask, + }; + ++static int ar724x_pci_irq_map(struct irq_domain *d, ++ unsigned int irq, irq_hw_number_t hw) ++{ ++ struct ar724x_pci_controller *apc = d->host_data; ++ ++ irq_set_chip_and_handler(irq, &ar724x_pci_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, apc); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops ar724x_pci_domain_ops = { ++ .xlate = irq_domain_xlate_onecell, ++ .map = ar724x_pci_irq_map, ++}; ++ + static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc, + int id) + { + void __iomem *base; +- int i; + + base = apc->ctrl_base; + + __raw_writel(0, base + AR724X_PCI_REG_INT_MASK); + __raw_writel(0, base + AR724X_PCI_REG_INT_STATUS); + +- apc->irq_base = ATH79_PCI_IRQ_BASE + (id * AR724X_PCI_IRQ_COUNT); +- +- for (i = apc->irq_base; +- i < apc->irq_base + AR724X_PCI_IRQ_COUNT; i++) { +- irq_set_chip_and_handler(i, &ar724x_pci_irq_chip, +- handle_level_irq); +- irq_set_chip_data(i, apc); +- } +- ++ apc->domain = irq_domain_add_linear(apc->np, 2, ++ &ar724x_pci_domain_ops, apc); + irq_set_chained_handler_and_data(apc->irq, ar724x_pci_irq_handler, + apc); + } +@@ -391,29 +399,11 @@ static int ar724x_pci_probe(struct platf + if (apc->irq < 0) + return -EINVAL; + +- res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base"); +- if (!res) +- return -EINVAL; +- +- apc->io_res.parent = res; +- apc->io_res.name = "PCI IO space"; +- apc->io_res.start = res->start; +- apc->io_res.end = res->end; +- apc->io_res.flags = IORESOURCE_IO; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base"); +- if (!res) +- return -EINVAL; +- +- apc->mem_res.parent = res; +- apc->mem_res.name = "PCI memory space"; +- apc->mem_res.start = res->start; +- apc->mem_res.end = res->end; +- apc->mem_res.flags = IORESOURCE_MEM; +- ++ apc->np = pdev->dev.of_node; + apc->pci_controller.pci_ops = &ar724x_pci_ops; + apc->pci_controller.io_resource = &apc->io_res; + apc->pci_controller.mem_resource = &apc->mem_res; ++ pci_load_of_ranges(&apc->pci_controller, pdev->dev.of_node); + + /* + * Do the full PCIE Root Complex Initialization Sequence if the PCIe +@@ -435,10 +425,16 @@ static int ar724x_pci_probe(struct platf + return 0; + } + ++static const struct of_device_id ar724x_pci_ids[] = { ++ { .compatible = "qcom,ar7240-pci" }, ++ {}, ++}; ++ + static struct platform_driver ar724x_pci_driver = { + .probe = ar724x_pci_probe, + .driver = { + .name = "ar724x-pci", ++ .of_match_table = of_match_ptr(ar724x_pci_ids), + }, + }; + diff --git a/target/linux/ath79/patches-5.4/0032-MIPS-ath79-sanitize-symbols.patch b/target/linux/ath79/patches-5.4/0032-MIPS-ath79-sanitize-symbols.patch new file mode 100644 index 0000000000..382c5e6dfa --- /dev/null +++ b/target/linux/ath79/patches-5.4/0032-MIPS-ath79-sanitize-symbols.patch @@ -0,0 +1,93 @@ +From 3fc8585cf76022dba7496627074d42af88c30718 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 23 Jun 2018 15:16:55 +0200 +Subject: [PATCH 32/33] MIPS: ath79: sanitize symbols + +We no longer need to select which SoCs are supported as the whole arch +code is always built. So lets drop all the SoC symbols + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 2 ++ + arch/mips/ath79/Kconfig | 44 +++++--------------------------------------- + arch/mips/pci/Makefile | 2 +- + 3 files changed, 8 insertions(+), 40 deletions(-) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -216,6 +216,8 @@ config ATH79 + select SYS_SUPPORTS_BIG_ENDIAN + select SYS_SUPPORTS_MIPS16 + select SYS_SUPPORTS_ZBOOT_UART_PROM ++ select HAVE_PCI ++ select USB_ARCH_HAS_EHCI + select USE_OF + select USB_EHCI_ROOT_HUB_TT if USB_EHCI_HCD_PLATFORM + help +--- a/arch/mips/ath79/Kconfig ++++ b/arch/mips/ath79/Kconfig +@@ -1,48 +1,14 @@ + # SPDX-License-Identifier: GPL-2.0 + if ATH79 + +-config SOC_AR71XX +- select HAVE_PCI +- def_bool n +- +-config SOC_AR724X +- select HAVE_PCI +- select PCI_AR724X if PCI +- def_bool n +- +-config SOC_AR913X +- def_bool n +- +-config SOC_AR933X +- def_bool n +- +-config SOC_AR934X +- select HAVE_PCI +- select PCI_AR724X if PCI +- def_bool n +- +-config SOC_QCA955X +- select HAVE_PCI +- select PCI_AR724X if PCI ++config PCI_AR71XX ++ bool "PCI support for AR7100 type SoCs" ++ depends on PCI + def_bool n + + config PCI_AR724X +- def_bool n +- +-config ATH79_DEV_GPIO_BUTTONS +- def_bool n +- +-config ATH79_DEV_LEDS_GPIO +- def_bool n +- +-config ATH79_DEV_SPI +- def_bool n +- +-config ATH79_DEV_USB +- def_bool n +- +-config ATH79_DEV_WMAC +- depends on (SOC_AR913X || SOC_AR933X || SOC_AR934X || SOC_QCA955X) ++ bool "PCI support for AR724x type SoCs" ++ depends on PCI + def_bool n + + endif +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -23,7 +23,7 @@ obj-$(CONFIG_BCM63XX) += pci-bcm63xx.o + ops-bcm63xx.o + obj-$(CONFIG_MIPS_ALCHEMY) += pci-alchemy.o + obj-$(CONFIG_PCI_AR2315) += pci-ar2315.o +-obj-$(CONFIG_SOC_AR71XX) += pci-ar71xx.o ++obj-$(CONFIG_PCI_AR71XX) += pci-ar71xx.o + obj-$(CONFIG_PCI_AR724X) += pci-ar724x.o + obj-$(CONFIG_MIPS_PCI_VIRTIO) += pci-virtio-guest.o + obj-$(CONFIG_PCI_XTALK_BRIDGE) += pci-xtalk-bridge.o diff --git a/target/linux/ath79/patches-5.4/0033-spi-ath79-drop-pdata-support.patch b/target/linux/ath79/patches-5.4/0033-spi-ath79-drop-pdata-support.patch new file mode 100644 index 0000000000..8fd99bd333 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0033-spi-ath79-drop-pdata-support.patch @@ -0,0 +1,71 @@ +From c4e197bbcecc7233aa9e553e7047fa50e4e1fe77 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 25 Jun 2018 15:52:34 +0200 +Subject: [PATCH 33/33] spi: ath79: drop pdata support + +The target is being converted to pure OF. We can therefore drop all of the +platform data code from the driver. + +Cc: linux-spi@vger.kernel.org +Acked-by: Mark Brown +Signed-off-by: John Crispin +--- + include/linux/platform_data/spi-ath79.h | 16 ------------------- + drivers/spi/spi-ath79.c | 8 -------- + 2 files changed, 27 deletions(-) + delete mode 100644 arch/mips/include/asm/mach-ath79/ath79_spi_platform.h + +--- a/include/linux/platform_data/spi-ath79.h ++++ /dev/null +@@ -1,16 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * Platform data definition for Atheros AR71XX/AR724X/AR913X SPI controller +- * +- * Copyright (C) 2008-2010 Gabor Juhos +- */ +- +-#ifndef _ATH79_SPI_PLATFORM_H +-#define _ATH79_SPI_PLATFORM_H +- +-struct ath79_spi_platform_data { +- unsigned bus_num; +- unsigned num_chipselect; +-}; +- +-#endif /* _ATH79_SPI_PLATFORM_H */ +--- a/drivers/spi/spi-ath79.c ++++ b/drivers/spi/spi-ath79.c +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include + + #define DRV_NAME "ath79-spi" + +@@ -138,7 +137,6 @@ static int ath79_spi_probe(struct platfo + { + struct spi_master *master; + struct ath79_spi *sp; +- struct ath79_spi_platform_data *pdata; + unsigned long rate; + int ret; + +@@ -152,16 +150,10 @@ static int ath79_spi_probe(struct platfo + master->dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, sp); + +- pdata = dev_get_platdata(&pdev->dev); +- + master->use_gpio_descriptors = true; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); + master->setup = spi_bitbang_setup; + master->cleanup = spi_bitbang_cleanup; +- if (pdata) { +- master->bus_num = pdata->bus_num; +- master->num_chipselect = pdata->num_chipselect; +- } + + sp->bitbang.master = master; + sp->bitbang.chipselect = ath79_spi_chipselect; diff --git a/target/linux/ath79/patches-5.4/0034-MIPS-ath79-ath9k-exports.patch b/target/linux/ath79/patches-5.4/0034-MIPS-ath79-ath9k-exports.patch new file mode 100644 index 0000000000..71acc22210 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0034-MIPS-ath79-ath9k-exports.patch @@ -0,0 +1,27 @@ +--- a/arch/mips/ath79/common.c ++++ b/arch/mips/ath79/common.c +@@ -31,11 +31,13 @@ EXPORT_SYMBOL_GPL(ath79_ddr_freq); + + enum ath79_soc_type ath79_soc; + unsigned int ath79_soc_rev; ++EXPORT_SYMBOL_GPL(ath79_soc_rev); + + void __iomem *ath79_pll_base; + void __iomem *ath79_reset_base; + EXPORT_SYMBOL_GPL(ath79_reset_base); +-static void __iomem *ath79_ddr_base; ++void __iomem *ath79_ddr_base; ++EXPORT_SYMBOL_GPL(ath79_ddr_base); + static void __iomem *ath79_ddr_wb_flush_base; + static void __iomem *ath79_ddr_pci_win_base; + +--- a/arch/mips/include/asm/mach-ath79/ath79.h ++++ b/arch/mips/include/asm/mach-ath79/ath79.h +@@ -149,6 +149,7 @@ void ath79_ddr_wb_flush(unsigned int reg + void ath79_ddr_set_pci_windows(void); + + extern void __iomem *ath79_pll_base; ++extern void __iomem *ath79_ddr_base; + extern void __iomem *ath79_reset_base; + + static inline void ath79_pll_wr(unsigned reg, u32 val) diff --git a/target/linux/ath79/patches-5.4/0036-GPIO-add-named-gpio-exports.patch b/target/linux/ath79/patches-5.4/0036-GPIO-add-named-gpio-exports.patch new file mode 100644 index 0000000000..c07d3d3fea --- /dev/null +++ b/target/linux/ath79/patches-5.4/0036-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,165 @@ +From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 24/53] GPIO: add named gpio exports + +Signed-off-by: John Crispin +--- + drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++ + drivers/gpio/gpiolib-sysfs.c | 10 +++++- + include/asm-generic/gpio.h | 6 ++++ + include/linux/gpio/consumer.h | 8 +++++ + 4 files changed, 91 insertions(+), 1 deletion(-) + +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -19,6 +19,8 @@ + #include + #include + #include ++#include ++#include + + #include "gpiolib.h" + #include "gpiolib-of.h" +@@ -895,3 +897,68 @@ void of_gpiochip_remove(struct gpio_chip + gpiochip_remove_pin_ranges(chip); + of_node_put(chip->of_node); + } ++ ++static struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_count(cnp); ++ ++ for (i = 0; i < max_gpio; i++) { ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ gpio = of_get_gpio_flags(cnp, i, &of_flags); ++ if (!gpio_is_valid(gpio)) ++ return gpio; ++ ++ if (of_flags == OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ else ++ flags |= GPIOF_IN; ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio, dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++ .probe = of_gpio_export_probe, ++}; ++ ++module_platform_driver(gpio_export_driver); +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -563,7 +563,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + struct gpio_chip *chip; + struct gpio_device *gdev; +@@ -625,6 +625,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -646,6 +648,12 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + + static int match_export(struct device *dev, const void *desc) +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g + return gpiod_export(gpio_to_desc(gpio), direction_may_change); + } + ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); ++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) ++{ ++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); ++} ++ + static inline int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) + { +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -661,6 +661,7 @@ static inline void devm_acpi_dev_remove_ + + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + ++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + int gpiod_export(struct gpio_desc *desc, bool direction_may_change); + int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +@@ -668,6 +669,13 @@ void gpiod_unexport(struct gpio_desc *de + + #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + ++static inline int _gpiod_export(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} ++ + static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) + { diff --git a/target/linux/ath79/patches-5.4/0036-MIPS-ath79-remove-irq-code-from-pci.patch b/target/linux/ath79/patches-5.4/0036-MIPS-ath79-remove-irq-code-from-pci.patch new file mode 100644 index 0000000000..80fcd0a7f5 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0036-MIPS-ath79-remove-irq-code-from-pci.patch @@ -0,0 +1,139 @@ +--- a/arch/mips/pci/pci-ar71xx.c ++++ b/arch/mips/pci/pci-ar71xx.c +@@ -51,11 +51,9 @@ + struct ar71xx_pci_controller { + struct device_node *np; + void __iomem *cfg_base; +- int irq; + struct pci_controller pci_ctrl; + struct resource io_res; + struct resource mem_res; +- struct irq_domain *domain; + }; + + /* Byte lane enable bits */ +@@ -227,104 +225,6 @@ static struct pci_ops ar71xx_pci_ops = { + .write = ar71xx_pci_write_config, + }; + +-static void ar71xx_pci_irq_handler(struct irq_desc *desc) +-{ +- void __iomem *base = ath79_reset_base; +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct ar71xx_pci_controller *apc = irq_desc_get_handler_data(desc); +- u32 pending; +- +- chained_irq_enter(chip, desc); +- pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) & +- __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); +- +- if (pending & AR71XX_PCI_INT_DEV0) +- generic_handle_irq(irq_linear_revmap(apc->domain, 1)); +- +- else if (pending & AR71XX_PCI_INT_DEV1) +- generic_handle_irq(irq_linear_revmap(apc->domain, 2)); +- +- else if (pending & AR71XX_PCI_INT_DEV2) +- generic_handle_irq(irq_linear_revmap(apc->domain, 3)); +- +- else if (pending & AR71XX_PCI_INT_CORE) +- generic_handle_irq(irq_linear_revmap(apc->domain, 4)); +- +- else +- spurious_interrupt(); +- chained_irq_exit(chip, desc); +-} +- +-static void ar71xx_pci_irq_unmask(struct irq_data *d) +-{ +- struct ar71xx_pci_controller *apc; +- unsigned int irq; +- void __iomem *base = ath79_reset_base; +- u32 t; +- +- apc = irq_data_get_irq_chip_data(d); +- irq = irq_linear_revmap(apc->domain, d->irq); +- +- t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); +- __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); +- +- /* flush write */ +- __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); +-} +- +-static void ar71xx_pci_irq_mask(struct irq_data *d) +-{ +- struct ar71xx_pci_controller *apc; +- unsigned int irq; +- void __iomem *base = ath79_reset_base; +- u32 t; +- +- apc = irq_data_get_irq_chip_data(d); +- irq = irq_linear_revmap(apc->domain, d->irq); +- +- t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); +- __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); +- +- /* flush write */ +- __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); +-} +- +-static struct irq_chip ar71xx_pci_irq_chip = { +- .name = "AR71XX PCI", +- .irq_mask = ar71xx_pci_irq_mask, +- .irq_unmask = ar71xx_pci_irq_unmask, +- .irq_mask_ack = ar71xx_pci_irq_mask, +-}; +- +-static int ar71xx_pci_irq_map(struct irq_domain *d, +- unsigned int irq, irq_hw_number_t hw) +-{ +- struct ar71xx_pci_controller *apc = d->host_data; +- +- irq_set_chip_and_handler(irq, &ar71xx_pci_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, apc); +- +- return 0; +-} +- +-static const struct irq_domain_ops ar71xx_pci_domain_ops = { +- .xlate = irq_domain_xlate_onecell, +- .map = ar71xx_pci_irq_map, +-}; +- +-static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc) +-{ +- void __iomem *base = ath79_reset_base; +- +- __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE); +- __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS); +- +- apc->domain = irq_domain_add_linear(apc->np, AR71XX_PCI_IRQ_COUNT, +- &ar71xx_pci_domain_ops, apc); +- irq_set_chained_handler_and_data(apc->irq, ar71xx_pci_irq_handler, +- apc); +-} +- + static void ar71xx_pci_reset(void) + { + ath79_device_reset_set(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE); +@@ -358,10 +258,6 @@ static int ar71xx_pci_probe(struct platf + if (IS_ERR(apc->cfg_base)) + return PTR_ERR(apc->cfg_base); + +- apc->irq = platform_get_irq(pdev, 0); +- if (apc->irq < 0) +- return -EINVAL; +- + ar71xx_pci_reset(); + + /* setup COMMAND register */ +@@ -372,8 +268,6 @@ static int ar71xx_pci_probe(struct platf + /* clear bus errors */ + ar71xx_pci_check_error(apc, 1); + +- ar71xx_pci_irq_init(apc); +- + apc->np = pdev->dev.of_node; + apc->pci_ctrl.pci_ops = &ar71xx_pci_ops; + apc->pci_ctrl.mem_resource = &apc->mem_res; diff --git a/target/linux/ath79/patches-5.4/0037-missing-registers.patch b/target/linux/ath79/patches-5.4/0037-missing-registers.patch new file mode 100644 index 0000000000..9067e4ca7b --- /dev/null +++ b/target/linux/ath79/patches-5.4/0037-missing-registers.patch @@ -0,0 +1,21 @@ +commit f3ffac90bc7266b7d917616f3233f58e8c08a196 +Author: Christian Lamparter +Date: Fri Aug 10 23:24:47 2018 +0200 + + ath79: gmac: add parsers for rxd(v)- and tx(d|en)-delay for AR9344 + + Signed-off-by: Christian Lamparter + +--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h ++++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +@@ -1226,6 +1226,10 @@ + #define AR934X_ETH_CFG_RDV_DELAY BIT(16) + #define AR934X_ETH_CFG_RDV_DELAY_MASK 0x3 + #define AR934X_ETH_CFG_RDV_DELAY_SHIFT 16 ++#define AR934X_ETH_CFG_TXD_DELAY_MASK 0x3 ++#define AR934X_ETH_CFG_TXD_DELAY_SHIFT 18 ++#define AR934X_ETH_CFG_TXE_DELAY_MASK 0x3 ++#define AR934X_ETH_CFG_TXE_DELAY_SHIFT 20 + + /* + * QCA953X GMAC Interface diff --git a/target/linux/ath79/patches-5.4/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch b/target/linux/ath79/patches-5.4/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch new file mode 100644 index 0000000000..bc09062dc5 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch @@ -0,0 +1,90 @@ +From 60efe35257b063ce584968f9f80b437030ce6ba6 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Mon, 18 Mar 2019 00:54:06 +0100 +Subject: [PATCH] MIPS: ath79: add missing QCA955x GMAC registers + +This adds missing GMAC register definitions for the Qualcomm Atheros +QCA955X series MIPS SoCs. + +They originate from the platforms U-Boot code and the AVM FRITZ!WLAN +Repeater 450E's GPL tarball. + +Signed-off-by: David Bauer +--- + .../mips/include/asm/mach-ath79/ar71xx_regs.h | 54 +++++++++++++++++++ + 1 file changed, 54 insertions(+) + +--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h ++++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +@@ -1246,7 +1246,12 @@ + */ + + #define QCA955X_GMAC_REG_ETH_CFG 0x00 ++#define QCA955X_GMAC_REG_SGMII_RESET 0x14 + #define QCA955X_GMAC_REG_SGMII_SERDES 0x18 ++#define QCA955X_GMAC_REG_MR_AN_CONTROL 0x1c ++#define QCA955X_GMAC_REG_MR_AN_STATUS 0x20 ++#define QCA955X_GMAC_REG_SGMII_CONFIG 0x34 ++#define QCA955X_GMAC_REG_SGMII_DEBUG 0x58 + + #define QCA955X_ETH_CFG_RGMII_EN BIT(0) + #define QCA955X_ETH_CFG_MII_GE0 BIT(1) +@@ -1268,9 +1273,58 @@ + #define QCA955X_ETH_CFG_TXE_DELAY_MASK 0x3 + #define QCA955X_ETH_CFG_TXE_DELAY_SHIFT 20 + ++#define QCA955X_SGMII_RESET_RX_CLK_N_RESET 0 ++#define QCA955X_SGMII_RESET_RX_CLK_N BIT(0) ++#define QCA955X_SGMII_RESET_TX_CLK_N BIT(1) ++#define QCA955X_SGMII_RESET_RX_125M_N BIT(2) ++#define QCA955X_SGMII_RESET_TX_125M_N BIT(3) ++#define QCA955X_SGMII_RESET_HW_RX_125M_N BIT(4) ++ + #define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS BIT(15) + #define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT 23 + #define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK 0xf ++ ++#define QCA955X_MR_AN_CONTROL_SPEED_SEL1 BIT(6) ++#define QCA955X_MR_AN_CONTROL_DUPLEX_MODE BIT(8) ++#define QCA955X_MR_AN_CONTROL_RESTART_AN BIT(9) ++#define QCA955X_MR_AN_CONTROL_POWER_DOWN BIT(11) ++#define QCA955X_MR_AN_CONTROL_AN_ENABLE BIT(12) ++#define QCA955X_MR_AN_CONTROL_SPEED_SEL0 BIT(13) ++#define QCA955X_MR_AN_CONTROL_LOOPBACK BIT(14) ++#define QCA955X_MR_AN_CONTROL_PHY_RESET BIT(15) ++ ++#define QCA955X_MR_AN_STATUS_EXT_CAP BIT(0) ++#define QCA955X_MR_AN_STATUS_LINK_UP BIT(2) ++#define QCA955X_MR_AN_STATUS_AN_ABILITY BIT(3) ++#define QCA955X_MR_AN_STATUS_REMOTE_FAULT BIT(4) ++#define QCA955X_MR_AN_STATUS_AN_COMPLETE BIT(5) ++#define QCA955X_MR_AN_STATUS_NO_PREAMBLE BIT(6) ++#define QCA955X_MR_AN_STATUS_BASE_PAGE BIT(7) ++ ++#define QCA955X_SGMII_CONFIG_MODE_CTRL_SHIFT 0 ++#define QCA955X_SGMII_CONFIG_MODE_CTRL_MASK 0x7 ++#define QCA955X_SGMII_CONFIG_ENABLE_SGMII_TX_PAUSE BIT(3) ++#define QCA955X_SGMII_CONFIG_MR_REG4_CHANGED BIT(4) ++#define QCA955X_SGMII_CONFIG_FORCE_SPEED BIT(5) ++#define QCA955X_SGMII_CONFIG_SPEED_SHIFT 6 ++#define QCA955X_SGMII_CONFIG_SPEED_MASK 0xc0 ++#define QCA955X_SGMII_CONFIG_REMOTE_PHY_LOOPBACK BIT(8) ++#define QCA955X_SGMII_CONFIG_NEXT_PAGE_LOADED BIT(9) ++#define QCA955X_SGMII_CONFIG_MDIO_ENABLE BIT(10) ++#define QCA955X_SGMII_CONFIG_MDIO_PULSE BIT(11) ++#define QCA955X_SGMII_CONFIG_MDIO_COMPLETE BIT(12) ++#define QCA955X_SGMII_CONFIG_PRBS_ENABLE BIT(13) ++#define QCA955X_SGMII_CONFIG_BERT_ENABLE BIT(14) ++ ++#define QCA955X_SGMII_DEBUG_TX_STATE_MASK 0xff ++#define QCA955X_SGMII_DEBUG_TX_STATE_SHIFT 0 ++#define QCA955X_SGMII_DEBUG_RX_STATE_MASK 0xff00 ++#define QCA955X_SGMII_DEBUG_RX_STATE_SHIFT 8 ++#define QCA955X_SGMII_DEBUG_RX_SYNC_STATE_MASK 0xff0000 ++#define QCA955X_SGMII_DEBUG_RX_SYNC_STATE_SHIFT 16 ++#define QCA955X_SGMII_DEBUG_ARB_STATE_MASK 0xf000000 ++#define QCA955X_SGMII_DEBUG_ARB_STATE_SHIFT 24 ++ + /* + * QCA956X GMAC Interface + */ diff --git a/target/linux/ath79/patches-5.4/0039-MIPS-ath79-export-UART1-reference-clock.patch b/target/linux/ath79/patches-5.4/0039-MIPS-ath79-export-UART1-reference-clock.patch new file mode 100644 index 0000000000..edf888c7e7 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0039-MIPS-ath79-export-UART1-reference-clock.patch @@ -0,0 +1,52 @@ +--- a/arch/mips/ath79/clock.c ++++ b/arch/mips/ath79/clock.c +@@ -40,6 +40,7 @@ static const char * const clk_names[ATH7 + [ATH79_CLK_AHB] = "ahb", + [ATH79_CLK_REF] = "ref", + [ATH79_CLK_MDIO] = "mdio", ++ [ATH79_CLK_UART1] = "uart1", + }; + + static const char * __init ath79_clk_name(int type) +@@ -344,6 +345,9 @@ static void __init ar934x_clocks_init(vo + if (clk_ctrl & AR934X_PLL_SWITCH_CLOCK_CONTROL_MDIO_CLK_SEL) + ath79_set_clk(ATH79_CLK_MDIO, 100 * 1000 * 1000); + ++ if (clk_ctrl & AR934X_PLL_SWITCH_CLOCK_CONTROL_UART1_CLK_SEL) ++ ath79_set_clk(ATH79_CLK_UART1, 100 * 1000 * 1000); ++ + iounmap(dpll_base); + } + +@@ -649,6 +653,9 @@ static void __init ath79_clocks_init_dt( + if (!clks[ATH79_CLK_MDIO]) + clks[ATH79_CLK_MDIO] = clks[ATH79_CLK_REF]; + ++ if (!clks[ATH79_CLK_UART1]) ++ clks[ATH79_CLK_UART1] = clks[ATH79_CLK_REF]; ++ + if (of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data)) { + pr_err("%pOF: could not register clk provider\n", np); + goto err_iounmap; +--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h ++++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +@@ -348,6 +348,7 @@ + #define AR934X_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL BIT(24) + + #define AR934X_PLL_SWITCH_CLOCK_CONTROL_MDIO_CLK_SEL BIT(6) ++#define AR934X_PLL_SWITCH_CLOCK_CONTROL_UART1_CLK_SEL BIT(7) + + #define QCA953X_PLL_CPU_CONFIG_REG 0x00 + #define QCA953X_PLL_DDR_CONFIG_REG 0x04 +--- a/include/dt-bindings/clock/ath79-clk.h ++++ b/include/dt-bindings/clock/ath79-clk.h +@@ -11,7 +11,8 @@ + #define ATH79_CLK_AHB 2 + #define ATH79_CLK_REF 3 + #define ATH79_CLK_MDIO 4 ++#define ATH79_CLK_UART1 5 + +-#define ATH79_CLK_END 5 ++#define ATH79_CLK_END 6 + + #endif /* __DT_BINDINGS_ATH79_CLK_H */ diff --git a/target/linux/ath79/patches-5.4/004-register_gpio_driver_earlier.patch b/target/linux/ath79/patches-5.4/004-register_gpio_driver_earlier.patch new file mode 100644 index 0000000000..fa4b05f724 --- /dev/null +++ b/target/linux/ath79/patches-5.4/004-register_gpio_driver_earlier.patch @@ -0,0 +1,18 @@ +HACK: register the GPIO driver earlier to ensure that gpio_request calls +from mach files succeed. + +--- a/drivers/gpio/gpio-ath79.c ++++ b/drivers/gpio/gpio-ath79.c +@@ -310,7 +310,11 @@ static struct platform_driver ath79_gpio + .probe = ath79_gpio_probe, + }; + +-module_platform_driver(ath79_gpio_driver); ++static int __init ath79_gpio_init(void) ++{ ++ return platform_driver_register(&ath79_gpio_driver); ++} ++postcore_initcall(ath79_gpio_init); + + MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X GPIO API support"); + MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ath79/patches-5.4/0051-spi-add-driver-for-ar934x-spi-controller.patch b/target/linux/ath79/patches-5.4/0051-spi-add-driver-for-ar934x-spi-controller.patch new file mode 100644 index 0000000000..e314fa2cd3 --- /dev/null +++ b/target/linux/ath79/patches-5.4/0051-spi-add-driver-for-ar934x-spi-controller.patch @@ -0,0 +1,283 @@ +From 7e161c423a232ef7ddf6c11b09ebe471dd5a23cf Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo +Date: Wed, 5 Feb 2020 18:25:37 +0800 +Subject: [PATCH v4 1/2] spi: add driver for ar934x spi controller + +This patch adds driver for SPI controller found in Qualcomm Atheros +AR934x/QCA95xx SoCs. +This controller is a superset of the already supported qca,ar7100-spi. +Besides the bit-bang mode in spi-ath79.c, this new controller added +a new "shift register" mode, allowing faster spi operations. + +Signed-off-by: Chuanhong Guo +--- + drivers/spi/Kconfig | 7 ++ + drivers/spi/Makefile | 1 + + drivers/spi/spi-ar934x.c | 235 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 243 insertions(+) + create mode 100644 drivers/spi/spi-ar934x.c + +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -62,6 +62,13 @@ config SPI_ALTERA + help + This is the driver for the Altera SPI Controller. + ++config SPI_AR934X ++ tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver" ++ depends on ATH79 || COMPILE_TEST ++ help ++ This enables support for the SPI controller present on the ++ Qualcomm Atheros AR934X/QCA95XX SoCs. ++ + config SPI_ATH79 + tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" + depends on ATH79 || COMPILE_TEST +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi- + + # SPI master controller drivers (bus) + obj-$(CONFIG_SPI_ALTERA) += spi-altera.o ++obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o + obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o + obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o + obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o +--- /dev/null ++++ b/drivers/spi/spi-ar934x.c +@@ -0,0 +1,235 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// SPI controller driver for Qualcomm Atheros AR934x/QCA95xx SoCs ++// ++// Copyright (C) 2020 Chuanhong Guo ++// ++// Based on spi-mt7621.c: ++// Copyright (C) 2011 Sergiy ++// Copyright (C) 2011-2013 Gabor Juhos ++// Copyright (C) 2014-2015 Felix Fietkau ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "spi-ar934x" ++ ++#define AR934X_SPI_REG_FS 0x00 ++#define AR934X_SPI_ENABLE BIT(0) ++ ++#define AR934X_SPI_REG_IOC 0x08 ++#define AR934X_SPI_IOC_INITVAL 0x70000 ++ ++#define AR934X_SPI_REG_CTRL 0x04 ++#define AR934X_SPI_CLK_MASK GENMASK(5, 0) ++ ++#define AR934X_SPI_DATAOUT 0x10 ++ ++#define AR934X_SPI_REG_SHIFT_CTRL 0x14 ++#define AR934X_SPI_SHIFT_EN BIT(31) ++#define AR934X_SPI_SHIFT_CS(n) BIT(28 + (n)) ++#define AR934X_SPI_SHIFT_TERM 26 ++#define AR934X_SPI_SHIFT_VAL(cs, term, count) \ ++ (AR934X_SPI_SHIFT_EN | AR934X_SPI_SHIFT_CS(cs) | \ ++ (term) << AR934X_SPI_SHIFT_TERM | (count)) ++ ++#define AR934X_SPI_DATAIN 0x18 ++ ++struct ar934x_spi { ++ struct spi_controller *ctlr; ++ void __iomem *base; ++ struct clk *clk; ++ unsigned int clk_freq; ++}; ++ ++static inline int ar934x_spi_clk_div(struct ar934x_spi *sp, unsigned int freq) ++{ ++ int div = DIV_ROUND_UP(sp->clk_freq, freq * 2) - 1; ++ ++ if (div < 0) ++ return 0; ++ else if (div > AR934X_SPI_CLK_MASK) ++ return -EINVAL; ++ else ++ return div; ++} ++ ++static int ar934x_spi_setup(struct spi_device *spi) ++{ ++ struct ar934x_spi *sp = spi_controller_get_devdata(spi->master); ++ ++ if ((spi->max_speed_hz == 0) || ++ (spi->max_speed_hz > (sp->clk_freq / 2))) { ++ spi->max_speed_hz = sp->clk_freq / 2; ++ } else if (spi->max_speed_hz < (sp->clk_freq / 128)) { ++ dev_err(&spi->dev, "spi clock is too low\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ar934x_spi_transfer_one_message(struct spi_controller *master, ++ struct spi_message *m) ++{ ++ struct ar934x_spi *sp = spi_controller_get_devdata(master); ++ struct spi_transfer *t = NULL; ++ struct spi_device *spi = m->spi; ++ unsigned long trx_done, trx_cur; ++ int stat = 0; ++ u8 term = 0; ++ int div, i; ++ u32 reg; ++ const u8 *tx_buf; ++ u8 *buf; ++ ++ m->actual_length = 0; ++ list_for_each_entry(t, &m->transfers, transfer_list) { ++ if (t->speed_hz) ++ div = ar934x_spi_clk_div(sp, t->speed_hz); ++ else ++ div = ar934x_spi_clk_div(sp, spi->max_speed_hz); ++ if (div < 0) { ++ stat = -EIO; ++ goto msg_done; ++ } ++ ++ reg = ioread32(sp->base + AR934X_SPI_REG_CTRL); ++ reg &= ~AR934X_SPI_CLK_MASK; ++ reg |= div; ++ iowrite32(reg, sp->base + AR934X_SPI_REG_CTRL); ++ iowrite32(0, sp->base + AR934X_SPI_DATAOUT); ++ ++ for (trx_done = 0; trx_done < t->len; trx_done += 4) { ++ trx_cur = t->len - trx_done; ++ if (trx_cur > 4) ++ trx_cur = 4; ++ else if (list_is_last(&t->transfer_list, &m->transfers)) ++ term = 1; ++ ++ if (t->tx_buf) { ++ tx_buf = t->tx_buf + trx_done; ++ reg = tx_buf[0]; ++ for (i = 1; i < trx_cur; i++) ++ reg = reg << 8 | tx_buf[i]; ++ iowrite32(reg, sp->base + AR934X_SPI_DATAOUT); ++ } ++ ++ reg = AR934X_SPI_SHIFT_VAL(spi->chip_select, term, ++ trx_cur * 8); ++ iowrite32(reg, sp->base + AR934X_SPI_REG_SHIFT_CTRL); ++ stat = readl_poll_timeout( ++ sp->base + AR934X_SPI_REG_SHIFT_CTRL, reg, ++ !(reg & AR934X_SPI_SHIFT_EN), 0, 5); ++ if (stat < 0) ++ goto msg_done; ++ ++ if (t->rx_buf) { ++ reg = ioread32(sp->base + AR934X_SPI_DATAIN); ++ buf = t->rx_buf + trx_done; ++ for (i = 0; i < trx_cur; i++) { ++ buf[trx_cur - i - 1] = reg & 0xff; ++ reg >>= 8; ++ } ++ } ++ } ++ m->actual_length += t->len; ++ } ++ ++msg_done: ++ m->status = stat; ++ spi_finalize_current_message(master); ++ ++ return 0; ++} ++ ++static const struct of_device_id ar934x_spi_match[] = { ++ { .compatible = "qca,ar934x-spi" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ar934x_spi_match); ++ ++static int ar934x_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_controller *ctlr; ++ struct ar934x_spi *sp; ++ void __iomem *base; ++ struct clk *clk; ++ int ret; ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "failed to get clock\n"); ++ return PTR_ERR(clk); ++ } ++ ++ ret = clk_prepare_enable(clk); ++ if (ret) ++ return ret; ++ ++ ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp)); ++ if (!ctlr) { ++ dev_info(&pdev->dev, "failed to allocate spi controller\n"); ++ return -ENOMEM; ++ } ++ ++ /* disable flash mapping and expose spi controller registers */ ++ iowrite32(AR934X_SPI_ENABLE, base + AR934X_SPI_REG_FS); ++ /* restore pins to default state: CSn=1 DO=CLK=0 */ ++ iowrite32(AR934X_SPI_IOC_INITVAL, base + AR934X_SPI_REG_IOC); ++ ++ ctlr->mode_bits = SPI_LSB_FIRST; ++ ctlr->setup = ar934x_spi_setup; ++ ctlr->transfer_one_message = ar934x_spi_transfer_one_message; ++ ctlr->bits_per_word_mask = SPI_BPW_MASK(8); ++ ctlr->dev.of_node = pdev->dev.of_node; ++ ctlr->num_chipselect = 3; ++ ++ dev_set_drvdata(&pdev->dev, ctlr); ++ ++ sp = spi_controller_get_devdata(ctlr); ++ sp->base = base; ++ sp->clk = clk; ++ sp->clk_freq = clk_get_rate(clk); ++ sp->ctlr = ctlr; ++ ++ return devm_spi_register_controller(&pdev->dev, ctlr); ++} ++ ++static int ar934x_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_controller *ctlr; ++ struct ar934x_spi *sp; ++ ++ ctlr = dev_get_drvdata(&pdev->dev); ++ sp = spi_controller_get_devdata(ctlr); ++ ++ clk_disable_unprepare(sp->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver ar934x_spi_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .of_match_table = ar934x_spi_match, ++ }, ++ .probe = ar934x_spi_probe, ++ .remove = ar934x_spi_remove, ++}; ++ ++module_platform_driver(ar934x_spi_driver); ++ ++MODULE_DESCRIPTION("SPI controller driver for Qualcomm Atheros AR934x/QCA95xx"); ++MODULE_AUTHOR("Chuanhong Guo "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/target/linux/ath79/patches-5.4/0060-serial-ar933x_uart-set-UART_CS_-RX-TX-_READY_ORIDE.patch b/target/linux/ath79/patches-5.4/0060-serial-ar933x_uart-set-UART_CS_-RX-TX-_READY_ORIDE.patch new file mode 100644 index 0000000000..485aadcbbd --- /dev/null +++ b/target/linux/ath79/patches-5.4/0060-serial-ar933x_uart-set-UART_CS_-RX-TX-_READY_ORIDE.patch @@ -0,0 +1,64 @@ +From patchwork Fri Feb 7 09:53:35 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 1190470 +Date: Fri, 7 Feb 2020 11:53:35 +0200 +From: Daniel Golle +To: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: Greg Kroah-Hartman , + Jiri Slaby , + Chuanhong Guo , + Eitan Cohen , + Ori Gofen +Subject: [PATCH] serial: ar933x_uart: set UART_CS_{RX,TX}_READY_ORIDE +Message-ID: <20200207095335.GA179836@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +Sender: linux-kernel-owner@vger.kernel.org +Precedence: bulk +List-ID: +X-Mailing-List: linux-kernel@vger.kernel.org + +On AR934x this UART is usually not initialized by the bootloader +as it is only used as a secondary serial port while the primary +UART is a newly introduced NS16550-compatible. +In order to make use of the ar933x-uart on AR934x without RTS/CTS +hardware flow control, one needs to set the +UART_CS_{RX,TX}_READY_ORIDE bits as other than on AR933x where this +UART is used as primary/console, the bootloader on AR934x typically +doesn't set those bits. +Setting them explicitely on AR933x should not do any harm, so just +set them unconditionally. + +Tested-by: Chuanhong Guo +Signed-off-by: Daniel Golle +--- + drivers/tty/serial/ar933x_uart.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/tty/serial/ar933x_uart.c ++++ b/drivers/tty/serial/ar933x_uart.c +@@ -286,6 +286,10 @@ static void ar933x_uart_set_termios(stru + ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, + AR933X_UART_CS_HOST_INT_EN); + ++ /* enable RX and TX ready overide */ ++ ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, ++ AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE); ++ + /* reenable the UART */ + ar933x_uart_rmw(up, AR933X_UART_CS_REG, + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, +@@ -418,6 +422,10 @@ static int ar933x_uart_startup(struct ua + ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, + AR933X_UART_CS_HOST_INT_EN); + ++ /* enable RX and TX ready overide */ ++ ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, ++ AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE); ++ + /* Enable RX interrupts */ + up->ier = AR933X_UART_INT_RX_VALID; + ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier); diff --git a/target/linux/ath79/patches-5.4/404-mtd-cybertan-trx-parser.patch b/target/linux/ath79/patches-5.4/404-mtd-cybertan-trx-parser.patch new file mode 100644 index 0000000000..c26ef60725 --- /dev/null +++ b/target/linux/ath79/patches-5.4/404-mtd-cybertan-trx-parser.patch @@ -0,0 +1,24 @@ +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -1,3 +1,4 @@ ++obj-$(CONFIG_MTD_PARSER_CYBERTAN) += parser_cybertan.o + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -94,6 +94,14 @@ config MTD_AFS_PARTS + for your particular device. It won't happen automatically. The + 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example. + ++config MTD_PARSER_CYBERTAN ++ tristate "Parser for Cybertan format partitions" ++ depends on MTD && (ATH79 || COMPILE_TEST) ++ help ++ Cybertan has a proprietory header than encompasses a Broadcom trx ++ header. This driver will parse the header and take care of the ++ special offsets that result in the extra headers. ++ + config MTD_PARSER_TRX + tristate "Parser for TRX format partitions" + depends on MTD && (BCM47XX || ARCH_BCM_5301X || COMPILE_TEST) diff --git a/target/linux/ath79/patches-5.4/405-mtd-tp-link-partition-parser.patch b/target/linux/ath79/patches-5.4/405-mtd-tp-link-partition-parser.patch new file mode 100644 index 0000000000..7ea07f376c --- /dev/null +++ b/target/linux/ath79/patches-5.4/405-mtd-tp-link-partition-parser.patch @@ -0,0 +1,25 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -62,6 +62,12 @@ config MTD_MYLOADER_PARTS + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. + ++config MTD_TPLINK_PARTS ++ tristate "TP-Link AR7XXX/AR9XXX partitioning support" ++ depends on ATH79 ++ ---help--- ++ TBD. ++ + comment "User Modules And Translation Layers" + + # +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_MTD) += mtd.o + mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o + + obj-y += parsers/ ++obj-$(CONFIG_MTD_TPLINK_PARTS) += tplinkpart.o + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o diff --git a/target/linux/ath79/patches-5.4/408-mtd-redboot_partition_scan.patch b/target/linux/ath79/patches-5.4/408-mtd-redboot_partition_scan.patch new file mode 100644 index 0000000000..8f3ee32f57 --- /dev/null +++ b/target/linux/ath79/patches-5.4/408-mtd-redboot_partition_scan.patch @@ -0,0 +1,44 @@ +--- a/drivers/mtd/parsers/redboot.c ++++ b/drivers/mtd/parsers/redboot.c +@@ -85,12 +85,18 @@ static int parse_redboot_partitions(stru + + parse_redboot_of(master); + ++ buf = vmalloc(master->erasesize); ++ if (!buf) ++ return -ENOMEM; ++ ++ restart: + if ( directory < 0 ) { + offset = master->size + directory * master->erasesize; + while (mtd_block_isbad(master, offset)) { + if (!offset) { + nogood: + printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n"); ++ vfree(buf); + return -EIO; + } + offset -= master->erasesize; +@@ -103,10 +109,6 @@ static int parse_redboot_partitions(stru + goto nogood; + } + } +- buf = vmalloc(master->erasesize); +- +- if (!buf) +- return -ENOMEM; + + printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", + master->name, offset); +@@ -179,6 +181,11 @@ static int parse_redboot_partitions(stru + } + if (i == numslots) { + /* Didn't find it */ ++ if (offset + master->erasesize < master->size) { ++ /* not at the end of the flash yet, maybe next block :) */ ++ directory++; ++ goto restart; ++ } + printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", + master->name); + ret = 0; diff --git a/target/linux/ath79/patches-5.4/425-at803x-allow-sgmii-aneg-override.patch b/target/linux/ath79/patches-5.4/425-at803x-allow-sgmii-aneg-override.patch new file mode 100644 index 0000000000..396b1ee7a2 --- /dev/null +++ b/target/linux/ath79/patches-5.4/425-at803x-allow-sgmii-aneg-override.patch @@ -0,0 +1,16 @@ +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -383,6 +383,13 @@ static int at803x_aneg_done(struct phy_d + if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) { + phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n"); + aneg_done = 0; ++#ifdef CONFIG_OF_MDIO ++ if (phydev->mdio.dev.of_node && ++ of_property_read_bool(phydev->mdio.dev.of_node, ++ "at803x-override-sgmii-link-check")) { ++ aneg_done = 1; ++ } ++#endif + } + /* switch back to copper page */ + phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); diff --git a/target/linux/ath79/patches-5.4/430-drivers-link-spi-before-mtd.patch b/target/linux/ath79/patches-5.4/430-drivers-link-spi-before-mtd.patch new file mode 100644 index 0000000000..4c6558db90 --- /dev/null +++ b/target/linux/ath79/patches-5.4/430-drivers-link-spi-before-mtd.patch @@ -0,0 +1,12 @@ +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -80,8 +80,8 @@ obj-y += scsi/ + obj-y += nvme/ + obj-$(CONFIG_ATA) += ata/ + obj-$(CONFIG_TARGET_CORE) += target/ +-obj-$(CONFIG_MTD) += mtd/ + obj-$(CONFIG_SPI) += spi/ ++obj-$(CONFIG_MTD) += mtd/ + obj-$(CONFIG_SPMI) += spmi/ + obj-$(CONFIG_HSI) += hsi/ + obj-$(CONFIG_SLIMBUS) += slimbus/ diff --git a/target/linux/ath79/patches-5.4/440-mtd-ar934x-nand-driver.patch b/target/linux/ath79/patches-5.4/440-mtd-ar934x-nand-driver.patch new file mode 100644 index 0000000000..8cb47ff703 --- /dev/null +++ b/target/linux/ath79/patches-5.4/440-mtd-ar934x-nand-driver.patch @@ -0,0 +1,25 @@ +--- a/drivers/mtd/nand/raw/Kconfig ++++ b/drivers/mtd/nand/raw/Kconfig +@@ -544,4 +544,12 @@ config MTD_NAND_DISKONCHIP_BBTWRITE + load time (assuming you build diskonchip as a module) with the module + parameter "inftl_bbt_write=1". + ++config MTD_NAND_AR934X ++ tristate "Support for NAND controller on Qualcomm Atheros AR934x/QCA955x SoCs" ++ depends on ATH79 || COMPILE_TEST ++ depends on HAS_IOMEM ++ help ++ Enables support for NAND controller on Qualcomm Atheros SoCs. ++ This controller is found on AR934x and QCA955x SoCs. ++ + endif # MTD_RAW_NAND +--- a/drivers/mtd/nand/raw/Makefile ++++ b/drivers/mtd/nand/raw/Makefile +@@ -57,6 +57,7 @@ obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nan + obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o + obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o + obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o ++obj-$(CONFIG_MTD_NAND_AR934X) += ar934x_nand.o + + nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o + nand-objs += nand_onfi.o diff --git a/target/linux/ath79/patches-5.4/470-MIPS-ath79-swizzle-pci-address-for-ar71xx.patch b/target/linux/ath79/patches-5.4/470-MIPS-ath79-swizzle-pci-address-for-ar71xx.patch new file mode 100644 index 0000000000..924faec509 --- /dev/null +++ b/target/linux/ath79/patches-5.4/470-MIPS-ath79-swizzle-pci-address-for-ar71xx.patch @@ -0,0 +1,98 @@ +--- /dev/null ++++ b/arch/mips/include/asm/mach-ath79/mangle-port.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (C) 2012 Gabor Juhos ++ * ++ * This file was derived from: inlude/asm-mips/mach-generic/mangle-port.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * ++ * 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. ++ */ ++ ++#ifndef __ASM_MACH_ATH79_MANGLE_PORT_H ++#define __ASM_MACH_ATH79_MANGLE_PORT_H ++ ++#ifdef CONFIG_PCI_AR71XX ++extern unsigned long (ath79_pci_swizzle_b)(unsigned long port); ++extern unsigned long (ath79_pci_swizzle_w)(unsigned long port); ++#else ++#define ath79_pci_swizzle_b(port) (port) ++#define ath79_pci_swizzle_w(port) (port) ++#endif ++ ++#define __swizzle_addr_b(port) ath79_pci_swizzle_b(port) ++#define __swizzle_addr_w(port) ath79_pci_swizzle_w(port) ++#define __swizzle_addr_l(port) (port) ++#define __swizzle_addr_q(port) (port) ++ ++# define ioswabb(a, x) (x) ++# define __mem_ioswabb(a, x) (x) ++# define ioswabw(a, x) (x) ++# define __mem_ioswabw(a, x) cpu_to_le16(x) ++# define ioswabl(a, x) (x) ++# define __mem_ioswabl(a, x) cpu_to_le32(x) ++# define ioswabq(a, x) (x) ++# define __mem_ioswabq(a, x) cpu_to_le64(x) ++ ++#endif /* __ASM_MACH_ATH79_MANGLE_PORT_H */ +--- a/arch/mips/pci/pci-ar71xx.c ++++ b/arch/mips/pci/pci-ar71xx.c +@@ -68,6 +68,45 @@ static const u32 ar71xx_pci_read_mask[8] + 0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0 + }; + ++static unsigned long (*__ath79_pci_swizzle_b)(unsigned long port); ++static unsigned long (*__ath79_pci_swizzle_w)(unsigned long port); ++ ++static inline bool ar71xx_is_pci_addr(unsigned long port) ++{ ++ unsigned long phys = CPHYSADDR(port); ++ ++ return (phys >= AR71XX_PCI_MEM_BASE && ++ phys < AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE); ++} ++ ++static unsigned long ar71xx_pci_swizzle_b(unsigned long port) ++{ ++ return ar71xx_is_pci_addr(port) ? port ^ 3 : port; ++} ++ ++static unsigned long ar71xx_pci_swizzle_w(unsigned long port) ++{ ++ return ar71xx_is_pci_addr(port) ? port ^ 2 : port; ++} ++ ++unsigned long ath79_pci_swizzle_b(unsigned long port) ++{ ++ if (__ath79_pci_swizzle_b) ++ return __ath79_pci_swizzle_b(port); ++ ++ return port; ++} ++EXPORT_SYMBOL(ath79_pci_swizzle_b); ++ ++unsigned long ath79_pci_swizzle_w(unsigned long port) ++{ ++ if (__ath79_pci_swizzle_w) ++ return __ath79_pci_swizzle_w(port); ++ ++ return port; ++} ++EXPORT_SYMBOL(ath79_pci_swizzle_w); ++ + static inline u32 ar71xx_pci_get_ble(int where, int size, int local) + { + u32 t; +@@ -276,6 +315,9 @@ static int ar71xx_pci_probe(struct platf + + register_pci_controller(&apc->pci_ctrl); + ++ __ath79_pci_swizzle_b = ar71xx_pci_swizzle_b; ++ __ath79_pci_swizzle_w = ar71xx_pci_swizzle_w; ++ + return 0; + } + diff --git a/target/linux/ath79/patches-5.4/900-mdio_bitbang_ignore_ta_value.patch b/target/linux/ath79/patches-5.4/900-mdio_bitbang_ignore_ta_value.patch new file mode 100644 index 0000000000..148d843518 --- /dev/null +++ b/target/linux/ath79/patches-5.4/900-mdio_bitbang_ignore_ta_value.patch @@ -0,0 +1,32 @@ +--- a/drivers/net/phy/mdio-bitbang.c ++++ b/drivers/net/phy/mdio-bitbang.c +@@ -152,7 +152,7 @@ static int mdiobb_cmd_addr(struct mdiobb + static int mdiobb_read(struct mii_bus *bus, int phy, int reg) + { + struct mdiobb_ctrl *ctrl = bus->priv; +- int ret, i; ++ int ret; + + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); +@@ -162,19 +162,7 @@ static int mdiobb_read(struct mii_bus *b + + ctrl->ops->set_mdio_dir(ctrl, 0); + +- /* check the turnaround bit: the PHY should be driving it to zero, if this +- * PHY is listed in phy_ignore_ta_mask as having broken TA, skip that +- */ +- if (mdiobb_get_bit(ctrl) != 0 && +- !(bus->phy_ignore_ta_mask & (1 << phy))) { +- /* PHY didn't drive TA low -- flush any bits it +- * may be trying to send. +- */ +- for (i = 0; i < 32; i++) +- mdiobb_get_bit(ctrl); +- +- return 0xffff; +- } ++ mdiobb_get_bit(ctrl); + + ret = mdiobb_get_num(ctrl, 16); + mdiobb_get_bit(ctrl); diff --git a/target/linux/ath79/patches-5.4/901-phy-mdio-bitbang-prevent-rescheduling-during-command.patch b/target/linux/ath79/patches-5.4/901-phy-mdio-bitbang-prevent-rescheduling-during-command.patch new file mode 100644 index 0000000000..8c71e5b5ca --- /dev/null +++ b/target/linux/ath79/patches-5.4/901-phy-mdio-bitbang-prevent-rescheduling-during-command.patch @@ -0,0 +1,61 @@ +From 66e584435ac0de6e0abeb6d7166fe4fe25d6bb73 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Tue, 16 Jun 2015 13:15:08 +0200 +Subject: [PATCH] phy/mdio-bitbang: prevent rescheduling during command + +It seems some phys have some maximum timings for accessing the MDIO line, +resulting in bit errors under cpu stress. Prevent this from happening by +disabling interrupts when sending commands. + +Signed-off-by: Jonas Gorski +--- + drivers/net/phy/mdio-bitbang.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/net/phy/mdio-bitbang.c ++++ b/drivers/net/phy/mdio-bitbang.c +@@ -14,6 +14,7 @@ + * Vitaly Bordug + */ + ++#include + #include + #include + #include +@@ -153,7 +154,9 @@ static int mdiobb_read(struct mii_bus *b + { + struct mdiobb_ctrl *ctrl = bus->priv; + int ret; ++ unsigned long flags; + ++ local_irq_save(flags); + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg); +@@ -166,13 +169,17 @@ static int mdiobb_read(struct mii_bus *b + + ret = mdiobb_get_num(ctrl, 16); + mdiobb_get_bit(ctrl); ++ local_irq_restore(flags); ++ + return ret; + } + + static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) + { + struct mdiobb_ctrl *ctrl = bus->priv; ++ unsigned long flags; + ++ local_irq_save(flags); + if (reg & MII_ADDR_C45) { + reg = mdiobb_cmd_addr(ctrl, phy, reg); + mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); +@@ -187,6 +194,8 @@ static int mdiobb_write(struct mii_bus * + + ctrl->ops->set_mdio_dir(ctrl, 0); + mdiobb_get_bit(ctrl); ++ local_irq_restore(flags); ++ + return 0; + } + diff --git a/target/linux/ath79/patches-5.4/910-unaligned_access_hacks.patch b/target/linux/ath79/patches-5.4/910-unaligned_access_hacks.patch new file mode 100644 index 0000000000..950430a2a5 --- /dev/null +++ b/target/linux/ath79/patches-5.4/910-unaligned_access_hacks.patch @@ -0,0 +1,869 @@ +--- a/arch/mips/include/asm/checksum.h ++++ b/arch/mips/include/asm/checksum.h +@@ -134,26 +134,30 @@ static inline __sum16 ip_fast_csum(const + const unsigned int *stop = word + ihl; + unsigned int csum; + int carry; ++ unsigned int w; + +- csum = word[0]; +- csum += word[1]; +- carry = (csum < word[1]); ++ csum = net_hdr_word(word++); ++ ++ w = net_hdr_word(word++); ++ csum += w; ++ carry = (csum < w); + csum += carry; + +- csum += word[2]; +- carry = (csum < word[2]); ++ w = net_hdr_word(word++); ++ csum += w; ++ carry = (csum < w); + csum += carry; + +- csum += word[3]; +- carry = (csum < word[3]); ++ w = net_hdr_word(word++); ++ csum += w; ++ carry = (csum < w); + csum += carry; + +- word += 4; + do { +- csum += *word; +- carry = (csum < *word); ++ w = net_hdr_word(word++); ++ csum += w; ++ carry = (csum < w); + csum += carry; +- word++; + } while (word != stop); + + return csum_fold(csum); +@@ -214,73 +218,6 @@ static inline __sum16 ip_compute_csum(co + return csum_fold(csum_partial(buff, len, 0)); + } + +-#define _HAVE_ARCH_IPV6_CSUM +-static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr, +- const struct in6_addr *daddr, +- __u32 len, __u8 proto, +- __wsum sum) +-{ +- __wsum tmp; +- +- __asm__( +- " .set push # csum_ipv6_magic\n" +- " .set noreorder \n" +- " .set noat \n" +- " addu %0, %5 # proto (long in network byte order)\n" +- " sltu $1, %0, %5 \n" +- " addu %0, $1 \n" +- +- " addu %0, %6 # csum\n" +- " sltu $1, %0, %6 \n" +- " lw %1, 0(%2) # four words source address\n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " lw %1, 4(%2) \n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " lw %1, 8(%2) \n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " lw %1, 12(%2) \n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " lw %1, 0(%3) \n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " lw %1, 4(%3) \n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " lw %1, 8(%3) \n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " lw %1, 12(%3) \n" +- " addu %0, $1 \n" +- " addu %0, %1 \n" +- " sltu $1, %0, %1 \n" +- +- " addu %0, $1 # Add final carry\n" +- " .set pop" +- : "=&r" (sum), "=&r" (tmp) +- : "r" (saddr), "r" (daddr), +- "0" (htonl(len)), "r" (htonl(proto)), "r" (sum)); +- +- return csum_fold(sum); +-} +- + #include + #endif /* CONFIG_GENERIC_CSUM */ + +--- a/include/uapi/linux/ip.h ++++ b/include/uapi/linux/ip.h +@@ -103,7 +103,7 @@ struct iphdr { + __be32 saddr; + __be32 daddr; + /*The options start here. */ +-}; ++} __attribute__((packed, aligned(2))); + + + struct ip_auth_hdr { +--- a/include/uapi/linux/ipv6.h ++++ b/include/uapi/linux/ipv6.h +@@ -131,7 +131,7 @@ struct ipv6hdr { + + struct in6_addr saddr; + struct in6_addr daddr; +-}; ++} __attribute__((packed, aligned(2))); + + + /* index values for the variables in ipv6_devconf */ +--- a/include/uapi/linux/tcp.h ++++ b/include/uapi/linux/tcp.h +@@ -55,7 +55,7 @@ struct tcphdr { + __be16 window; + __sum16 check; + __be16 urg_ptr; +-}; ++} __attribute__((packed, aligned(2))); + + /* + * The union cast uses a gcc extension to avoid aliasing problems +@@ -65,7 +65,7 @@ struct tcphdr { + union tcp_word_hdr { + struct tcphdr hdr; + __be32 words[5]; +-}; ++} __attribute__((packed, aligned(2))); + + #define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) + +--- a/include/uapi/linux/udp.h ++++ b/include/uapi/linux/udp.h +@@ -25,7 +25,7 @@ struct udphdr { + __be16 dest; + __be16 len; + __sum16 check; +-}; ++} __attribute__((packed, aligned(2))); + + /* UDP socket options */ + #define UDP_CORK 1 /* Never send partially complete segments */ +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -271,8 +271,8 @@ nf_ct_get_tuple(const struct sk_buff *sk + + switch (l3num) { + case NFPROTO_IPV4: +- tuple->src.u3.ip = ap[0]; +- tuple->dst.u3.ip = ap[1]; ++ tuple->src.u3.ip = net_hdr_word(ap++); ++ tuple->dst.u3.ip = net_hdr_word(ap); + break; + case NFPROTO_IPV6: + memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6)); +--- a/include/uapi/linux/icmp.h ++++ b/include/uapi/linux/icmp.h +@@ -82,7 +82,7 @@ struct icmphdr { + } frag; + __u8 reserved[4]; + } un; +-}; ++} __attribute__((packed, aligned(2))); + + + /* +--- a/include/uapi/linux/in6.h ++++ b/include/uapi/linux/in6.h +@@ -43,7 +43,7 @@ struct in6_addr { + #define s6_addr16 in6_u.u6_addr16 + #define s6_addr32 in6_u.u6_addr32 + #endif +-}; ++} __attribute__((packed, aligned(2))); + #endif /* __UAPI_DEF_IN6_ADDR */ + + #if __UAPI_DEF_SOCKADDR_IN6 +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -849,10 +850,10 @@ static void tcp_v6_send_response(const s + topt = (__be32 *)(t1 + 1); + + if (tsecr) { +- *topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | +- (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); +- *topt++ = htonl(tsval); +- *topt++ = htonl(tsecr); ++ put_unaligned_be32((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | ++ (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP, topt++); ++ put_unaligned_be32(tsval, topt++); ++ put_unaligned_be32(tsecr, topt++); + } + + #ifdef CONFIG_TCP_MD5SIG +--- a/include/linux/ipv6.h ++++ b/include/linux/ipv6.h +@@ -6,6 +6,7 @@ + + #define ipv6_optlen(p) (((p)->hdrlen+1) << 3) + #define ipv6_authlen(p) (((p)->hdrlen+2) << 2) ++ + /* + * This structure contains configuration options per IPv6 link. + */ +--- a/net/ipv6/datagram.c ++++ b/net/ipv6/datagram.c +@@ -476,7 +476,7 @@ int ipv6_recv_error(struct sock *sk, str + ipv6_iface_scope_id(&sin->sin6_addr, + IP6CB(skb)->iif); + } else { +- ipv6_addr_set_v4mapped(*(__be32 *)(nh + serr->addr_offset), ++ ipv6_addr_set_v4mapped(net_hdr_word(nh + serr->addr_offset), + &sin->sin6_addr); + sin->sin6_scope_id = 0; + } +@@ -830,12 +830,12 @@ int ip6_datagram_send_ctl(struct net *ne + } + + if (fl6->flowlabel&IPV6_FLOWINFO_MASK) { +- if ((fl6->flowlabel^*(__be32 *)CMSG_DATA(cmsg))&~IPV6_FLOWINFO_MASK) { ++ if ((fl6->flowlabel^net_hdr_word(CMSG_DATA(cmsg)))&~IPV6_FLOWINFO_MASK) { + err = -EINVAL; + goto exit_f; + } + } +- fl6->flowlabel = IPV6_FLOWINFO_MASK & *(__be32 *)CMSG_DATA(cmsg); ++ fl6->flowlabel = IPV6_FLOWINFO_MASK & net_hdr_word(CMSG_DATA(cmsg)); + break; + + case IPV6_2292HOPOPTS: +--- a/net/ipv6/exthdrs.c ++++ b/net/ipv6/exthdrs.c +@@ -752,7 +752,7 @@ static bool ipv6_hop_jumbo(struct sk_buf + goto drop; + } + +- pkt_len = ntohl(*(__be32 *)(nh + optoff + 2)); ++ pkt_len = ntohl(net_hdr_word(nh + optoff + 2)); + if (pkt_len <= IPV6_MAXPLEN) { + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); +--- a/include/linux/types.h ++++ b/include/linux/types.h +@@ -225,5 +225,11 @@ struct callback_head { + typedef void (*rcu_callback_t)(struct rcu_head *head); + typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func); + ++struct net_hdr_word { ++ u32 words[1]; ++} __attribute__((packed, aligned(2))); ++ ++#define net_hdr_word(_p) (((struct net_hdr_word *) (_p))->words[0]) ++ + #endif /* __ASSEMBLY__ */ + #endif /* _LINUX_TYPES_H */ +--- a/net/ipv4/af_inet.c ++++ b/net/ipv4/af_inet.c +@@ -1441,8 +1441,8 @@ struct sk_buff *inet_gro_receive(struct + if (unlikely(ip_fast_csum((u8 *)iph, 5))) + goto out_unlock; + +- id = ntohl(*(__be32 *)&iph->id); +- flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb)) | (id & ~IP_DF)); ++ id = ntohl(net_hdr_word(&iph->id)); ++ flush = (u16)((ntohl(net_hdr_word(iph)) ^ skb_gro_len(skb)) | (id & ~IP_DF)); + id >>= 16; + + list_for_each_entry(p, head, list) { +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -457,48 +457,53 @@ static void tcp_options_write(__be32 *pt + u16 options = opts->options; /* mungable copy */ + + if (unlikely(OPTION_MD5 & options)) { +- *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | +- (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); ++ net_hdr_word(ptr++) = ++ htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | ++ (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); + /* overload cookie hash location */ + opts->hash_location = (__u8 *)ptr; + ptr += 4; + } + + if (unlikely(opts->mss)) { +- *ptr++ = htonl((TCPOPT_MSS << 24) | +- (TCPOLEN_MSS << 16) | +- opts->mss); ++ net_hdr_word(ptr++) = ++ htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | ++ opts->mss); + } + + if (likely(OPTION_TS & options)) { + if (unlikely(OPTION_SACK_ADVERTISE & options)) { +- *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | +- (TCPOLEN_SACK_PERM << 16) | +- (TCPOPT_TIMESTAMP << 8) | +- TCPOLEN_TIMESTAMP); ++ net_hdr_word(ptr++) = ++ htonl((TCPOPT_SACK_PERM << 24) | ++ (TCPOLEN_SACK_PERM << 16) | ++ (TCPOPT_TIMESTAMP << 8) | ++ TCPOLEN_TIMESTAMP); + options &= ~OPTION_SACK_ADVERTISE; + } else { +- *ptr++ = htonl((TCPOPT_NOP << 24) | +- (TCPOPT_NOP << 16) | +- (TCPOPT_TIMESTAMP << 8) | +- TCPOLEN_TIMESTAMP); ++ net_hdr_word(ptr++) = ++ htonl((TCPOPT_NOP << 24) | ++ (TCPOPT_NOP << 16) | ++ (TCPOPT_TIMESTAMP << 8) | ++ TCPOLEN_TIMESTAMP); + } +- *ptr++ = htonl(opts->tsval); +- *ptr++ = htonl(opts->tsecr); ++ net_hdr_word(ptr++) = htonl(opts->tsval); ++ net_hdr_word(ptr++) = htonl(opts->tsecr); + } + + if (unlikely(OPTION_SACK_ADVERTISE & options)) { +- *ptr++ = htonl((TCPOPT_NOP << 24) | +- (TCPOPT_NOP << 16) | +- (TCPOPT_SACK_PERM << 8) | +- TCPOLEN_SACK_PERM); ++ net_hdr_word(ptr++) = ++ htonl((TCPOPT_NOP << 24) | ++ (TCPOPT_NOP << 16) | ++ (TCPOPT_SACK_PERM << 8) | ++ TCPOLEN_SACK_PERM); + } + + if (unlikely(OPTION_WSCALE & options)) { +- *ptr++ = htonl((TCPOPT_NOP << 24) | +- (TCPOPT_WINDOW << 16) | +- (TCPOLEN_WINDOW << 8) | +- opts->ws); ++ net_hdr_word(ptr++) = ++ htonl((TCPOPT_NOP << 24) | ++ (TCPOPT_WINDOW << 16) | ++ (TCPOLEN_WINDOW << 8) | ++ opts->ws); + } + + if (unlikely(opts->num_sack_blocks)) { +@@ -506,16 +511,17 @@ static void tcp_options_write(__be32 *pt + tp->duplicate_sack : tp->selective_acks; + int this_sack; + +- *ptr++ = htonl((TCPOPT_NOP << 24) | +- (TCPOPT_NOP << 16) | +- (TCPOPT_SACK << 8) | +- (TCPOLEN_SACK_BASE + (opts->num_sack_blocks * ++ net_hdr_word(ptr++) = ++ htonl((TCPOPT_NOP << 24) | ++ (TCPOPT_NOP << 16) | ++ (TCPOPT_SACK << 8) | ++ (TCPOLEN_SACK_BASE + (opts->num_sack_blocks * + TCPOLEN_SACK_PERBLOCK))); + + for (this_sack = 0; this_sack < opts->num_sack_blocks; + ++this_sack) { +- *ptr++ = htonl(sp[this_sack].start_seq); +- *ptr++ = htonl(sp[this_sack].end_seq); ++ net_hdr_word(ptr++) = htonl(sp[this_sack].start_seq); ++ net_hdr_word(ptr++) = htonl(sp[this_sack].end_seq); + } + + tp->rx_opt.dsack = 0; +@@ -528,13 +534,14 @@ static void tcp_options_write(__be32 *pt + + if (foc->exp) { + len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len; +- *ptr = htonl((TCPOPT_EXP << 24) | (len << 16) | ++ net_hdr_word(ptr) = ++ htonl((TCPOPT_EXP << 24) | (len << 16) | + TCPOPT_FASTOPEN_MAGIC); + p += TCPOLEN_EXP_FASTOPEN_BASE; + } else { + len = TCPOLEN_FASTOPEN_BASE + foc->len; +- *p++ = TCPOPT_FASTOPEN; +- *p++ = len; ++ net_hdr_word(p++) = TCPOPT_FASTOPEN; ++ net_hdr_word(p++) = len; + } + + memcpy(p, foc->val, foc->len); +--- a/include/uapi/linux/igmp.h ++++ b/include/uapi/linux/igmp.h +@@ -33,7 +33,7 @@ struct igmphdr { + __u8 code; /* For newer IGMP */ + __sum16 csum; + __be32 group; +-}; ++} __attribute__((packed, aligned(2))); + + /* V3 group record types [grec_type] */ + #define IGMPV3_MODE_IS_INCLUDE 1 +@@ -49,7 +49,7 @@ struct igmpv3_grec { + __be16 grec_nsrcs; + __be32 grec_mca; + __be32 grec_src[0]; +-}; ++} __attribute__((packed, aligned(2))); + + struct igmpv3_report { + __u8 type; +@@ -58,7 +58,7 @@ struct igmpv3_report { + __be16 resv2; + __be16 ngrec; + struct igmpv3_grec grec[0]; +-}; ++} __attribute__((packed, aligned(2))); + + struct igmpv3_query { + __u8 type; +@@ -79,7 +79,7 @@ struct igmpv3_query { + __u8 qqic; + __be16 nsrcs; + __be32 srcs[0]; +-}; ++} __attribute__((packed, aligned(2))); + + #define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* From RFC1112 */ + #define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Ditto */ +--- a/net/core/flow_dissector.c ++++ b/net/core/flow_dissector.c +@@ -196,7 +196,7 @@ __be32 __skb_flow_get_ports(const struct + ports = __skb_header_pointer(skb, thoff + poff, + sizeof(_ports), data, hlen, &_ports); + if (ports) +- return *ports; ++ return (__be32)net_hdr_word(ports); + } + + return 0; +--- a/include/uapi/linux/icmpv6.h ++++ b/include/uapi/linux/icmpv6.h +@@ -77,7 +77,7 @@ struct icmp6hdr { + #define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other + #define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime + #define icmp6_router_pref icmp6_dataun.u_nd_ra.router_pref +-}; ++} __attribute__((packed, aligned(2))); + + + #define ICMPV6_ROUTER_PREF_LOW 0x3 +--- a/include/net/ndisc.h ++++ b/include/net/ndisc.h +@@ -92,7 +92,7 @@ struct ra_msg { + struct icmp6hdr icmph; + __be32 reachable_time; + __be32 retrans_timer; +-}; ++} __attribute__((packed, aligned(2))); + + struct rd_msg { + struct icmp6hdr icmph; +@@ -371,10 +371,10 @@ static inline u32 ndisc_hashfn(const voi + { + const u32 *p32 = pkey; + +- return (((p32[0] ^ hash32_ptr(dev)) * hash_rnd[0]) + +- (p32[1] * hash_rnd[1]) + +- (p32[2] * hash_rnd[2]) + +- (p32[3] * hash_rnd[3])); ++ return (((net_hdr_word(&p32[0]) ^ hash32_ptr(dev)) * hash_rnd[0]) + ++ (net_hdr_word(&p32[1]) * hash_rnd[1]) + ++ (net_hdr_word(&p32[2]) * hash_rnd[2]) + ++ (net_hdr_word(&p32[3]) * hash_rnd[3])); + } + + static inline struct neighbour *__ipv6_neigh_lookup_noref(struct net_device *dev, const void *pkey) +--- a/net/sched/cls_u32.c ++++ b/net/sched/cls_u32.c +@@ -155,7 +155,7 @@ next_knode: + data = skb_header_pointer(skb, toff, 4, &hdata); + if (!data) + goto out; +- if ((*data ^ key->val) & key->mask) { ++ if ((net_hdr_word(data) ^ key->val) & key->mask) { + n = rcu_dereference_bh(n->next); + goto next_knode; + } +@@ -206,8 +206,8 @@ check_terminal: + &hdata); + if (!data) + goto out; +- sel = ht->divisor & u32_hash_fold(*data, &n->sel, +- n->fshift); ++ sel = ht->divisor & u32_hash_fold(net_hdr_word(data), ++ &n->sel, n->fshift); + } + if (!(n->sel.flags & (TC_U32_VAROFFSET | TC_U32_OFFSET | TC_U32_EAT))) + goto next_ht; +--- a/net/ipv6/ip6_offload.c ++++ b/net/ipv6/ip6_offload.c +@@ -240,7 +240,7 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff * + continue; + + iph2 = (struct ipv6hdr *)(p->data + off); +- first_word = *(__be32 *)iph ^ *(__be32 *)iph2; ++ first_word = net_hdr_word(iph) ^ net_hdr_word(iph2); + + /* All fields must match except length and Traffic Class. + * XXX skbs on the gro_list have all been parsed and pulled +--- a/include/net/addrconf.h ++++ b/include/net/addrconf.h +@@ -47,7 +47,7 @@ struct prefix_info { + __be32 reserved2; + + struct in6_addr prefix; +-}; ++} __attribute__((packed, aligned(2))); + + #include + #include +--- a/include/net/inet_ecn.h ++++ b/include/net/inet_ecn.h +@@ -125,9 +125,9 @@ static inline int IP6_ECN_set_ce(struct + if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph))) + return 0; + +- from = *(__be32 *)iph; ++ from = net_hdr_word(iph); + to = from | htonl(INET_ECN_CE << 20); +- *(__be32 *)iph = to; ++ net_hdr_word(iph) = to; + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_add(csum_sub(skb->csum, (__force __wsum)from), + (__force __wsum)to); +--- a/include/net/ipv6.h ++++ b/include/net/ipv6.h +@@ -146,7 +146,7 @@ struct frag_hdr { + __u8 reserved; + __be16 frag_off; + __be32 identification; +-}; ++} __attribute__((packed, aligned(2))); + + #define IP6_MF 0x0001 + #define IP6_OFFSET 0xFFF8 +@@ -557,8 +557,8 @@ static inline void __ipv6_addr_set_half( + } + #endif + #endif +- addr[0] = wh; +- addr[1] = wl; ++ net_hdr_word(&addr[0]) = wh; ++ net_hdr_word(&addr[1]) = wl; + } + + static inline void ipv6_addr_set(struct in6_addr *addr, +@@ -617,6 +617,8 @@ static inline bool ipv6_prefix_equal(con + const __be32 *a1 = addr1->s6_addr32; + const __be32 *a2 = addr2->s6_addr32; + unsigned int pdw, pbi; ++ /* Used for last <32-bit fraction of prefix */ ++ u32 pbia1, pbia2; + + /* check complete u32 in prefix */ + pdw = prefixlen >> 5; +@@ -625,7 +627,9 @@ static inline bool ipv6_prefix_equal(con + + /* check incomplete u32 in prefix */ + pbi = prefixlen & 0x1f; +- if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi)))) ++ pbia1 = net_hdr_word(&a1[pdw]); ++ pbia2 = net_hdr_word(&a2[pdw]); ++ if (pbi && ((pbia1 ^ pbia2) & htonl((0xffffffff) << (32 - pbi)))) + return false; + + return true; +@@ -741,13 +745,13 @@ static inline void ipv6_addr_set_v4mappe + */ + static inline int __ipv6_addr_diff32(const void *token1, const void *token2, int addrlen) + { +- const __be32 *a1 = token1, *a2 = token2; ++ const struct in6_addr *a1 = token1, *a2 = token2; + int i; + + addrlen >>= 2; + + for (i = 0; i < addrlen; i++) { +- __be32 xb = a1[i] ^ a2[i]; ++ __be32 xb = a1->s6_addr32[i] ^ a2->s6_addr32[i]; + if (xb) + return i * 32 + 31 - __fls(ntohl(xb)); + } +@@ -934,17 +938,18 @@ static inline int ip6_multipath_hash_pol + static inline void ip6_flow_hdr(struct ipv6hdr *hdr, unsigned int tclass, + __be32 flowlabel) + { +- *(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | flowlabel; ++ net_hdr_word((__be32 *)hdr) = ++ htonl(0x60000000 | (tclass << 20)) | flowlabel; + } + + static inline __be32 ip6_flowinfo(const struct ipv6hdr *hdr) + { +- return *(__be32 *)hdr & IPV6_FLOWINFO_MASK; ++ return net_hdr_word((__be32 *)hdr) & IPV6_FLOWINFO_MASK; + } + + static inline __be32 ip6_flowlabel(const struct ipv6hdr *hdr) + { +- return *(__be32 *)hdr & IPV6_FLOWLABEL_MASK; ++ return net_hdr_word((__be32 *)hdr) & IPV6_FLOWLABEL_MASK; + } + + static inline u8 ip6_tclass(__be32 flowinfo) +--- a/include/net/secure_seq.h ++++ b/include/net/secure_seq.h +@@ -3,6 +3,7 @@ + #define _NET_SECURE_SEQ + + #include ++#include + + u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); + u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, +--- a/include/uapi/linux/in.h ++++ b/include/uapi/linux/in.h +@@ -84,7 +84,7 @@ enum { + /* Internet address. */ + struct in_addr { + __be32 s_addr; +-}; ++} __attribute__((packed, aligned(2))); + #endif + + #define IP_TOS 1 +--- a/net/ipv6/ip6_fib.c ++++ b/net/ipv6/ip6_fib.c +@@ -140,7 +140,7 @@ static __be32 addr_bit_set(const void *t + * See include/asm-generic/bitops/le.h. + */ + return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) & +- addr[fn_bit >> 5]; ++ net_hdr_word(&addr[fn_bit >> 5]); + } + + struct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh) +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -415,7 +415,7 @@ static void tcp_sack(const struct sk_buf + + /* Fast path for timestamp-only option */ + if (length == TCPOLEN_TSTAMP_ALIGNED +- && *(__be32 *)ptr == htonl((TCPOPT_NOP << 24) ++ && net_hdr_word(ptr) == htonl((TCPOPT_NOP << 24) + | (TCPOPT_NOP << 16) + | (TCPOPT_TIMESTAMP << 8) + | TCPOLEN_TIMESTAMP)) +--- a/net/xfrm/xfrm_input.c ++++ b/net/xfrm/xfrm_input.c +@@ -162,8 +162,8 @@ int xfrm_parse_spi(struct sk_buff *skb, + if (!pskb_may_pull(skb, hlen)) + return -EINVAL; + +- *spi = *(__be32 *)(skb_transport_header(skb) + offset); +- *seq = *(__be32 *)(skb_transport_header(skb) + offset_seq); ++ *spi = net_hdr_word(skb_transport_header(skb) + offset); ++ *seq = net_hdr_word(skb_transport_header(skb) + offset_seq); + return 0; + } + EXPORT_SYMBOL(xfrm_parse_spi); +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -3953,14 +3953,16 @@ static bool tcp_parse_aligned_timestamp( + { + const __be32 *ptr = (const __be32 *)(th + 1); + +- if (*ptr == htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) +- | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) { ++ if (net_hdr_word(ptr) == ++ htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | ++ (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) { + tp->rx_opt.saw_tstamp = 1; + ++ptr; +- tp->rx_opt.rcv_tsval = ntohl(*ptr); ++ tp->rx_opt.rcv_tsval = get_unaligned_be32(ptr); + ++ptr; +- if (*ptr) +- tp->rx_opt.rcv_tsecr = ntohl(*ptr) - tp->tsoffset; ++ if (net_hdr_word(ptr)) ++ tp->rx_opt.rcv_tsecr = get_unaligned_be32(ptr) - ++ tp->tsoffset; + else + tp->rx_opt.rcv_tsecr = 0; + return true; +--- a/include/uapi/linux/if_pppox.h ++++ b/include/uapi/linux/if_pppox.h +@@ -51,6 +51,7 @@ struct pppoe_addr { + */ + struct pptp_addr { + __u16 call_id; ++ __u16 pad; + struct in_addr sin_addr; + }; + +--- a/net/ipv6/netfilter/nf_log_ipv6.c ++++ b/net/ipv6/netfilter/nf_log_ipv6.c +@@ -63,9 +63,9 @@ static void dump_ipv6_packet(struct net + /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ + nf_log_buf_add(m, "LEN=%zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", + ntohs(ih->payload_len) + sizeof(struct ipv6hdr), +- (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, ++ (ntohl(net_hdr_word(ih)) & 0x0ff00000) >> 20, + ih->hop_limit, +- (ntohl(*(__be32 *)ih) & 0x000fffff)); ++ (ntohl(net_hdr_word(ih)) & 0x000fffff)); + + fragment = 0; + ptr = ip6hoff + sizeof(struct ipv6hdr); +--- a/include/net/neighbour.h ++++ b/include/net/neighbour.h +@@ -274,8 +274,10 @@ static inline bool neigh_key_eq128(const + const u32 *n32 = (const u32 *)n->primary_key; + const u32 *p32 = pkey; + +- return ((n32[0] ^ p32[0]) | (n32[1] ^ p32[1]) | +- (n32[2] ^ p32[2]) | (n32[3] ^ p32[3])) == 0; ++ return ((n32[0] ^ net_hdr_word(&p32[0])) | ++ (n32[1] ^ net_hdr_word(&p32[1])) | ++ (n32[2] ^ net_hdr_word(&p32[2])) | ++ (n32[3] ^ net_hdr_word(&p32[3]))) == 0; + } + + static inline struct neighbour *___neigh_lookup_noref( +--- a/include/uapi/linux/netfilter_arp/arp_tables.h ++++ b/include/uapi/linux/netfilter_arp/arp_tables.h +@@ -70,7 +70,7 @@ struct arpt_arp { + __u8 flags; + /* Inverse flags */ + __u16 invflags; +-}; ++} __attribute__((aligned(4))); + + /* Values for "flag" field in struct arpt_ip (general arp structure). + * No flags defined yet. +--- a/net/core/utils.c ++++ b/net/core/utils.c +@@ -443,8 +443,14 @@ void inet_proto_csum_replace16(__sum16 * + bool pseudohdr) + { + __be32 diff[] = { +- ~from[0], ~from[1], ~from[2], ~from[3], +- to[0], to[1], to[2], to[3], ++ ~net_hdr_word(&from[0]), ++ ~net_hdr_word(&from[1]), ++ ~net_hdr_word(&from[2]), ++ ~net_hdr_word(&from[3]), ++ net_hdr_word(&to[0]), ++ net_hdr_word(&to[1]), ++ net_hdr_word(&to[2]), ++ net_hdr_word(&to[3]), + }; + if (skb->ip_summed != CHECKSUM_PARTIAL) { + *sum = csum_fold(csum_partial(diff, sizeof(diff), +--- a/include/linux/etherdevice.h ++++ b/include/linux/etherdevice.h +@@ -496,7 +496,7 @@ static inline bool is_etherdev_addr(cons + * @b: Pointer to Ethernet header + * + * Compare two Ethernet headers, returns 0 if equal. +- * This assumes that the network header (i.e., IP header) is 4-byte ++ * This assumes that the network header (i.e., IP header) is 2-byte + * aligned OR the platform can handle unaligned access. This is the + * case for all packets coming into netif_receive_skb or similar + * entry points. +@@ -519,11 +519,12 @@ static inline unsigned long compare_ethe + fold |= *(unsigned long *)(a + 6) ^ *(unsigned long *)(b + 6); + return fold; + #else +- u32 *a32 = (u32 *)((u8 *)a + 2); +- u32 *b32 = (u32 *)((u8 *)b + 2); ++ const u16 *a16 = a; ++ const u16 *b16 = b; + +- return (*(u16 *)a ^ *(u16 *)b) | (a32[0] ^ b32[0]) | +- (a32[1] ^ b32[1]) | (a32[2] ^ b32[2]); ++ return (a16[0] ^ b16[0]) | (a16[1] ^ b16[1]) | (a16[2] ^ b16[2]) | ++ (a16[3] ^ b16[3]) | (a16[4] ^ b16[4]) | (a16[5] ^ b16[5]) | ++ (a16[6] ^ b16[6]); + #endif + } + +--- a/net/ipv4/tcp_offload.c ++++ b/net/ipv4/tcp_offload.c +@@ -223,7 +223,7 @@ struct sk_buff *tcp_gro_receive(struct l + + th2 = tcp_hdr(p); + +- if (*(u32 *)&th->source ^ *(u32 *)&th2->source) { ++ if (net_hdr_word(&th->source) ^ net_hdr_word(&th2->source)) { + NAPI_GRO_CB(p)->same_flow = 0; + continue; + } +@@ -241,8 +241,8 @@ found: + ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH)); + flush |= (__force int)(th->ack_seq ^ th2->ack_seq); + for (i = sizeof(*th); i < thlen; i += 4) +- flush |= *(u32 *)((u8 *)th + i) ^ +- *(u32 *)((u8 *)th2 + i); ++ flush |= net_hdr_word((u8 *)th + i) ^ ++ net_hdr_word((u8 *)th2 + i); + + /* When we receive our second frame we can made a decision on if we + * continue this flow as an atomic flow with a fixed ID or if we use +--- a/net/ipv6/netfilter/ip6table_mangle.c ++++ b/net/ipv6/netfilter/ip6table_mangle.c +@@ -47,7 +47,7 @@ ip6t_mangle_out(struct sk_buff *skb, con + hop_limit = ipv6_hdr(skb)->hop_limit; + + /* flowlabel and prio (includes version, which shouldn't change either */ +- flowlabel = *((u_int32_t *)ipv6_hdr(skb)); ++ flowlabel = net_hdr_word(ipv6_hdr(skb)); + + ret = ip6t_do_table(skb, state, state->net->ipv6.ip6table_mangle); + +@@ -56,7 +56,7 @@ ip6t_mangle_out(struct sk_buff *skb, con + !ipv6_addr_equal(&ipv6_hdr(skb)->daddr, &daddr) || + skb->mark != mark || + ipv6_hdr(skb)->hop_limit != hop_limit || +- flowlabel != *((u_int32_t *)ipv6_hdr(skb)))) { ++ flowlabel != net_hdr_word(ipv6_hdr(skb)))) { + err = ip6_route_me_harder(state->net, skb); + if (err < 0) + ret = NF_DROP_ERR(err); diff --git a/target/linux/ath79/tiny/config-default b/target/linux/ath79/tiny/config-default index 9b845abbff..42243cfc48 100644 --- a/target/linux/ath79/tiny/config-default +++ b/target/linux/ath79/tiny/config-default @@ -1,9 +1,12 @@ CONFIG_LEDS_RESET=y CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y +CONFIG_NET_DEVLINK=y CONFIG_NET_DSA=y CONFIG_NET_DSA_MV88E6060=y +# CONFIG_NET_DSA_TAG_QCA is not set CONFIG_NET_DSA_TAG_TRAILER=y CONFIG_NET_SWITCHDEV=y +CONFIG_PHYLINK=y CONFIG_PHY_AR7100_USB=y CONFIG_PHY_AR7200_USB=y CONFIG_REGULATOR=y -- cgit v1.2.3