diff --git a/target/linux/apm821xx/config-4.9 b/target/linux/apm821xx/config-4.9 deleted file mode 100644 index c1c47a7bdd..0000000000 --- a/target/linux/apm821xx/config-4.9 +++ /dev/null @@ -1,326 +0,0 @@ -# CONFIG_40x is not set -CONFIG_44x=y -CONFIG_460EX=y -CONFIG_4xx=y -CONFIG_4xx_SOC=y -# CONFIG_ADVANCED_OPTIONS is not set -CONFIG_APM821xx=y -CONFIG_APOLLO3G=y -# CONFIG_ARCHES is not set -CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y -CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y -CONFIG_ARCH_HAS_DMA_SET_COHERENT_MASK=y -CONFIG_ARCH_HAS_ELF_RANDOMIZE=y -CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y -CONFIG_ARCH_HAS_ILOG2_U32=y -CONFIG_ARCH_HAS_SG_CHAIN=y -CONFIG_ARCH_HAS_WALK_MEMORY=y -CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y -CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_MAY_HAVE_PC_FDC=y -CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y -CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y -CONFIG_ARCH_PHYS_ADDR_T_64BIT=y -# CONFIG_ARCH_RANDOM is not set -CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y -CONFIG_ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT=y -CONFIG_ARCH_SUPPORTS_UPROBES=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARCH_USE_BUILTIN_BSWAP=y -CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y -CONFIG_AUDIT_ARCH=y -# CONFIG_BAMBOO is not set -CONFIG_BCH=y -CONFIG_BLK_MQ_PCI=y -# CONFIG_BLUESTONE is not set -CONFIG_BOOKE=y -CONFIG_BOOKE_WDT=y -# CONFIG_BOUNCE is not set -CONFIG_BUCKMINSTER=y -# CONFIG_CANYONLANDS is not set -CONFIG_CLONE_BACKWARDS=y -CONFIG_CMDLINE="rootfstype=squashfs noinitrd" -CONFIG_CMDLINE_BOOL=y -CONFIG_CONSISTENT_SIZE=0x00200000 -CONFIG_CPU_BIG_ENDIAN=y -CONFIG_CRC16=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_PPC4XX=y -CONFIG_CRYPTO_HASH=y -CONFIG_CRYPTO_HASH2=y -CONFIG_CRYPTO_HW=y -CONFIG_CRYPTO_LZO=y -CONFIG_CRYPTO_MD5_PPC=y -CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_SHA1_PPC=y -CONFIG_CRYPTO_WORKQUEUE=y -CONFIG_DECOMPRESS_GZIP=y -# CONFIG_DEFAULT_UIMAGE is not set -CONFIG_DTC=y -# CONFIG_E200 is not set -CONFIG_EARLY_PRINTK=y -# CONFIG_EBONY is not set -CONFIG_EDAC_ATOMIC_SCRUB=y -CONFIG_EDAC_SUPPORT=y -# CONFIG_EIGER is not set -# CONFIG_EPAPR_BOOT is not set -CONFIG_EXTRA_TARGETS="uImage" -CONFIG_FIXED_PHY=y -CONFIG_FREEZER=y -# CONFIG_FSL_LBC is not set -# CONFIG_FSL_ULI1575 is not set -CONFIG_GENERIC_ATOMIC64=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CMOS_UPDATE=y -CONFIG_GENERIC_CPU_AUTOPROBE=y -# CONFIG_GENERIC_CSUM is not set -CONFIG_GENERIC_IO=y -CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_IRQ_SHOW_LEVEL=y -CONFIG_GENERIC_ISA_DMA=y -CONFIG_GENERIC_MSI_IRQ=y -CONFIG_GENERIC_NVRAM=y -CONFIG_GENERIC_PCI_IOMAP=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_GENERIC_STRNCPY_FROM_USER=y -CONFIG_GENERIC_STRNLEN_USER=y -# CONFIG_GENERIC_TBSYNC is not set -CONFIG_GENERIC_TIME_VSYSCALL_OLD=y -# CONFIG_GEN_RTC is not set -# CONFIG_GE_FPGA is not set -# CONFIG_GLACIER is not set -CONFIG_GPIOLIB=y -CONFIG_GPIO_GENERIC=y -CONFIG_GPIO_GENERIC_PLATFORM=y -CONFIG_GPIO_SYSFS=y -CONFIG_HAS_DMA=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT_MAP=y -# CONFIG_HAS_RAPIDIO is not set -# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set -CONFIG_HAVE_ARCH_AUDITSYSCALL=y -# CONFIG_HAVE_ARCH_BITREVERSE is not set -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_HAVE_ARCH_KGDB=y -CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set -CONFIG_HAVE_CBPF_JIT=y -CONFIG_HAVE_DEBUG_KMEMLEAK=y -CONFIG_HAVE_DEBUG_STACKOVERFLOW=y -CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_DYNAMIC_FTRACE=y -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -CONFIG_HAVE_FUNCTION_TRACER=y -# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set -CONFIG_HAVE_GENERIC_RCU_GUP=y -CONFIG_HAVE_IDE=y -CONFIG_HAVE_IOREMAP_PROT=y -CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y -CONFIG_HAVE_MEMBLOCK=y -CONFIG_HAVE_MEMBLOCK_NODE_MAP=y -CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -CONFIG_HAVE_NET_DSA=y -CONFIG_HAVE_OPROFILE=y -CONFIG_HAVE_PERF_EVENTS=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y -# CONFIG_HAVE_SETUP_PER_CPU_AREA is not set -CONFIG_HAVE_SYSCALL_TRACEPOINTS=y -CONFIG_HAVE_VIRT_CPU_ACCOUNTING=y -CONFIG_HW_RANDOM=y -CONFIG_HW_RANDOM_PPC4XX=y -CONFIG_HZ=1000 -# CONFIG_HZ_100 is not set -CONFIG_HZ_1000=y -CONFIG_HZ_PERIODIC=y -CONFIG_I2C=y -CONFIG_I2C_BOARDINFO=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_IBM_IIC=y -CONFIG_IBM_EMAC=y -CONFIG_IBM_EMAC_EMAC4=y -CONFIG_IBM_EMAC_POLL_WEIGHT=32 -CONFIG_IBM_EMAC_RGMII=y -CONFIG_IBM_EMAC_RXB=128 -CONFIG_IBM_EMAC_RX_COPY_THRESHOLD=256 -CONFIG_IBM_EMAC_RX_SKB_HEADROOM=0 -CONFIG_IBM_EMAC_TAH=y -CONFIG_IBM_EMAC_TXB=128 -# CONFIG_ICON is not set -CONFIG_IKAREM=y -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_IOMMU_HELPER is not set -# CONFIG_IPIC is not set -CONFIG_IRQCHIP=y -CONFIG_IRQ_DOMAIN=y -CONFIG_IRQ_FORCED_THREADING=y -CONFIG_IRQ_WORK=y -CONFIG_ISA_DMA_API=y -# CONFIG_JFFS2_FS is not set -# CONFIG_KATMAI is not set -CONFIG_KERNEL_GZIP=y -CONFIG_KERNEL_START=0xc0000000 -CONFIG_LEDS_TRIGGER_MTD=y -CONFIG_LIBFDT=y -CONFIG_LOWMEM_SIZE=0x30000000 -CONFIG_LZO_COMPRESS=y -CONFIG_LZO_DECOMPRESS=y -# CONFIG_MATH_EMULATION is not set -CONFIG_MDIO_BOARDINFO=y -# CONFIG_MDIO_HISI_FEMAC is not set -# CONFIG_MFD_ACT8945A is not set -# CONFIG_MFD_MAX77620 is not set -# CONFIG_MMIO_NVRAM is not set -CONFIG_MODULES_USE_ELF_RELA=y -# CONFIG_MPIC is not set -# CONFIG_MPIC_U3_HT_IRQS is not set -# CONFIG_MPIC_WEIRD is not set -CONFIG_MTD_CFI_ADV_OPTIONS=y -# CONFIG_MTD_CFI_GEOMETRY is not set -CONFIG_MTD_NAND=y -CONFIG_MTD_NAND_BCH=y -CONFIG_MTD_NAND_ECC=y -CONFIG_MTD_NAND_ECC_BCH=y -CONFIG_MTD_NAND_ECC_SMC=y -CONFIG_MTD_NAND_NDFC=y -# CONFIG_MTD_SPLIT is not set -# CONFIG_MTD_SPLIT_SQUASHFS_ROOT is not set -CONFIG_NEED_DMA_MAP_STATE=y -# CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK is not set -CONFIG_NEED_PER_CPU_KM=y -CONFIG_NEED_SG_DMA_LENGTH=y -# CONFIG_NONSTATIC_KERNEL is not set -CONFIG_NOT_COHERENT_CACHE=y -CONFIG_NO_BOOTMEM=y -CONFIG_NR_IRQS=512 -CONFIG_OF=y -CONFIG_OF_ADDRESS=y -CONFIG_OF_ADDRESS_PCI=y -CONFIG_OF_EARLY_FLATTREE=y -CONFIG_OF_FLATTREE=y -CONFIG_OF_GPIO=y -CONFIG_OF_IRQ=y -CONFIG_OF_MDIO=y -CONFIG_OF_NET=y -CONFIG_OF_PCI=y -CONFIG_OF_PCI_IRQ=y -CONFIG_OF_RESERVED_MEM=y -CONFIG_OLD_SIGACTION=y -CONFIG_OLD_SIGSUSPEND=y -CONFIG_PAGE_OFFSET=0xc0000000 -CONFIG_PCI=y -CONFIG_PCIEAER=y -CONFIG_PCIEPORTBUS=y -CONFIG_PCIE_PME=y -CONFIG_PCI_BUS_ADDR_T_64BIT=y -CONFIG_PCI_DISABLE_COMMON_QUIRKS=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCI_MSI=y -# CONFIG_PCI_MSI_IRQ_DOMAIN is not set -CONFIG_PGTABLE_LEVELS=2 -CONFIG_PHYLIB=y -CONFIG_PHYSICAL_START=0x00000000 -CONFIG_PHYS_64BIT=y -CONFIG_PHYS_ADDR_T_64BIT=y -CONFIG_PM=y -CONFIG_PM_AUTOSLEEP=y -# CONFIG_PM_DEBUG is not set -CONFIG_PM_SLEEP=y -CONFIG_PM_WAKELOCKS=y -CONFIG_PM_WAKELOCKS_GC=y -CONFIG_PM_WAKELOCKS_LIMIT=100 -CONFIG_PPC=y -CONFIG_PPC32=y -CONFIG_PPC44x_SIMPLE=y -CONFIG_PPC4xx_CPM=y -CONFIG_PPC4xx_GPIO=y -# CONFIG_PPC4xx_HSTA_MSI is not set -CONFIG_PPC4xx_MSI=y -CONFIG_PPC4xx_OCM=y -CONFIG_PPC4xx_PCI_EXPRESS=y -# CONFIG_PPC64 is not set -# CONFIG_PPC_47x is not set -# CONFIG_PPC_85xx is not set -# CONFIG_PPC_8xx is not set -# CONFIG_PPC_970_NAP is not set -CONFIG_PPC_ADV_DEBUG_DACS=2 -CONFIG_PPC_ADV_DEBUG_DAC_RANGE=y -CONFIG_PPC_ADV_DEBUG_DVCS=2 -CONFIG_PPC_ADV_DEBUG_IACS=4 -CONFIG_PPC_ADV_DEBUG_REGS=y -# CONFIG_PPC_BOOK3S_32 is not set -# CONFIG_PPC_CELL is not set -# CONFIG_PPC_CELL_NATIVE is not set -# CONFIG_PPC_COPRO_BASE is not set -CONFIG_PPC_DCR=y -# CONFIG_PPC_DCR_MMIO is not set -CONFIG_PPC_DCR_NATIVE=y -# CONFIG_PPC_DOORBELL is not set -# CONFIG_PPC_EARLY_DEBUG is not set -# CONFIG_PPC_EPAPR_HV_PIC is not set -CONFIG_PPC_FPU=y -# CONFIG_PPC_I8259 is not set -# CONFIG_PPC_ICP_HV is not set -# CONFIG_PPC_ICP_NATIVE is not set -# CONFIG_PPC_ICS_RTAS is not set -CONFIG_PPC_INDIRECT_PCI=y -CONFIG_PPC_LIB_RHEAP=y -CONFIG_PPC_MMU_NOHASH=y -# CONFIG_PPC_MM_SLICES is not set -# CONFIG_PPC_MPC106 is not set -CONFIG_PPC_MSI_BITMAP=y -# CONFIG_PPC_P7_NAP is not set -CONFIG_PPC_PCI_CHOICE=y -# CONFIG_PPC_RTAS is not set -CONFIG_PPC_UDBG_16550=y -CONFIG_PPC_WERROR=y -# CONFIG_PPC_XICS is not set -# CONFIG_PQ2ADS is not set -CONFIG_PTE_64BIT=y -# CONFIG_QORIQ_THERMAL is not set -# CONFIG_RAINIER is not set -CONFIG_RAS=y -# CONFIG_RCU_STALL_COMMON is not set -CONFIG_RD_GZIP=y -# CONFIG_RELOCATABLE is not set -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -# CONFIG_SAM440EP is not set -# CONFIG_SCHED_INFO is not set -# CONFIG_SCSI_DMA is not set -# CONFIG_SENSORS_TC654 is not set -# CONFIG_SEQUOIA is not set -CONFIG_SERIAL_8250_EXTENDED=y -CONFIG_SERIAL_8250_FSL=y -CONFIG_SERIAL_8250_SHARE_IRQ=y -CONFIG_SERIAL_OF_PLATFORM=y -CONFIG_SIMPLE_GPIO=y -CONFIG_SPARSE_IRQ=y -CONFIG_SRCU=y -CONFIG_SUSPEND=y -CONFIG_SUSPEND_FREEZER=y -# CONFIG_SWIOTLB is not set -CONFIG_SWPHY=y -CONFIG_SYSCTL_EXCEPTION_TRACE=y -# CONFIG_TAISHAN is not set -CONFIG_TASK_SIZE=0xc0000000 -CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_USB_SUPPORT=y -CONFIG_VDSO32=y -# CONFIG_VIRT_CPU_ACCOUNTING_NATIVE is not set -# CONFIG_WARP is not set -CONFIG_WATCHDOG_CORE=y -CONFIG_WNDR4700=y -# CONFIG_XILINX_SYSACE is not set -# CONFIG_XILINX_VIRTEX440_GENERIC_BOARD is not set -CONFIG_XZ_DEC_BCJ=y -CONFIG_XZ_DEC_POWERPC=y -# CONFIG_YOSEMITE is not set -CONFIG_ZLIB_DEFLATE=y -CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/apm821xx/patches-4.9/200-add-meraki-mr24-ikarem-support.patch b/target/linux/apm821xx/patches-4.9/200-add-meraki-mr24-ikarem-support.patch deleted file mode 100644 index 018bcf21a0..0000000000 --- a/target/linux/apm821xx/patches-4.9/200-add-meraki-mr24-ikarem-support.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -40,6 +40,19 @@ config EBONY - help - This option enables support for the IBM PPC440GP evaluation board. - -+config IKAREM -+ bool "Ikarem" -+ depends on 44x -+ default n -+ select PPC44x_SIMPLE -+ select APM821xx -+ select PCI_MSI -+ select PPC4xx_MSI -+ select PPC4xx_PCI_EXPRESS -+ select IBM_EMAC_RGMII -+ help -+ This option enables support for the Cisco Meraki MR24 (Ikarem) Access Point. -+ - config SAM440EP - bool "Sam440ep" - depends on 44x ---- a/arch/powerpc/platforms/44x/ppc44x_simple.c -+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c -@@ -62,6 +62,7 @@ static char *board[] __initdata = { - "amcc,sequoia", - "amcc,taishan", - "amcc,yosemite", -+ "meraki,ikarem", - "mosaixtech,icon" - }; - diff --git a/target/linux/apm821xx/patches-4.9/201-add-amcc-apollo3g-support.patch b/target/linux/apm821xx/patches-4.9/201-add-amcc-apollo3g-support.patch deleted file mode 100644 index 0581a43e33..0000000000 --- a/target/linux/apm821xx/patches-4.9/201-add-amcc-apollo3g-support.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -143,6 +143,17 @@ config CANYONLANDS - help - This option enables support for the AMCC PPC460EX evaluation board. - -+config APOLLO3G -+ bool "Apollo3G" -+ depends on 44x -+ default n -+ select PPC44x_SIMPLE -+ select APM821xx -+ select IBM_EMAC_RGMII -+ select 460EX -+ help -+ This option enables support for the AMCC Apollo 3G board. -+ - config GLACIER - bool "Glacier" - depends on 44x ---- a/arch/powerpc/platforms/44x/ppc44x_simple.c -+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c -@@ -50,6 +50,7 @@ machine_device_initcall(ppc44x_simple, p - * board.c file for it rather than adding it to this list. - */ - static char *board[] __initdata = { -+ "amcc,apollo3g", - "amcc,arches", - "amcc,bamboo", - "apm,bluestone", diff --git a/target/linux/apm821xx/patches-4.9/202-add-netgear-wndr4700-support.patch b/target/linux/apm821xx/patches-4.9/202-add-netgear-wndr4700-support.patch deleted file mode 100644 index 6b6db1dddb..0000000000 --- a/target/linux/apm821xx/patches-4.9/202-add-netgear-wndr4700-support.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Makefile -+++ b/arch/powerpc/platforms/44x/Makefile -@@ -3,6 +3,7 @@ ifneq ($(CONFIG_PPC4xx_CPM),y) - obj-$(CONFIG_44x) += idle.o - endif - obj-$(CONFIG_PPC44x_SIMPLE) += ppc44x_simple.o -+obj-$(CONFIG_WNDR4700) += wndr4700.o - obj-$(CONFIG_EBONY) += ebony.o - obj-$(CONFIG_SAM440EP) += sam440ep.o - obj-$(CONFIG_WARP) += warp.o ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -260,6 +260,19 @@ config ICON - help - This option enables support for the AMCC PPC440SPe evaluation board. - -+config WNDR4700 -+ bool "WNDR4700" -+ depends on 44x -+ default n -+ select APM821xx -+ select PCI_MSI -+ select PPC4xx_MSI -+ select PPC4xx_PCI_EXPRESS -+ select IBM_EMAC_RGMII -+ select 460EX -+ help -+ This option enables support for the Netgear WNDR4700/WNDR4720 board. -+ - config XILINX_VIRTEX440_GENERIC_BOARD - bool "Generic Xilinx Virtex 5 FXT board support" - depends on 44x diff --git a/target/linux/apm821xx/patches-4.9/203-add-meraki-mx60-buckminster-support.patch b/target/linux/apm821xx/patches-4.9/203-add-meraki-mx60-buckminster-support.patch deleted file mode 100644 index 8c072387d4..0000000000 --- a/target/linux/apm821xx/patches-4.9/203-add-meraki-mx60-buckminster-support.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -30,6 +30,19 @@ config BLUESTONE - help - This option enables support for the APM APM821xx Evaluation board. - -+config BUCKMINSTER -+ bool "Buckminster" -+ depends on 44x -+ default n -+ select APM821xx -+ select PCI_MSI -+ select PPC4xx_MSI -+ select PPC4xx_PCI_EXPRESS -+ select IBM_EMAC_RGMII -+ select 460EX -+ help -+ This option enables support for the Cisco Meraki MX60/MX60W (Buckminster) Security Appliance -+ - config EBONY - bool "Ebony" - depends on 44x ---- a/arch/powerpc/platforms/44x/ppc44x_simple.c -+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c -@@ -63,6 +63,7 @@ static char *board[] __initdata = { - "amcc,sequoia", - "amcc,taishan", - "amcc,yosemite", -+ "meraki,buckminster", - "meraki,ikarem", - "mosaixtech,icon" - }; diff --git a/target/linux/apm821xx/patches-4.9/300-fix-atheros-nics-on-apm82181.patch b/target/linux/apm821xx/patches-4.9/300-fix-atheros-nics-on-apm82181.patch deleted file mode 100644 index 0b1affcfe7..0000000000 --- a/target/linux/apm821xx/patches-4.9/300-fix-atheros-nics-on-apm82181.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- a/arch/powerpc/sysdev/ppc4xx_pci.c -+++ b/arch/powerpc/sysdev/ppc4xx_pci.c -@@ -1066,15 +1066,24 @@ static int __init apm821xx_pciex_init_po - u32 val; - - /* -- * Do a software reset on PCIe ports. -- * This code is to fix the issue that pci drivers doesn't re-assign -- * bus number for PCIE devices after Uboot -- * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 -- * PT quad port, SAS LSI 1064E) -+ * Only reset the PHY when no link is currently established. -+ * This is for the Atheros PCIe board which has problems to establish -+ * the link (again) after this PHY reset. All other currently tested -+ * PCIe boards don't show this problem. - */ -- -- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); -- mdelay(10); -+ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); -+ if (!(val & 0x00001000)) { -+ /* -+ * Do a software reset on PCIe ports. -+ * This code is to fix the issue that pci drivers doesn't re-assign -+ * bus number for PCIE devices after Uboot -+ * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 -+ * PT quad port, SAS LSI 1064E) -+ */ -+ -+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); -+ mdelay(10); -+ } - - if (port->endpoint) - val = PTYPE_LEGACY_ENDPOINT << 20; -@@ -1091,9 +1100,12 @@ static int __init apm821xx_pciex_init_po - mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); - mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); - -- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); -- mdelay(50); -- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); -+ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); -+ if (!(val & 0x00001000)) { -+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); -+ mdelay(50); -+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); -+ } - - mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, - mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | diff --git a/target/linux/apm821xx/patches-4.9/301-fix-memory-map-wndr4700.patch b/target/linux/apm821xx/patches-4.9/301-fix-memory-map-wndr4700.patch deleted file mode 100644 index d6a1006177..0000000000 --- a/target/linux/apm821xx/patches-4.9/301-fix-memory-map-wndr4700.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/arch/powerpc/sysdev/ppc4xx_pci.c -+++ b/arch/powerpc/sysdev/ppc4xx_pci.c -@@ -1913,9 +1913,9 @@ static void __init ppc4xx_configure_pcie - * if it works - */ - out_le32(mbase + PECFG_PIM0LAL, 0x00000000); -- out_le32(mbase + PECFG_PIM0LAH, 0x00000000); -+ out_le32(mbase + PECFG_PIM0LAH, 0x00000008); - out_le32(mbase + PECFG_PIM1LAL, 0x00000000); -- out_le32(mbase + PECFG_PIM1LAH, 0x00000000); -+ out_le32(mbase + PECFG_PIM1LAH, 0x0000000c); - out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); - out_le32(mbase + PECFG_PIM01SAL, 0x00000000); - diff --git a/target/linux/apm821xx/patches-4.9/701-powerpc_ibm_apm82181_phyclk_fix.patch b/target/linux/apm821xx/patches-4.9/701-powerpc_ibm_apm82181_phyclk_fix.patch deleted file mode 100644 index 457c5e3780..0000000000 --- a/target/linux/apm821xx/patches-4.9/701-powerpc_ibm_apm82181_phyclk_fix.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- a/drivers/net/ethernet/ibm/emac/core.c -+++ b/drivers/net/ethernet/ibm/emac/core.c -@@ -129,6 +129,7 @@ static inline void emac_report_timeout_e - { - if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX | - EMAC_FTR_460EX_PHY_CLK_FIX | -+ EMAC_FTR_APM821XX_PHY_CLK_FIX | - EMAC_FTR_440EP_PHY_CLK_FIX)) - DBG(dev, "%s" NL, error); - else if (net_ratelimit()) -@@ -146,6 +147,10 @@ static inline void emac_rx_clk_tx(struct - if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX)) - dcri_clrset(SDR0, SDR0_MFR, - 0, SDR0_MFR_ECS >> dev->cell_index); -+ -+ if (emac_has_feature(dev, EMAC_FTR_APM821XX_PHY_CLK_FIX)) -+ dcri_clrset(SDR0, SDR0_ETH_CFG, -+ 0, 0x00000100 >> dev->cell_index); - #endif - } - -@@ -155,6 +160,10 @@ static inline void emac_rx_clk_default(s - if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX)) - dcri_clrset(SDR0, SDR0_MFR, - SDR0_MFR_ECS >> dev->cell_index, 0); -+ -+ if (emac_has_feature(dev, EMAC_FTR_APM821XX_PHY_CLK_FIX)) -+ dcri_clrset(SDR0, SDR0_ETH_CFG, -+ 0x00000100 >> dev->cell_index, 0); - #endif - } - -@@ -2617,6 +2626,7 @@ static int emac_init_config(struct emac_ - if (of_device_is_compatible(np, "ibm,emac-apm821xx")) { - dev->features |= (EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE | - EMAC_FTR_APM821XX_NO_HALF_DUPLEX | -+ EMAC_FTR_APM821XX_PHY_CLK_FIX | - EMAC_FTR_460EX_PHY_CLK_FIX); - } - } else if (of_device_is_compatible(np, "ibm,emac4")) { ---- a/drivers/net/ethernet/ibm/emac/core.h -+++ b/drivers/net/ethernet/ibm/emac/core.h -@@ -333,6 +333,8 @@ struct emac_instance { - */ - #define EMAC_FTR_APM821XX_NO_HALF_DUPLEX 0x00001000 - -+#define EMAC_FTR_APM821XX_PHY_CLK_FIX 0x000002000 -+ - /* Right now, we don't quite handle the always/possible masks on the - * most optimal way as we don't have a way to say something like - * always EMAC4. Patches welcome. diff --git a/target/linux/apm821xx/patches-4.9/702-powerpc_ibm_phy_add_dt_parser.patch b/target/linux/apm821xx/patches-4.9/702-powerpc_ibm_phy_add_dt_parser.patch deleted file mode 100644 index 4e53fdfa21..0000000000 --- a/target/linux/apm821xx/patches-4.9/702-powerpc_ibm_phy_add_dt_parser.patch +++ /dev/null @@ -1,328 +0,0 @@ -From b1c54da602ae9215cfbde1c3ed3b6296b76f07fc Mon Sep 17 00:00:00 2001 -Message-Id: -In-Reply-To: <246bd6614529d28dc48b11981ab5dae7a7364fc2.1486337989.git.chunkeey@googlemail.com> -References: <246bd6614529d28dc48b11981ab5dae7a7364fc2.1486337989.git.chunkeey@googlemail.com> -From: Christian Lamparter -Date: Mon, 13 Jun 2016 15:42:21 +0200 -Subject: [RFC 2/2] net: emac: add support for device-tree based PHY discovery - and setup -To: netdev@vger.kernel.org, - devicetree@vger.kernel.org -Cc: David S. Miller , - Ivan Mikhaylov , - Mark Rutland , - Rob Herring - -This patch adds glue-code that allows the EMAC driver to interface -with the existing dt-supported PHYs in drivers/net/phy. - -Because currently, the emac driver maintains a small library of -supported phys for in a private phy.c file located in the drivers -directory. - -The support is limited to mostly single ethernet transceiver like the: -CIS8201, BCM5248, ET1011C, Marvell 88E1111 and 88E1112, AR8035. -However, routers like the Netgear WNDR4700 and Cisco Meraki MX60(W) -have a 5-port switch (QCA8327N) attached to the MDIO of the EMAC. -The switch chip has already a proper phy-driver (ar8216) that uses -the generic phy library. - -Signed-off-by: Christian Lamparter ---- ---- a/drivers/net/ethernet/ibm/emac/core.c -+++ b/drivers/net/ethernet/ibm/emac/core.c -@@ -42,6 +42,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -2422,6 +2423,229 @@ static int emac_read_uint_prop(struct de - return 0; - } - -+static void emac_adjust_link(struct net_device *ndev) -+{ -+ struct emac_instance *dev = netdev_priv(ndev); -+ struct phy_device *phy = dev->phy_dev; -+ -+ dev->phy.autoneg = phy->autoneg; -+ dev->phy.speed = phy->speed; -+ dev->phy.duplex = phy->duplex; -+ dev->phy.pause = phy->pause; -+ dev->phy.asym_pause = phy->asym_pause; -+ dev->phy.advertising = phy->advertising; -+} -+ -+static int emac_mii_bus_read(struct mii_bus *bus, int addr, int regnum) -+{ -+ return emac_mdio_read(bus->priv, addr, regnum); -+} -+ -+static int emac_mii_bus_write(struct mii_bus *bus, int addr, int regnum, u16 val) -+{ -+ emac_mdio_write(bus->priv, addr, regnum, val); -+ return 0; -+} -+ -+static int emac_mii_bus_reset(struct mii_bus *bus) -+{ -+ struct emac_instance *dev = netdev_priv(bus->priv); -+ int err; -+ -+ err = emac_reset(dev); -+ if (err) -+ return err; -+ /* Meraki MX60(W)'s uboot will disable the switch and -+ * a bus reset won't do anything. */ -+ emac_mii_reset_phy(&dev->phy); -+ return 0; -+} -+ -+static int emac_mdio_setup_aneg(struct mii_phy *phy, u32 advertise) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ -+ dev->phy.autoneg = AUTONEG_ENABLE; -+ dev->phy.speed = SPEED_1000; -+ dev->phy.duplex = DUPLEX_FULL; -+ dev->phy.advertising = advertise; -+ phy->autoneg = AUTONEG_ENABLE; -+ phy->speed = dev->phy.speed; -+ phy->duplex = dev->phy.duplex; -+ phy->advertising = advertise; -+ return phy_start_aneg(dev->phy_dev); -+} -+ -+static int emac_mdio_setup_forced(struct mii_phy *phy, int speed, int fd) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ -+ dev->phy.autoneg = AUTONEG_DISABLE; -+ dev->phy.speed = speed; -+ dev->phy.duplex = fd; -+ phy->autoneg = AUTONEG_DISABLE; -+ phy->speed = speed; -+ phy->duplex = fd; -+ return phy_start_aneg(dev->phy_dev); -+} -+ -+static int emac_mdio_poll_link(struct mii_phy *phy) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ int res; -+ -+ res = phy_read_status(dev->phy_dev); -+ if (res) { -+ dev_err(&dev->ofdev->dev, "link update failed (%d).", res); -+ return ethtool_op_get_link(ndev); -+ } -+ -+ return dev->phy_dev->link; -+} -+ -+static int emac_mdio_read_link(struct mii_phy *phy) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ int res; -+ -+ res = phy_read_status(dev->phy_dev); -+ if (res) -+ return res; -+ -+ dev->phy.speed = phy->speed; -+ dev->phy.duplex = phy->duplex; -+ dev->phy.pause = phy->pause; -+ dev->phy.asym_pause = phy->asym_pause; -+ return 0; -+} -+ -+static int emac_mdio_init_phy(struct mii_phy *phy) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ -+ phy_start(dev->phy_dev); -+ dev->phy.autoneg = phy->autoneg; -+ dev->phy.speed = phy->speed; -+ dev->phy.duplex = phy->duplex; -+ dev->phy.advertising = phy->advertising; -+ dev->phy.pause = phy->pause; -+ dev->phy.asym_pause = phy->asym_pause; -+ -+ return phy_init_hw(dev->phy_dev); -+} -+ -+static const struct mii_phy_ops emac_dt_mdio_phy_ops = { -+ .init = emac_mdio_init_phy, -+ .setup_aneg = emac_mdio_setup_aneg, -+ .setup_forced = emac_mdio_setup_forced, -+ .poll_link = emac_mdio_poll_link, -+ .read_link = emac_mdio_read_link, -+}; -+ -+static int emac_dt_mdio_probe(struct emac_instance *dev) -+{ -+ struct device_node *mii_np; -+ int res; -+ -+ mii_np = of_get_child_by_name(dev->ofdev->dev.of_node, "mdio"); -+ if (!mii_np) { -+ dev_err(&dev->ofdev->dev, "no mdio definition found."); -+ return -ENODEV; -+ } -+ -+ if (!of_device_is_available(mii_np)) { -+ res = 1; -+ goto put_node; -+ } -+ -+ dev->mii_bus = devm_mdiobus_alloc(&dev->ofdev->dev); -+ if (!dev->mii_bus) { -+ res = -ENOMEM; -+ goto put_node; -+ } -+ -+ dev->mii_bus->priv = dev->ndev; -+ dev->mii_bus->parent = dev->ndev->dev.parent; -+ dev->mii_bus->name = "emac_mdio"; -+ dev->mii_bus->read = &emac_mii_bus_read; -+ dev->mii_bus->write = &emac_mii_bus_write; -+ dev->mii_bus->reset = &emac_mii_bus_reset; -+ snprintf(dev->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev->ofdev->name); -+ res = of_mdiobus_register(dev->mii_bus, mii_np); -+ if (res) { -+ dev_err(&dev->ofdev->dev, "cannot register MDIO bus %s (%d)", -+ dev->mii_bus->name, res); -+ } -+ -+ put_node: -+ of_node_put(mii_np); -+ return res; -+} -+ -+static int emac_dt_phy_probe(struct emac_instance *dev, -+ struct device_node *phy_handle) -+{ -+ u32 phy_flags = 0; -+ int res; -+ -+ res = of_property_read_u32(phy_handle, "phy-flags", &phy_flags); -+ if (res < 0 && res != -EINVAL) -+ return res; -+ -+ dev->phy.def = devm_kzalloc(&dev->ofdev->dev, sizeof(*dev->phy.def), -+ GFP_KERNEL); -+ if (!dev->phy.def) -+ return -ENOMEM; -+ -+ dev->phy_dev = of_phy_connect(dev->ndev, phy_handle, -+ &emac_adjust_link, phy_flags, -+ PHY_INTERFACE_MODE_RGMII); -+ if (!dev->phy_dev) { -+ dev_err(&dev->ofdev->dev, "failed to connect to PHY.\n"); -+ return -ENODEV; -+ } -+ -+ dev->phy.def->phy_id = dev->phy_dev->drv->phy_id; -+ dev->phy.def->phy_id_mask = dev->phy_dev->drv->phy_id_mask; -+ dev->phy.def->name = dev->phy_dev->drv->name; -+ dev->phy.def->ops = &emac_dt_mdio_phy_ops; -+ dev->phy.features = dev->phy_dev->supported; -+ dev->phy.address = dev->phy_dev->mdio.addr; -+ dev->phy.mode = dev->phy_dev->interface; -+ return 0; -+} -+ -+static int emac_probe_dt_phy(struct emac_instance *dev) -+{ -+ struct device_node *np = dev->ofdev->dev.of_node; -+ struct device_node *phy_handle; -+ int res = 0; -+ -+ phy_handle = of_parse_phandle(np, "phy-handle", 0); -+ -+ if (phy_handle) { -+ res = emac_dt_mdio_probe(dev); -+ if (!res) { -+ res = emac_dt_phy_probe(dev, phy_handle); -+ if (!res) -+ res = 1; -+ else -+ mdiobus_unregister(dev->mii_bus); -+ } -+ } -+ -+ of_node_put(phy_handle); -+ /* if no phy device was specified in the device tree, then we fallback -+ * to the old emac_phy.c probe code for compatibility reasons. -+ */ -+ return res; -+} -+ - static int emac_init_phy(struct emac_instance *dev) - { - struct device_node *np = dev->ofdev->dev.of_node; -@@ -2492,6 +2716,22 @@ static int emac_init_phy(struct emac_ins - - emac_configure(dev); - -+ if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) { -+ int res = emac_probe_dt_phy(dev); -+ -+ if (res == 1) { -+ mutex_unlock(&emac_phy_map_lock); -+ goto init_phy; -+ } else if (res < 0) { -+ mutex_unlock(&emac_phy_map_lock); -+ dev_err(&dev->ofdev->dev, "failed to attach dt phy (%d).\n", -+ res); -+ return res; -+ } -+ -+ /* continue with old code */ -+ } -+ - if (dev->phy_address != 0xffffffff) - phy_map = ~(1 << dev->phy_address); - -@@ -2519,6 +2759,7 @@ static int emac_init_phy(struct emac_ins - return -ENXIO; - } - -+ init_phy: - /* Init PHY */ - if (dev->phy.def->ops->init) - dev->phy.def->ops->init(&dev->phy); -@@ -2988,6 +3229,12 @@ static int emac_remove(struct platform_d - if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII)) - zmii_detach(dev->zmii_dev, dev->zmii_port); - -+ if (dev->phy_dev) -+ phy_disconnect(dev->phy_dev); -+ -+ if (dev->mii_bus) -+ mdiobus_unregister(dev->mii_bus); -+ - busy_phy_map &= ~(1 << dev->phy.address); - DBG(dev, "busy_phy_map now %#x" NL, busy_phy_map); - ---- a/drivers/net/ethernet/ibm/emac/core.h -+++ b/drivers/net/ethernet/ibm/emac/core.h -@@ -199,6 +199,10 @@ struct emac_instance { - struct emac_instance *mdio_instance; - struct mutex mdio_lock; - -+ /* Device-tree based phy configuration */ -+ struct mii_bus *mii_bus; -+ struct phy_device *phy_dev; -+ - /* ZMII infos if any */ - u32 zmii_ph; - u32 zmii_port; diff --git a/target/linux/apm821xx/patches-4.9/703-net-emac-fix-reset-timeout-with-AR8035-phy.patch b/target/linux/apm821xx/patches-4.9/703-net-emac-fix-reset-timeout-with-AR8035-phy.patch deleted file mode 100644 index 8acde84ec0..0000000000 --- a/target/linux/apm821xx/patches-4.9/703-net-emac-fix-reset-timeout-with-AR8035-phy.patch +++ /dev/null @@ -1,112 +0,0 @@ -From b2e79053e7456a961249c8865214a1e95b49c863 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter -Date: Sat, 3 Jun 2017 18:16:19 +0200 -Subject: [PATCH] net: emac: fix reset timeout with AR8035 phy - -This patch fixes a problem where the AR8035 PHY can't be -detected on an Cisco Meraki MR24, if the ethernet cable is -not connected on boot. - -Russell Senior provided steps to reproduce the issue: -|Disconnect ethernet cable, apply power, wait until device has booted, -|plug in ethernet, check for interfaces, no eth0 is listed. -| -|This appears to be a problem during probing of the AR8035 Phy chip. -|When ethernet has no link, the phy detection fails, and eth0 is not -|created. Plugging ethernet later has no effect, because there is no -|interface as far as the kernel is concerned. The relevant part of -|the boot log looks like this: -|this is the failing case: -| -|[ 0.876611] /plb/opb/emac-rgmii@ef601500: input 0 in RGMII mode -|[ 0.882532] /plb/opb/ethernet@ef600c00: reset timeout -|[ 0.888546] /plb/opb/ethernet@ef600c00: can't find PHY! -|and the succeeding case: -| -|[ 0.876672] /plb/opb/emac-rgmii@ef601500: input 0 in RGMII mode -|[ 0.883952] eth0: EMAC-0 /plb/opb/ethernet@ef600c00, MAC 00:01:.. -|[ 0.890822] eth0: found Atheros 8035 Gigabit Ethernet PHY (0x01) - -Based on the comment and the commit message of -commit 23fbb5a87c56 ("emac: Fix EMAC soft reset on 460EX/GT"). -This is because the AR8035 PHY doesn't provide the TX Clock, -if the ethernet cable is not attached. This causes the reset -to timeout and the PHY detection code in emac_init_phy() is -unable to detect the AR8035 PHY. As a result, the emac driver -bails out early and the user left with no ethernet. - -In order to stay compatible with existing configurations, the driver -tries the current reset approach at first. Only if the first attempt -timed out, it does perform one more retry with the clock input -temporarily switched to the internal clock source for just the -duration of the reset. - -LEDE-Bug: #687 - -Cc: Chris Blake -Reported-by: Russell Senior -Fixes: 23fbb5a87c56e98 ("emac: Fix EMAC soft reset on 460EX/GT") -Reviewed-by: Andrew Lunn -Signed-off-by: Christian Lamparter ---- - drivers/net/ethernet/ibm/emac/core.c | 26 ++++++++++++++++++++++---- - 1 file changed, 22 insertions(+), 4 deletions(-) - ---- a/drivers/net/ethernet/ibm/emac/core.c -+++ b/drivers/net/ethernet/ibm/emac/core.c -@@ -352,6 +352,7 @@ static int emac_reset(struct emac_instan - { - struct emac_regs __iomem *p = dev->emacp; - int n = 20; -+ bool __maybe_unused try_internal_clock = false; - - DBG(dev, "reset" NL); - -@@ -364,6 +365,7 @@ static int emac_reset(struct emac_instan - } - - #ifdef CONFIG_PPC_DCR_NATIVE -+do_retry: - /* - * PPC460EX/GT Embedded Processor Advanced User's Manual - * section 28.10.1 Mode Register 0 (EMACx_MR0) states: -@@ -371,10 +373,19 @@ static int emac_reset(struct emac_instan - * of the EMAC. If none is present, select the internal clock - * (SDR0_ETH_CFG[EMACx_PHY_CLK] = 1). - * After a soft reset, select the external clock. -+ * -+ * The AR8035-A PHY Meraki MR24 does not provide a TX Clk if the -+ * ethernet cable is not attached. This causes the reset to timeout -+ * and the PHY detection code in emac_init_phy() is unable to -+ * communicate and detect the AR8035-A PHY. As a result, the emac -+ * driver bails out early and the user has no ethernet. -+ * In order to stay compatible with existing configurations, the -+ * driver will temporarily switch to the internal clock, after -+ * the first reset fails. - */ - if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) { -- if (dev->phy_address == 0xffffffff && -- dev->phy_map == 0xffffffff) { -+ if (try_internal_clock || (dev->phy_address == 0xffffffff && -+ dev->phy_map == 0xffffffff)) { - /* No PHY: select internal loop clock before reset */ - dcri_clrset(SDR0, SDR0_ETH_CFG, - 0, SDR0_ETH_CFG_ECS << dev->cell_index); -@@ -392,8 +403,15 @@ static int emac_reset(struct emac_instan - - #ifdef CONFIG_PPC_DCR_NATIVE - if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) { -- if (dev->phy_address == 0xffffffff && -- dev->phy_map == 0xffffffff) { -+ if (!n && !try_internal_clock) { -+ /* first attempt has timed out. */ -+ n = 20; -+ try_internal_clock = true; -+ goto do_retry; -+ } -+ -+ if (try_internal_clock || (dev->phy_address == 0xffffffff && -+ dev->phy_map == 0xffffffff)) { - /* No PHY: restore external clock source after reset */ - dcri_clrset(SDR0, SDR0_ETH_CFG, - SDR0_ETH_CFG_ECS << dev->cell_index, 0); diff --git a/target/linux/apm821xx/patches-4.9/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch b/target/linux/apm821xx/patches-4.9/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch deleted file mode 100644 index b30511d756..0000000000 --- a/target/linux/apm821xx/patches-4.9/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch +++ /dev/null @@ -1,545 +0,0 @@ -From 419992bae5aaa4e06402e0b7c79fcf7bcb6b4764 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter -Date: Thu, 2 Jun 2016 00:48:46 +0200 -Subject: [PATCH] usb: xhci: add firmware loader for uPD720201 and uPD720202 - w/o ROM - -This patch adds a firmware loader for the uPD720201K8-711-BAC-A -and uPD720202K8-711-BAA-A variant. Both of these chips are listed -in Renesas' R19UH0078EJ0500 Rev.5.00 "User's Manual: Hardware" as -devices which need the firmware loader on page 2 in order to -work as they "do not support the External ROM". - -The "Firmware Download Sequence" is describe in chapter -"7.1 FW Download Interface" R19UH0078EJ0500 Rev.5.00 page 131. - -The firmware "K2013080.mem" is available from a USB3.0 Host to -PCIe Adapter (PP2U-E card) "Firmware download" archive. An -alternative version can be sourced from Netgear's WNDR4700 GPL -archives. - -The release notes of the PP2U-E's "Firmware Download" ver 2.0.1.3 -(2012-06-15) state that the firmware is for the following devices: - - uPD720201 ES 2.0 sample whose revision ID is 2. - - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. - - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. - -If someone from Renesas is listening: It would be great, if these -firmwares could be added to linux-firmware.git. - -Cc: Yoshihiro Shimoda -Signed-off-by: Christian Lamparter ---- - drivers/usb/host/xhci-pci.c | 492 ++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 492 insertions(+) - ---- a/drivers/usb/host/xhci-pci.c -+++ b/drivers/usb/host/xhci-pci.c -@@ -24,6 +24,8 @@ - #include - #include - #include -+#include -+#include - - #include "xhci.h" - #include "xhci-trace.h" -@@ -239,6 +241,458 @@ static void xhci_pme_acpi_rtd3_enable(st - static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } - #endif /* CONFIG_ACPI */ - -+static const struct renesas_fw_entry { -+ const char *firmware_name; -+ u16 device; -+ u8 revision; -+ u16 expected_version; -+} renesas_fw_table[] = { -+ /* -+ * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A -+ * are listed in R19UH0078EJ0500 Rev.5.00 as devices which -+ * need the software loader. -+ * -+ * PP2U/ReleaseNote_USB3-201-202-FW.txt: -+ * -+ * Note: This firmware is for the following devices. -+ * - uPD720201 ES 2.0 sample whose revision ID is 2. -+ * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. -+ * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. -+ */ -+ { "K2013080.mem", 0x0014, 0x02, 0x2013 }, -+ { "K2013080.mem", 0x0014, 0x03, 0x2013 }, -+ { "K2013080.mem", 0x0015, 0x02, 0x2013 }, -+}; -+ -+static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev) -+{ -+ const struct renesas_fw_entry *entry; -+ size_t i; -+ -+ /* This loader will only work with a RENESAS device. */ -+ if (!(dev->vendor == PCI_VENDOR_ID_RENESAS)) -+ return NULL; -+ -+ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { -+ entry = &renesas_fw_table[i]; -+ if (entry->device == dev->device && -+ entry->revision == dev->revision) -+ return entry; -+ } -+ -+ return NULL; -+} -+ -+static int renesas_fw_download_image(struct pci_dev *dev, -+ const u32 *fw, -+ size_t step) -+{ -+ size_t i; -+ int err; -+ u8 fw_status; -+ bool data0_or_data1; -+ -+ /* -+ * The hardware does alternate between two 32-bit pages. -+ * (This is because each row of the firmware is 8 bytes). -+ * -+ * for even steps we use DATA0, for odd steps DATA1. -+ */ -+ data0_or_data1 = (step & 1) == 1; -+ -+ /* step+1. Read "Set DATAX" and confirm it is cleared. */ -+ for (i = 0; i < 10000; i++) { -+ err = pci_read_config_byte(dev, 0xF5, &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (!(fw_status & BIT(data0_or_data1))) -+ break; -+ -+ udelay(1); -+ } -+ if (i == 10000) -+ return -ETIMEDOUT; -+ -+ /* -+ * step+2. Write FW data to "DATAX". -+ * "LSB is left" => force little endian -+ */ -+ err = pci_write_config_dword(dev, data0_or_data1 ? 0xFC : 0xF8, -+ (__force u32) cpu_to_le32(fw[step])); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ udelay(100); -+ -+ /* step+3. Set "Set DATAX". */ -+ err = pci_write_config_byte(dev, 0xF5, BIT(data0_or_data1)); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ return 0; -+} -+ -+static int renesas_fw_verify(struct pci_dev *dev, -+ const void *fw_data, -+ size_t length) -+{ -+ const struct renesas_fw_entry *entry = renesas_needs_fw_dl(dev); -+ u16 fw_version_pointer; -+ u16 fw_version; -+ -+ if (!entry) -+ return -EINVAL; -+ -+ /* -+ * The Firmware's Data Format is describe in -+ * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124 -+ */ -+ -+ /* "Each row is 8 bytes". => firmware size must be a multiple of 8. */ -+ if (length % 8 != 0) { -+ dev_err(&dev->dev, "firmware size is not a multipe of 8."); -+ return -EINVAL; -+ } -+ -+ /* -+ * The bootrom chips of the big brother have sizes up to 64k, let's -+ * assume that's the biggest the firmware can get. -+ */ -+ if (length < 0x1000 || length >= 0x10000) { -+ dev_err(&dev->dev, "firmware is size %zd is not (4k - 64k).", -+ length); -+ return -EINVAL; -+ } -+ -+ /* The First 2 bytes are fixed value (55aa). "LSB on Left" */ -+ if (get_unaligned_le16(fw_data) != 0x55aa) { -+ dev_err(&dev->dev, "no valid firmware header found."); -+ return -EINVAL; -+ } -+ -+ /* verify the firmware version position and print it. */ -+ fw_version_pointer = get_unaligned_le16(fw_data + 4); -+ if (fw_version_pointer + 2 >= length) { -+ dev_err(&dev->dev, "firmware version pointer is outside of the firmware image."); -+ return -EINVAL; -+ } -+ -+ fw_version = get_unaligned_le16(fw_data + fw_version_pointer); -+ dev_dbg(&dev->dev, "got firmware version: %02x.", fw_version); -+ -+ if (fw_version != entry->expected_version) { -+ dev_err(&dev->dev, "firmware version mismatch, expected version: %02x.", -+ entry->expected_version); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int renesas_fw_check_running(struct pci_dev *pdev) -+{ -+ int err; -+ u8 fw_state; -+ -+ /* -+ * Test if the device is actually needing the firmware. As most -+ * BIOSes will initialize the device for us. If the device is -+ * initialized. -+ */ -+ err = pci_read_config_byte(pdev, 0xF4, &fw_state); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* -+ * Check if "FW Download Lock" is locked. If it is and the FW is -+ * ready we can simply continue. If the FW is not ready, we have -+ * to give up. -+ */ -+ if (fw_state & BIT(1)) { -+ dev_dbg(&pdev->dev, "FW Download Lock is engaged."); -+ -+ if (fw_state & BIT(4)) -+ return 0; -+ -+ dev_err(&pdev->dev, "FW Download Lock is set and FW is not ready. Giving Up."); -+ return -EIO; -+ } -+ -+ /* -+ * Check if "FW Download Enable" is set. If someone (us?) tampered -+ * with it and it can't be resetted, we have to give up too... and -+ * ask for a forgiveness and a reboot. -+ */ -+ if (fw_state & BIT(0)) { -+ dev_err(&pdev->dev, "FW Download Enable is stale. Giving Up (poweroff/reboot needed)."); -+ return -EIO; -+ } -+ -+ /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */ -+ switch ((fw_state & 0x70)) { -+ case 0: /* No result yet */ -+ dev_dbg(&pdev->dev, "FW is not ready/loaded yet."); -+ -+ /* tell the caller, that this device needs the firmware. */ -+ return 1; -+ -+ case BIT(4): /* Success, device should be working. */ -+ dev_dbg(&pdev->dev, "FW is ready."); -+ return 0; -+ -+ case BIT(5): /* Error State */ -+ dev_err(&pdev->dev, "hardware is in an error state. Giving up (poweroff/reboot needed)."); -+ return -ENODEV; -+ -+ default: /* All other states are marked as "Reserved states" */ -+ dev_err(&pdev->dev, "hardware is in an invalid state %x. Giving up (poweroff/reboot needed).", -+ (fw_state & 0x70) >> 4); -+ return -EINVAL; -+ } -+} -+ -+static int renesas_hw_check_run_stop_busy(struct pci_dev *pdev) -+{ -+#if 0 -+ u32 val; -+ -+ /* -+ * 7.1.3 Note 3: "... must not set 'FW Download Enable' when -+ * 'RUN/STOP' of USBCMD Register is set" -+ */ -+ val = readl(hcd->regs + 0x20); -+ if (val & BIT(0)) { -+ dev_err(&pdev->dev, "hardware is busy and can't receive a FW."); -+ return -EBUSY; -+ } -+#endif -+ return 0; -+} -+ -+static int renesas_fw_download(struct pci_dev *pdev, -+ const struct firmware *fw, unsigned int retry_counter) -+{ -+ const u32 *fw_data = (const u32 *) fw->data; -+ size_t i; -+ int err; -+ u8 fw_status; -+ -+ /* -+ * For more information and the big picture: please look at the -+ * "Firmware Download Sequence" in "7.1 FW Download Interface" -+ * of R19UH0078EJ0500 Rev.5.00 page 131 -+ */ -+ err = renesas_hw_check_run_stop_busy(pdev); -+ if (err) -+ return err; -+ -+ /* -+ * 0. Set "FW Download Enable" bit in the -+ * "FW Download Control & Status Register" at 0xF4 -+ */ -+ err = pci_write_config_byte(pdev, 0xF4, BIT(0)); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* 1 - 10 follow one step after the other. */ -+ for (i = 0; i < fw->size / 4; i++) { -+ err = renesas_fw_download_image(pdev, fw_data, i); -+ if (err) { -+ dev_err(&pdev->dev, "Firmware Download Step %zd failed at position %zd bytes with (%d).", -+ i, i * 4, err); -+ return err; -+ } -+ } -+ -+ /* -+ * This sequence continues until the last data is written to -+ * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1" -+ * is cleared by the hardware beforehand. -+ */ -+ for (i = 0; i < 10000; i++) { -+ err = pci_read_config_byte(pdev, 0xF5, &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (!(fw_status & (BIT(0) | BIT(1)))) -+ break; -+ -+ udelay(1); -+ } -+ if (i == 10000) -+ dev_warn(&pdev->dev, "Final Firmware Download step timed out."); -+ -+ /* -+ * 11. After finishing writing the last data of FW, the -+ * System Software must clear "FW Download Enable" -+ */ -+ err = pci_write_config_byte(pdev, 0xF4, 0); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* 12. Read "Result Code" and confirm it is good. */ -+ for (i = 0; i < 10000; i++) { -+ err = pci_read_config_byte(pdev, 0xF4, &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (fw_status & BIT(4)) -+ break; -+ -+ udelay(1); -+ } -+ if (i == 10000) { -+ /* Timed out / Error - let's see if we can fix this */ -+ err = renesas_fw_check_running(pdev); -+ switch (err) { -+ case 0: /* -+ * we shouldn't end up here. -+ * maybe it took a little bit longer. -+ * But all should be well? -+ */ -+ break; -+ -+ case 1: /* (No result yet? - we can try to retry) */ -+ if (retry_counter < 10) { -+ retry_counter++; -+ dev_warn(&pdev->dev, "Retry Firmware download: %d try.", -+ retry_counter); -+ return renesas_fw_download(pdev, fw, -+ retry_counter); -+ } -+ return -ETIMEDOUT; -+ -+ default: -+ return err; -+ } -+ } -+ /* -+ * Optional last step: Engage Firmware Lock -+ * -+ * err = pci_write_config_byte(pdev, 0xF4, BIT(2)); -+ * if (err) -+ * return pcibios_err_to_errno(err); -+ */ -+ -+ return 0; -+} -+ -+struct renesas_fw_ctx { -+ struct pci_dev *pdev; -+ const struct pci_device_id *id; -+ bool resume; -+}; -+ -+static int xhci_pci_probe(struct pci_dev *pdev, -+ const struct pci_device_id *id); -+ -+static void renesas_fw_callback(const struct firmware *fw, -+ void *context) -+{ -+ struct renesas_fw_ctx *ctx = context; -+ struct pci_dev *pdev = ctx->pdev; -+ struct device *parent = pdev->dev.parent; -+ int err = -ENOENT; -+ -+ if (fw) { -+ err = renesas_fw_verify(pdev, fw->data, fw->size); -+ if (!err) { -+ err = renesas_fw_download(pdev, fw, 0); -+ release_firmware(fw); -+ if (!err) { -+ if (ctx->resume) -+ return; -+ -+ err = xhci_pci_probe(pdev, ctx->id); -+ if (!err) { -+ /* everything worked */ -+ devm_kfree(&pdev->dev, ctx); -+ return; -+ } -+ -+ /* in case of an error - fall through */ -+ } else { -+ dev_err(&pdev->dev, "firmware failed to download (%d).", -+ err); -+ } -+ } -+ } else { -+ dev_err(&pdev->dev, "firmware failed to load (%d).", err); -+ } -+ -+ dev_info(&pdev->dev, "Unloading driver"); -+ -+ if (parent) -+ device_lock(parent); -+ -+ device_release_driver(&pdev->dev); -+ -+ if (parent) -+ device_unlock(parent); -+ -+ pci_dev_put(pdev); -+} -+ -+static int renesas_fw_alive_check(struct pci_dev *pdev) -+{ -+ const struct renesas_fw_entry *entry; -+ int err; -+ -+ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ -+ entry = renesas_needs_fw_dl(pdev); -+ if (!entry) -+ return 0; -+ -+ err = renesas_fw_check_running(pdev); -+ /* Also go ahead, if the firmware is running */ -+ if (err == 0) -+ return 0; -+ -+ /* At this point, we can be sure that the FW isn't ready. */ -+ return err; -+} -+ -+static int renesas_fw_download_to_hw(struct pci_dev *pdev, -+ const struct pci_device_id *id, -+ bool do_resume) -+{ -+ const struct renesas_fw_entry *entry; -+ struct renesas_fw_ctx *ctx; -+ int err; -+ -+ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ -+ entry = renesas_needs_fw_dl(pdev); -+ if (!entry) -+ return 0; -+ -+ err = renesas_fw_check_running(pdev); -+ /* Continue ahead, if the firmware is already running. */ -+ if (err == 0) -+ return 0; -+ -+ if (err != 1) -+ return err; -+ -+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; -+ ctx->pdev = pdev; -+ ctx->resume = do_resume; -+ ctx->id = id; -+ -+ pci_dev_get(pdev); -+ err = request_firmware_nowait(THIS_MODULE, 1, entry->firmware_name, -+ &pdev->dev, GFP_KERNEL, ctx, renesas_fw_callback); -+ if (err) { -+ pci_dev_put(pdev); -+ return err; -+ } -+ -+ /* -+ * The renesas_fw_callback() callback will continue the probe -+ * process, once it aquires the firmware. -+ */ -+ return 1; -+} -+ - /* called during probe() after chip reset completes */ - static int xhci_pci_setup(struct usb_hcd *hcd) - { -@@ -278,6 +732,22 @@ static int xhci_pci_probe(struct pci_dev - struct hc_driver *driver; - struct usb_hcd *hcd; - -+ /* -+ * Check if this device is a RENESAS uPD720201/2 device. -+ * Otherwise, we can continue with xhci_pci_probe as usual. -+ */ -+ retval = renesas_fw_download_to_hw(dev, id, false); -+ switch (retval) { -+ case 0: -+ break; -+ -+ case 1: /* let it load the firmware and recontinue the probe. */ -+ return 0; -+ -+ default: -+ return retval; -+ }; -+ - driver = (struct hc_driver *)id->driver_data; - - /* Prevent runtime suspending between USB-2 and USB-3 initialization */ -@@ -335,6 +805,16 @@ static void xhci_pci_remove(struct pci_d - { - struct xhci_hcd *xhci; - -+ if (renesas_fw_alive_check(dev)) { -+ /* -+ * bail out early, if this was a renesas device w/o FW. -+ * Else we might hit the NMI watchdog in xhci_handsake -+ * during xhci_reset as part of the driver's unloading. -+ * which we forced in the renesas_fw_callback(). -+ */ -+ return; -+ } -+ - xhci = hcd_to_xhci(pci_get_drvdata(dev)); - xhci->xhc_state |= XHCI_STATE_REMOVING; - if (xhci->shared_hcd) { diff --git a/target/linux/apm821xx/patches-4.9/802-usb-xhci-force-msi-renesas-xhci.patch b/target/linux/apm821xx/patches-4.9/802-usb-xhci-force-msi-renesas-xhci.patch deleted file mode 100644 index 3548571052..0000000000 --- a/target/linux/apm821xx/patches-4.9/802-usb-xhci-force-msi-renesas-xhci.patch +++ /dev/null @@ -1,54 +0,0 @@ -From a0dc613140bab907a3d5787a7ae7b0638bf674d0 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter -Date: Thu, 23 Jun 2016 20:28:20 +0200 -Subject: [PATCH] usb: xhci: force MSI for uPD720201 and - uPD720202 - -The APM82181 does not support MSI-X. When probed, it will -produce a noisy warning. - ---- - drivers/usb/host/pci-quirks.c | 362 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 362 insertions(+) - ---- a/drivers/usb/host/xhci-pci.c -+++ b/drivers/usb/host/xhci-pci.c -@@ -196,7 +196,7 @@ static void xhci_pci_quirks(struct devic - xhci->quirks |= XHCI_TRUST_TX_LENGTH; - if (pdev->vendor == PCI_VENDOR_ID_RENESAS && - pdev->device == 0x0015) -- xhci->quirks |= XHCI_RESET_ON_RESUME; -+ xhci->quirks |= XHCI_RESET_ON_RESUME | XHCI_FORCE_MSI; - if (pdev->vendor == PCI_VENDOR_ID_VIA) - xhci->quirks |= XHCI_RESET_ON_RESUME; - ---- a/drivers/usb/host/xhci.c -+++ b/drivers/usb/host/xhci.c -@@ -390,10 +390,14 @@ static int xhci_try_enable_msi(struct us - free_irq(hcd->irq, hcd); - hcd->irq = 0; - -- ret = xhci_setup_msix(xhci); -- if (ret) -- /* fall back to msi*/ -+ if (xhci->quirks & XHCI_FORCE_MSI) { - ret = xhci_setup_msi(xhci); -+ } else { -+ ret = xhci_setup_msix(xhci); -+ if (ret) -+ /* fall back to msi*/ -+ ret = xhci_setup_msi(xhci); -+ } - - if (!ret) - /* hcd->irq is 0, we have MSI */ ---- a/drivers/usb/host/xhci.h -+++ b/drivers/usb/host/xhci.h -@@ -1680,6 +1680,7 @@ struct xhci_hcd { - /* support xHCI 0.96 spec USB2 software LPM */ - unsigned sw_lpm_support:1; - /* support xHCI 1.0 spec USB2 hardware LPM */ -+#define XHCI_FORCE_MSI (1 << 24) - unsigned hw_lpm_support:1; - /* cached usb2 extened protocol capabilites */ - u32 *ext_caps; diff --git a/target/linux/apm821xx/patches-4.9/804-usb-dwc2-add-amcc-usb-otg-405ex.patch b/target/linux/apm821xx/patches-4.9/804-usb-dwc2-add-amcc-usb-otg-405ex.patch deleted file mode 100644 index 3dd2ed5734..0000000000 --- a/target/linux/apm821xx/patches-4.9/804-usb-dwc2-add-amcc-usb-otg-405ex.patch +++ /dev/null @@ -1,48 +0,0 @@ ---- a/drivers/usb/dwc2/platform.c -+++ b/drivers/usb/dwc2/platform.c -@@ -279,6 +279,37 @@ static int dwc2_get_dr_mode(struct dwc2_ - return 0; - } - -+static const struct dwc2_core_params params_amcc_dwc_otg = { -+ .otg_cap = DWC2_CAP_PARAM_HNP_SRP_CAPABLE, -+ .otg_ver = -1, -+ .dma_enable = -1, -+ .dma_desc_enable = -1, -+ .speed = -1, -+ .enable_dynamic_fifo = -1, -+ .en_multiple_tx_fifo = -1, -+ .host_rx_fifo_size = -1, -+ .host_nperio_tx_fifo_size = -1, -+ .host_perio_tx_fifo_size = -1, -+ .max_transfer_size = -1, -+ .max_packet_count = -1, -+ .host_channels = -1, -+ .phy_type = -1, -+ .phy_utmi_width = -1, -+ .phy_ulpi_ddr = -1, -+ .phy_ulpi_ext_vbus = -1, -+ .i2c_enable = -1, -+ .ulpi_fs_ls = -1, -+ .host_support_fs_ls_low_power = -1, -+ .host_ls_low_power_phy_clk = -1, -+ .ts_dline = -1, -+ .reload_ctl = -1, -+ .ahbcfg = GAHBCFG_HBSTLEN_INCR16 << -+ GAHBCFG_HBSTLEN_SHIFT, -+ .uframe_sched = -1, -+ .external_id_pin_ctl = -1, -+ .hibernation = -1, -+}; -+ - static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) - { - struct platform_device *pdev = to_platform_device(hsotg->dev); -@@ -511,6 +542,7 @@ static void dwc2_driver_shutdown(struct - } - - static const struct of_device_id dwc2_of_match_table[] = { -+ { .compatible = "amcc,usb-otg-405ex", .data = ¶ms_amcc_dwc_otg }, - { .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 }, - { .compatible = "hisilicon,hi6220-usb", .data = ¶ms_hi6220 }, - { .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 }, diff --git a/target/linux/apm821xx/patches-4.9/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch b/target/linux/apm821xx/patches-4.9/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch deleted file mode 100644 index ceacde9dda..0000000000 --- a/target/linux/apm821xx/patches-4.9/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch +++ /dev/null @@ -1,1027 +0,0 @@ -From 5ea2e152d846bf60901107fefd81a58f792f3bc2 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter -Date: Fri, 10 Jun 2016 03:00:46 +0200 -Subject: [PATCH] hwmon: add driver for Microchip TC654/TC655 PWM fan - controllers - -This patch adds a hwmon driver for the Microchip TC654 and TC655 -Dual SMBus PWM Fan Speed Controllers with Fan Fault detection. - -The chip is described in the DS2001734C Spec Document from Microchip. -It supports: - - Shared PWM Fan Drive for two fans - - Provides RPM - - automatic PWM controller (needs additional - NTC/PTC Thermistors.) - - Overtemperature alarm (when using NTC/PTC - Thermistors) - -Signed-off-by: Christian Lamparter ---- - drivers/hwmon/Kconfig | 10 + - drivers/hwmon/Makefile | 1 + - drivers/hwmon/tc654.c | 969 +++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 980 insertions(+) - create mode 100644 drivers/hwmon/tc654.c - ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1549,6 +1549,16 @@ config SENSORS_INA3221 - This driver can also be built as a module. If so, the module - will be called ina3221. - -+config SENSORS_TC654 -+ tristate "Microchip TC654 and TC655" -+ depends on I2C -+ help -+ If you say yes here you get support for Microchip TC655 and TC654 -+ Dual PWM Fan Speed Controllers and sensor chips. -+ -+ This driver can also be built as a module. If so, the module -+ will be called tc654. -+ - config SENSORS_TC74 - tristate "Microchip TC74" - depends on I2C ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc4 - obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o - obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o - obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o -+obj-$(CONFIG_SENSORS_TC654) += tc654.o - obj-$(CONFIG_SENSORS_TC74) += tc74.o - obj-$(CONFIG_SENSORS_THMC50) += thmc50.o - obj-$(CONFIG_SENSORS_TMP102) += tmp102.o ---- /dev/null -+++ b/drivers/hwmon/tc654.c -@@ -0,0 +1,969 @@ -+/* -+ * tc654.c - Support for Microchip TC654/TC655 -+ * "A Dual SMBus PWM FAN Speed Controllers with Fan Fault Detection" -+ * -+ * Copyright (c) 2016 Christian Lamparter -+ * -+ * 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 version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * The chip is described in the DS2001734C Spec Document from Microchip. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Hardware definitions */ -+/* 5.1.4 Address Byte stats that TC654/TC655 are fixed at 0x1b */ -+static const unsigned short normal_i2c[] = { 0x1b, I2C_CLIENT_END }; -+ -+enum TC654_REGS { -+ TC654_REG_RPM1 = 0x00, -+ TC654_REG_RPM2, -+ TC654_REG_FAN1_FAULT_THRESH, -+ TC654_REG_FAN2_FAULT_THRESH, -+ TC654_REG_CONFIG, -+ TC654_REG_STATUS, -+ TC654_REG_DUTY_CYCLE, -+ TC654_REG_MFR_ID, -+ TC654_REG_VER_ID, -+ -+ /* keep last */ -+ __TC654_REG_NUM, -+}; -+ -+#define TC654_MFR_ID_MICROCHIP 0x84 -+#define TC654_VER_ID 0x00 -+#define TC655_VER_ID 0x01 -+ -+enum TC654_CONTROL_BITS { -+ TC654_CTRL_SDM = BIT(0), -+ TC654_CTRL_F1PPR_S = 1, -+ TC654_CTRL_F1PPR_M = (BIT(1) | BIT(2)), -+ TC654_CTRL_F2PPR_S = 3, -+ TC654_CTRL_F2PPR_M = (BIT(3) | BIT(4)), -+ TC654_CTRL_DUTYC = BIT(5), -+ TC654_CTRL_RES = BIT(6), -+ TC654_CTRL_FFCLR = BIT(7), -+}; -+ -+enum TC654_STATUS_BITS { -+ TC654_STATUS_F1F = BIT(0), -+ TC654_STATUS_F2F = BIT(1), -+ TC654_STATUS_VSTAT = BIT(2), -+ TC654_STATUS_R1CO = BIT(3), -+ TC654_STATUS_R2CO = BIT(4), -+ TC654_STATUS_OTF = BIT(5), -+}; -+ -+enum TC654_FAN { -+ TC654_FAN1 = 0, -+ TC654_FAN2, -+ -+ /* keep last */ -+ __NUM_TC654_FAN, -+}; -+ -+enum TC654_FAN_MODE { -+ TC654_PWM_OFF, /* Shutdown Mode - switch of both fans */ -+ TC654_PWM_VIN, /* Fans will be controlled via V_in analog input pin */ -+ TC654_PWM_3000, /* sets fans to 30% duty cycle */ -+ TC654_PWM_3467, -+ TC654_PWM_3933, /* default case - if V_in pin is open */ -+ TC654_PWM_4400, -+ TC654_PWM_4867, -+ TC654_PWM_5333, -+ TC654_PWM_5800, -+ TC654_PWM_6267, -+ TC654_PWM_6733, -+ TC654_PWM_7200, -+ TC654_PWM_7667, -+ TC654_PWM_8133, -+ TC654_PWM_8600, -+ TC654_PWM_9067, -+ TC654_PWM_9533, -+ TC654_PWM_10000, /* sets fans to 100% duty cycle */ -+}; -+ -+enum TC654_ALARMS { -+ TC654_ALARM_FAN1_FAULT, -+ TC654_ALARM_FAN2_FAULT, -+ TC654_ALARM_FAN1_COUNTER_OVERFLOW, -+ TC654_ALARM_FAN2_COUNTER_OVERFLOW, -+ TC654_ALARM_OVER_TEMPERATURE, -+ -+ /* KEEP LAST */ -+ __NUM_TC654_ALARMS, -+}; -+ -+static const struct pwm_table_entry { -+ u8 min; -+ enum TC654_FAN_MODE mode; -+} pwm_table[] = { -+ { 0, TC654_PWM_OFF }, -+ { 1, TC654_PWM_3000 }, -+ { 88, TC654_PWM_3467 }, -+ {101, TC654_PWM_3933 }, -+ {113, TC654_PWM_4400 }, -+ {125, TC654_PWM_4867 }, -+ {137, TC654_PWM_5333 }, -+ {148, TC654_PWM_5800 }, -+ {160, TC654_PWM_6267 }, -+ {172, TC654_PWM_6733 }, -+ {184, TC654_PWM_7200 }, -+ {196, TC654_PWM_7667 }, -+ {208, TC654_PWM_8133 }, -+ {220, TC654_PWM_8600 }, -+ {232, TC654_PWM_9067 }, -+ {244, TC654_PWM_9533 }, -+ {255, TC654_PWM_10000 }, -+}; -+ -+/* driver context */ -+struct tc654 { -+ struct i2c_client *client; -+ -+ struct mutex update_lock; -+ -+ unsigned long last_updated; /* in jiffies */ -+ u8 cached_regs[__TC654_REG_NUM]; -+ -+ bool valid; /* monitored registers are valid */ -+ u16 fan_input[__NUM_TC654_FAN]; -+ bool alarms[__NUM_TC654_ALARMS]; -+ bool vin_status; -+ bool pwm_manual; -+ -+ /* optional cooling device */ -+ struct thermal_cooling_device *cdev; -+}; -+ -+/* hardware accessors and functions */ -+static int read_tc(struct tc654 *tc, u8 reg) -+{ -+ s32 status; -+ -+ if (reg <= TC654_REG_VER_ID) { -+ /* Table 6.1 states that all registers are readable */ -+ status = i2c_smbus_read_byte_data(tc->client, reg); -+ } else -+ status = -EINVAL; -+ -+ if (status < 0) { -+ dev_warn(&tc->client->dev, "can't read register 0x%02x due to error (%d)", -+ reg, status); -+ } else { -+ tc->cached_regs[reg] = status; -+ } -+ -+ return status; -+} -+ -+static int write_tc(struct tc654 *tc, u8 i2c_reg, u8 val) -+{ -+ s32 status; -+ -+ /* -+ * Table 6.1 states that both fan threshold registers, -+ * the Config and Duty Cycle are writeable. -+ */ -+ switch (i2c_reg) { -+ case TC654_REG_FAN1_FAULT_THRESH: -+ case TC654_REG_FAN2_FAULT_THRESH: -+ case TC654_REG_DUTY_CYCLE: -+ case TC654_REG_CONFIG: -+ status = i2c_smbus_write_byte_data(tc->client, i2c_reg, val); -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ if (status < 0) { -+ dev_warn(&tc->client->dev, "can't write register 0x%02x with value 0x%02x due to error (%d)", -+ i2c_reg, val, status); -+ } else { -+ tc->cached_regs[i2c_reg] = val; -+ } -+ -+ return status; -+} -+ -+static int mod_config(struct tc654 *tc, u8 set, u8 clear) -+{ -+ u8 val = 0; -+ -+ /* a bit can't be set and cleared on the same time. */ -+ if (set & clear) -+ return -EINVAL; -+ -+ /* invalidate data to force re-read from hardware */ -+ tc->valid = false; -+ val = (tc->cached_regs[TC654_REG_CONFIG] | set) & (~clear); -+ return write_tc(tc, TC654_REG_CONFIG, val); -+} -+ -+static int read_fan_rpm(struct tc654 *tc, enum TC654_FAN fan) -+{ -+ int ret; -+ -+ /* 6.1 RPM-OUTPUT1 and RPM-OUTPUT2 registers */ -+ ret = read_tc(tc, fan == TC654_FAN1 ? TC654_REG_RPM1 : TC654_REG_RPM2); -+ if (ret < 0) -+ return ret; -+ -+ /* -+ * The Resolution Selection Bit in 6.3 CONFIGURATION REGISTER -+ * is needed to convert the raw value to the RPM. -+ * 0 = RPM1 and RPM2 use (8-Bit) resolution => * 50 RPM -+ * 1 = RPM1 and RPM2 use (9-Bit) resolution => * 25 RPM -+ */ -+ return ret * (25 << -+ !(tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES)); -+} -+ -+static int write_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan, -+ u16 rpm) -+{ -+ u8 converted_rpm; -+ -+ if (rpm > 12750) -+ return -EINVAL; -+ -+ /* -+ * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers -+ * -+ * Both registers operate in 50 RPM mode exclusively. -+ */ -+ converted_rpm = rpm / 50; -+ -+ /* invalidate data to force re-read from hardware */ -+ tc->valid = false; -+ return write_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH : -+ TC654_REG_FAN2_FAULT_THRESH, converted_rpm); -+} -+ -+ -+static int read_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan) -+{ -+ /* -+ * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers -+ * -+ * Both registers operate in 50 RPM mode exclusively. -+ */ -+ return read_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH : -+ TC654_REG_FAN2_FAULT_THRESH) * 50; -+} -+ -+static enum TC654_FAN_MODE get_fan_mode(struct tc654 *tc) -+{ -+ if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) { -+ return TC654_PWM_OFF; -+ } else if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_DUTYC) { -+ return TC654_PWM_3000 + tc->cached_regs[TC654_REG_DUTY_CYCLE]; -+ } else if (tc->vin_status == 0) -+ return TC654_PWM_VIN; -+ -+ return -EINVAL; -+} -+ -+static int write_fan_mode(struct tc654 *tc, enum TC654_FAN_MODE mode) -+{ -+ int err; -+ u8 pwm_mode; -+ bool in_sdm; -+ -+ in_sdm = !!(tc->cached_regs[TC654_REG_CONFIG] & -+ TC654_CTRL_SDM); -+ -+ switch (mode) { -+ case TC654_PWM_OFF: -+ if (in_sdm) -+ return 0; -+ -+ /* Enter Shutdown Mode - Switches off all fans */ -+ err = mod_config(tc, TC654_CTRL_SDM, TC654_CTRL_DUTYC); -+ if (err) -+ return err; -+ -+ return 0; -+ -+ case TC654_PWM_VIN: -+ if (tc->vin_status) { -+ dev_err(&tc->client->dev, -+ "V_in pin is open, can't enable automatic mode."); -+ return -EINVAL; -+ } -+ -+ err = mod_config(tc, 0, TC654_CTRL_SDM | TC654_CTRL_DUTYC); -+ if (err) -+ return err; -+ -+ tc->pwm_manual = false; -+ return 0; -+ -+ case TC654_PWM_3000: -+ case TC654_PWM_3467: -+ case TC654_PWM_3933: -+ case TC654_PWM_4400: -+ case TC654_PWM_4867: -+ case TC654_PWM_5333: -+ case TC654_PWM_5800: -+ case TC654_PWM_6267: -+ case TC654_PWM_6733: -+ case TC654_PWM_7200: -+ case TC654_PWM_7667: -+ case TC654_PWM_8133: -+ case TC654_PWM_8600: -+ case TC654_PWM_9067: -+ case TC654_PWM_9533: -+ case TC654_PWM_10000: -+ pwm_mode = mode - TC654_PWM_3000; -+ if (!in_sdm) { -+ err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode); -+ if (err) -+ return err; -+ } -+ -+ err = mod_config(tc, TC654_CTRL_DUTYC, TC654_CTRL_SDM); -+ if (err) -+ return err; -+ -+ tc->pwm_manual = true; -+ -+ if (in_sdm) { -+ /* -+ * In case the TC654/TC655 was in SDM mode, the write -+ * above into the TC654_REG_DUTY_CYCLE register will -+ * have no effect because the chip was switched off. -+ * -+ * Note: The TC654/TC655 have a special "power-on" -+ * feature where the PWM will be forced to 100% for -+ * one full second in order to spin-up a resting fan. -+ */ -+ err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode); -+ if (err) -+ return err; -+ } -+ -+ return 0; -+ -+ default: -+ return -EINVAL; -+ } -+} -+ -+static struct tc654 *tc654_update_device(struct device *dev) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ -+ mutex_lock(&tc->update_lock); -+ -+ /* -+ * In Chapter "1.0 Electrical Characteristics", -+ * the "Fault Output Response Time" is specified as 2.4 seconds. -+ */ -+ if (time_after(jiffies, tc->last_updated + 2 * HZ + (HZ * 2) / 5) -+ || !tc->valid) { -+ size_t i; -+ int ret; -+ bool alarm_triggered; -+ -+ tc->valid = false; -+ -+ for (i = 0; i < __NUM_TC654_FAN; i++) { -+ ret = read_fan_rpm(tc, i); -+ if (ret < 0) -+ goto out; -+ -+ tc->fan_input[i] = ret; -+ } -+ -+ ret = read_tc(tc, TC654_REG_STATUS); -+ if (ret < 0) -+ goto out; -+ -+ alarm_triggered = !!(ret & (TC654_STATUS_F1F | -+ TC654_STATUS_F2F | TC654_STATUS_R1CO | -+ TC654_STATUS_R2CO | TC654_STATUS_OTF)); -+ -+ tc->alarms[TC654_ALARM_FAN1_FAULT] = !!(ret & TC654_STATUS_F1F); -+ tc->alarms[TC654_ALARM_FAN2_FAULT] = !!(ret & TC654_STATUS_F2F); -+ tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] = -+ !!(ret & TC654_STATUS_R1CO); -+ tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW] = -+ !!(ret & TC654_STATUS_R2CO); -+ tc->alarms[TC654_ALARM_OVER_TEMPERATURE] = -+ !!(ret & TC654_STATUS_OTF); -+ tc->vin_status = !!(ret & TC654_STATUS_VSTAT); -+ -+ /* -+ * From 4.5 and 6.3 -+ * -+ * ... "If the V_in pin is open when TC654_CTRL_DUTYC is not -+ * selected, then V_out duty cycle will default to 39.33%.". -+ * -+ * and most importantly 6.5: -+ * ... "V_in pin is open, the duty cycle will go to the default -+ * setting of this register, which is 0010 (39.33%)." -+ */ -+ tc->pwm_manual |= tc->vin_status && -+ (tc->cached_regs[TC654_REG_CONFIG] & -+ TC654_CTRL_DUTYC); -+ -+ if (alarm_triggered) { -+ /* -+ * as the name implies, this FLAG needs to be -+ * set in order to clear the FAN Fault error. -+ */ -+ ret = mod_config(tc, TC654_CTRL_FFCLR, 0); -+ if (ret < 0) -+ goto out; -+ } -+ -+ tc->last_updated = jiffies; -+ tc->valid = true; -+ } -+ -+out: -+ mutex_unlock(&tc->update_lock); -+ return tc; -+} -+ -+static u8 get_fan_pulse(struct tc654 *tc, enum TC654_FAN fan) -+{ -+ u8 fan_pulse_mask = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M; -+ u8 fan_pulse_shift = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S; -+ -+ return 1 << ((tc->cached_regs[TC654_REG_CONFIG] & fan_pulse_mask) >> -+ fan_pulse_shift); -+} -+ -+static int -+set_fan_pulse(struct tc654 *tc, enum TC654_FAN fan, int pulses) -+{ -+ int old_pulses; -+ int err; -+ u8 new_pulse_per_rotation; -+ u8 fan_pulse_mask = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M; -+ u8 fan_pulse_shift = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S; -+ -+ switch (pulses) { -+ case 1: -+ new_pulse_per_rotation = 0; -+ break; -+ case 2: -+ new_pulse_per_rotation = 1; -+ break; -+ case 4: -+ new_pulse_per_rotation = 2; -+ break; -+ case 8: -+ new_pulse_per_rotation = 3; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ new_pulse_per_rotation <<= fan_pulse_shift; -+ new_pulse_per_rotation &= fan_pulse_mask; -+ -+ old_pulses = tc->cached_regs[TC654_REG_CONFIG]; -+ old_pulses &= fan_pulse_mask; -+ -+ if (new_pulse_per_rotation == old_pulses) -+ return 0; -+ -+ mutex_lock(&tc->update_lock); -+ err = mod_config(tc, new_pulse_per_rotation, -+ old_pulses & (~new_pulse_per_rotation)); -+ mutex_unlock(&tc->update_lock); -+ -+ /* invalidate RPM data to force re-read from hardware */ -+ tc->valid = false; -+ -+ return err; -+} -+ -+static int get_fan_speed(struct tc654 *tc) -+{ -+ enum TC654_FAN_MODE mode; -+ size_t i; -+ -+ mode = get_fan_mode(tc); -+ for (i = 0; i < ARRAY_SIZE(pwm_table); i++) { -+ if (mode == pwm_table[i].mode) -+ return pwm_table[i].min; -+ } -+ -+ return -EINVAL; -+} -+ -+static int set_fan_speed(struct tc654 *tc, int new_value) -+{ -+ int result; -+ size_t i; -+ -+ if (new_value > pwm_table[ARRAY_SIZE(pwm_table) - 1].min || -+ new_value < pwm_table[0].min) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(pwm_table); i++) { -+ /* exact match */ -+ if (pwm_table[i].min == new_value) -+ break; -+ -+ /* a little bit too big - go with the previous entry */ -+ if (pwm_table[i].min > new_value) { -+ --i; -+ break; -+ } -+ } -+ -+ mutex_lock(&tc->update_lock); -+ result = write_fan_mode(tc, pwm_table[i].mode); -+ mutex_unlock(&tc->update_lock); -+ if (result < 0) -+ return result; -+ -+ return 0; -+} -+ -+/* sysfs */ -+ -+static ssize_t -+show_fan_input(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", tc->fan_input[nr]); -+} -+ -+static ssize_t -+show_fan_min(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", read_fan_fault_thresh(tc, nr)); -+} -+ -+static ssize_t -+show_fan_min_alarm(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", nr == TC654_FAN1 ? -+ tc->alarms[TC654_ALARM_FAN1_FAULT] : -+ tc->alarms[TC654_ALARM_FAN2_FAULT]); -+} -+ -+static ssize_t -+show_fan_max_alarm(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", nr == TC654_FAN1 ? -+ tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] : -+ tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW]); -+} -+ -+static ssize_t -+set_fan_min(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_min; -+ int nr = to_sensor_dev_attr(da)->index; -+ int old_min = read_fan_fault_thresh(tc, nr); -+ int status = kstrtol(buf, 10, &new_min); -+ -+ if (status < 0) -+ return status; -+ -+ new_min = (new_min / 50) * 50; -+ if (new_min == old_min) /* No change */ -+ return count; -+ -+ if (new_min < 0 || new_min > 12750) -+ return -EINVAL; -+ -+ mutex_lock(&tc->update_lock); -+ status = write_fan_fault_thresh(tc, nr, new_min); -+ mutex_unlock(&tc->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_fan_max(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ int max_rpm = tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES ? -+ (((1 << 9) - 1) * 25) /* ((2**9) - 1) * 25 RPM */: -+ (((1 << 8) - 1) * 50) /* ((2**8) - 1) * 50 RPM */; -+ -+ return sprintf(buf, "%d\n", max_rpm); -+} -+ -+static ssize_t -+show_fan_fault(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ u8 fan_fault_mask = nr == TC654_FAN1 ? -+ TC654_STATUS_F1F : TC654_STATUS_F2F; -+ -+ return sprintf(buf, "%d\n", -+ !!(tc->cached_regs[TC654_REG_STATUS] & fan_fault_mask)); -+} -+ -+static ssize_t -+show_fan_pulses(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", get_fan_pulse(tc, nr)); -+} -+ -+static ssize_t -+set_fan_pulses(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_pulse; -+ int nr = to_sensor_dev_attr(da)->index; -+ int status = kstrtol(buf, 10, &new_pulse); -+ -+ if (status < 0) -+ return status; -+ -+ status = set_fan_pulse(tc, nr, new_pulse); -+ if (status < 0) -+ return status; -+ -+ return count; -+} -+ -+static ssize_t -+show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int pwm_enabled; -+ -+ if ((tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) && -+ !tc->pwm_manual) { -+ pwm_enabled = 0; /* full off */ -+ } else { -+ if (tc->valid && tc->vin_status == 0) -+ pwm_enabled = 2; /* automatic fan speed control */ -+ -+ pwm_enabled = 1; /* PWM Mode */ -+ } -+ -+ return sprintf(buf, "%d\n", pwm_enabled); -+} -+ -+static ssize_t -+set_pwm_enable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_value; -+ -+ int result = kstrtol(buf, 10, &new_value); -+ -+ if (result < 0) -+ return result; -+ -+ mutex_lock(&tc->update_lock); -+ switch (new_value) { -+ case 0: /* no fan control (i.e. is OFF) */ -+ result = write_fan_mode(tc, TC654_PWM_OFF); -+ tc->pwm_manual = false; -+ break; -+ -+ case 1: /* manual fan control enabled (using pwm) */ -+ result = write_fan_mode(tc, TC654_PWM_10000); -+ break; -+ -+ case 2: /* automatic fan speed control enabled */ -+ result = write_fan_mode(tc, TC654_PWM_VIN); -+ break; -+ -+ default: -+ result = -EINVAL; -+ } -+ -+ mutex_unlock(&tc->update_lock); -+ return result < 0 ? result : count; -+} -+ -+static ssize_t -+show_pwm(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int ret; -+ -+ ret = get_fan_speed(tc); -+ if (ret < 0) -+ return ret; -+ -+ return sprintf(buf, "%d\n", ret); -+} -+ -+static ssize_t -+set_pwm(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_value = -1; -+ int result = kstrtol(buf, 10, &new_value); -+ -+ if (result < 0) -+ return result; -+ -+ if (new_value < 0 || new_value > INT_MAX) -+ return -EINVAL; -+ -+ if (!tc->pwm_manual) -+ return -EINVAL; -+ -+ result = set_fan_speed(tc, new_value); -+ if (result < 0) -+ return result; -+ -+ return count; -+} -+ -+static ssize_t -+show_temp_alarm_otf(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ -+ return sprintf(buf, "%d\n", tc->alarms[TC654_ALARM_OVER_TEMPERATURE]); -+} -+ -+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, show_fan_min, -+ set_fan_min, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_fan_min_alarm, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, show_fan_max_alarm, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO, show_fan_max, NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_pulses, S_IRUGO | S_IWUSR, show_fan_pulses, -+ set_fan_pulses, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, show_fan_min, -+ set_fan_min, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_max, S_IRUGO, show_fan_max, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_min_alarm, S_IRUGO, show_fan_min_alarm, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_max_alarm, S_IRUGO, show_fan_max_alarm, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_pulses, S_IRUGO | S_IWUSR, show_fan_pulses, -+ set_fan_pulses, TC654_FAN2); -+ -+static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, -+ set_pwm_enable); -+static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); -+ -+static DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_temp_alarm_otf, NULL); -+ -+/* sensors present on all models */ -+static struct attribute *tc654_attrs[] = { -+ &sensor_dev_attr_fan1_input.dev_attr.attr, -+ &sensor_dev_attr_fan1_min.dev_attr.attr, -+ &sensor_dev_attr_fan1_max.dev_attr.attr, -+ &sensor_dev_attr_fan1_min_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan1_max_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan1_fault.dev_attr.attr, -+ &sensor_dev_attr_fan1_pulses.dev_attr.attr, -+ &sensor_dev_attr_fan2_input.dev_attr.attr, -+ &sensor_dev_attr_fan2_min.dev_attr.attr, -+ &sensor_dev_attr_fan2_max.dev_attr.attr, -+ &sensor_dev_attr_fan2_min_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan2_max_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan2_fault.dev_attr.attr, -+ &sensor_dev_attr_fan2_pulses.dev_attr.attr, -+ -+ &dev_attr_pwm1_enable.attr, -+ &dev_attr_pwm1.attr, -+ -+ &dev_attr_temp1_emergency_alarm.attr, -+ NULL -+}; -+ -+ATTRIBUTE_GROUPS(tc654); -+ -+/* cooling device */ -+ -+static int tc654_get_max_state(struct thermal_cooling_device *cdev, -+ unsigned long *state) -+{ -+ *state = 255; -+ return 0; -+} -+ -+static int tc654_get_cur_state(struct thermal_cooling_device *cdev, -+ unsigned long *state) -+{ -+ struct tc654 *tc = cdev->devdata; -+ int ret; -+ -+ if (!tc) -+ return -EINVAL; -+ -+ ret = get_fan_speed(tc); -+ if (ret < 0) -+ return ret; -+ -+ *state = ret; -+ return 0; -+} -+ -+static int tc654_set_cur_state(struct thermal_cooling_device *cdev, -+ unsigned long state) -+{ -+ struct tc654 *tc = cdev->devdata; -+ -+ if (!tc) -+ return -EINVAL; -+ -+ if (state > INT_MAX) -+ return -EINVAL; -+ -+ return set_fan_speed(tc, state); -+} -+ -+static const struct thermal_cooling_device_ops tc654_fan_cool_ops = { -+ .get_max_state = tc654_get_max_state, -+ .get_cur_state = tc654_get_cur_state, -+ .set_cur_state = tc654_set_cur_state, -+}; -+ -+ -+/* hardware probe and detection */ -+ -+static int -+tc654_probe(struct i2c_client *client, const struct i2c_device_id *id) -+{ -+ struct tc654 *tc; -+ struct device *hwmon_dev; -+ int ret, i; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) -+ return -EIO; -+ -+ tc = devm_kzalloc(&client->dev, sizeof(*tc), GFP_KERNEL); -+ if (!tc) -+ return -ENOMEM; -+ -+ i2c_set_clientdata(client, tc); -+ tc->client = client; -+ mutex_init(&tc->update_lock); -+ -+ /* cache all 8 registers */ -+ for (i = 0; i < __TC654_REG_NUM; i++) { -+ ret = read_tc(tc, i); -+ if (ret < 0) -+ return ret; -+ } -+ -+ /* sysfs hooks */ -+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, -+ client->name, tc, -+ tc654_groups); -+ if (IS_ERR(hwmon_dev)) -+ return PTR_ERR(hwmon_dev); -+ -+#if IS_ENABLED(CONFIG_OF) -+ /* Optional cooling device register for Device tree platforms */ -+ tc->cdev = thermal_of_cooling_device_register(client->dev.of_node, -+ "tc654", tc, -+ &tc654_fan_cool_ops); -+#else /* CONFIG_OF */ -+ /* Optional cooling device register for non Device tree platforms */ -+ tc->cdev = thermal_cooling_device_register("tc654", tc, -+ &tc654_fan_cool_ops); -+#endif /* CONFIG_OF */ -+ -+ dev_info(&client->dev, "%s: sensor '%s'\n", -+ dev_name(hwmon_dev), client->name); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id tc654_ids[] = { -+ { "tc654", 0, }, -+ { } -+}; -+MODULE_DEVICE_TABLE(i2c, tc654_ids); -+ -+/* Return 0 if detection is successful, -ENODEV otherwise */ -+static int -+tc654_detect(struct i2c_client *new_client, struct i2c_board_info *info) -+{ -+ struct i2c_adapter *adapter = new_client->adapter; -+ int manufacturer, product; -+ -+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) -+ return -ENODEV; -+ -+ manufacturer = i2c_smbus_read_byte_data(new_client, TC654_REG_MFR_ID); -+ if (manufacturer != TC654_MFR_ID_MICROCHIP) -+ return -ENODEV; -+ -+ product = i2c_smbus_read_byte_data(new_client, TC654_REG_VER_ID); -+ if (!((product == TC654_VER_ID) || (product == TC655_VER_ID))) -+ return -ENODEV; -+ -+ strlcpy(info->type, "tc654", I2C_NAME_SIZE); -+ return 0; -+} -+ -+static struct i2c_driver tc654_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "tc654", -+ }, -+ .probe = tc654_probe, -+ .id_table = tc654_ids, -+ .detect = tc654_detect, -+ .address_list = normal_i2c, -+}; -+ -+module_i2c_driver(tc654_driver); -+ -+MODULE_AUTHOR("Christian Lamparter "); -+MODULE_DESCRIPTION("Microchip TC654/TC655 hwmon driver"); -+MODULE_LICENSE("GPL");