apm821xx: remove 4.4 kernel support
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
This commit is contained in:
		 Christian Lamparter
					Christian Lamparter
				
			
				
					committed by
					
						 Felix Fietkau
						Felix Fietkau
					
				
			
			
				
	
			
			
			 Felix Fietkau
						Felix Fietkau
					
				
			
						parent
						
							51397d7d95
						
					
				
				
					commit
					cead8f9dfd
				
			| @@ -1,331 +0,0 @@ | |||||||
| # CONFIG_40x is not set |  | ||||||
| CONFIG_44x=y |  | ||||||
| CONFIG_4xx=y |  | ||||||
| CONFIG_4xx_SOC=y |  | ||||||
| # CONFIG_ADVANCED_OPTIONS is not set |  | ||||||
| CONFIG_APM821xx=y |  | ||||||
| # CONFIG_APOLLO3G is not set |  | ||||||
| # 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_ATOMIC64_DEC_IF_POSITIVE=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_REQUIRE_GPIOLIB=y |  | ||||||
| CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y |  | ||||||
| CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=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_ARCH_WANT_OPTIONAL_GPIOLIB=y |  | ||||||
| CONFIG_AUDIT_ARCH=y |  | ||||||
| # CONFIG_BAMBOO is not set |  | ||||||
| CONFIG_BCH=y |  | ||||||
| # CONFIG_BLUESTONE is not set |  | ||||||
| CONFIG_BUCKMINSTER=y |  | ||||||
| CONFIG_BOOKE=y |  | ||||||
| CONFIG_BOOKE_WDT=y |  | ||||||
| CONFIG_BOUNCE=y |  | ||||||
| # CONFIG_CANYONLANDS is not set |  | ||||||
| # CONFIG_IKAREM is not set |  | ||||||
| CONFIG_CC_OPTIMIZE_FOR_SIZE=y |  | ||||||
| CONFIG_CLONE_BACKWARDS=y |  | ||||||
| CONFIG_CMDLINE="rootfstype=squashfs noinitrd" |  | ||||||
| CONFIG_CMDLINE_BOOL=y |  | ||||||
| CONFIG_CONSISTENT_SIZE=0x00200000 |  | ||||||
| CONFIG_CPU_BIG_ENDIAN=y |  | ||||||
| # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set |  | ||||||
| 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_FREEZER=y |  | ||||||
| # 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_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_GLOB=y |  | ||||||
| CONFIG_GPIOLIB=y |  | ||||||
| CONFIG_GPIO_DEVRES=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_BPF_JIT=y |  | ||||||
| CONFIG_HAVE_DEBUG_KMEMLEAK=y |  | ||||||
| CONFIG_HAVE_DEBUG_STACKOVERFLOW=y |  | ||||||
| CONFIG_HAVE_DMA_API_DEBUG=y |  | ||||||
| CONFIG_HAVE_DMA_ATTRS=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_LATENCYTOP_SUPPORT=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_REGS_AND_STACK_ACCESS_API=y |  | ||||||
| # CONFIG_HAVE_SETUP_PER_CPU_AREA is not set |  | ||||||
| CONFIG_HAVE_SYSCALL_TRACEPOINTS=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_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_KATMAI is not set |  | ||||||
| CONFIG_KERNEL_START=0xc0000000 |  | ||||||
| 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_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_FIRMWARE_NAME is not set |  | ||||||
| # CONFIG_MTD_SPLIT_SQUASHFS_ROOT is not set |  | ||||||
| # CONFIG_MTD_SPLIT_SUPPORT is not set |  | ||||||
| # CONFIG_MTD_UBI is not set |  | ||||||
| # CONFIG_MTD_UBI_BEB_LIMIT is not set |  | ||||||
| # CONFIG_MTD_UBI_BLOCK is not set |  | ||||||
| # CONFIG_MTD_UBI_FASTMAP is not set |  | ||||||
| # CONFIG_MTD_UBI_GLUEBI is not set |  | ||||||
| # CONFIG_MTD_UBI_WL_THRESHOLD 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_MTD=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_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 is not set |  | ||||||
| CONFIG_PPC4xx_CPM=y |  | ||||||
| CONFIG_PPC4xx_GPIO=y |  | ||||||
| # CONFIG_PPC4xx_HSTA_MSI is not set |  | ||||||
| # CONFIG_PPC4xx_MSI is not set |  | ||||||
| CONFIG_PPC4xx_OCM=y |  | ||||||
| # CONFIG_PPC4xx_PCI_EXPRESS is not set |  | ||||||
| # 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 is not set |  | ||||||
| # 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_RAINIER is not set |  | ||||||
| CONFIG_RAS=y |  | ||||||
| # CONFIG_RCU_STALL_COMMON is not set |  | ||||||
| CONFIG_RD_GZIP=y |  | ||||||
| CONFIG_RWSEM_XCHGADD_ALGORITHM=y |  | ||||||
| # CONFIG_SAM440EP is not set |  | ||||||
| CONFIG_SCHED_HRTICK=y |  | ||||||
| # CONFIG_SCHED_INFO is not set |  | ||||||
| # CONFIG_SCSI_DMA 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_STRICT_MM_TYPECHECKS is not set |  | ||||||
| CONFIG_SUSPEND=y |  | ||||||
| CONFIG_SUSPEND_FREEZER=y |  | ||||||
| # CONFIG_SWIOTLB is not set |  | ||||||
| CONFIG_SYSCTL_EXCEPTION_TRACE=y |  | ||||||
| # CONFIG_TAISHAN is not set |  | ||||||
| CONFIG_TASK_SIZE=0xc0000000 |  | ||||||
| CONFIG_TICK_CPU_ACCOUNTING=y |  | ||||||
| # CONFIG_UBIFS_FS is not set |  | ||||||
| # CONFIG_UBIFS_FS_ADVANCED_COMPR is not set |  | ||||||
| # CONFIG_UBIFS_FS_LZO is not set |  | ||||||
| # CONFIG_UBIFS_FS_ZLIB is not set |  | ||||||
| CONFIG_USB_SUPPORT=y |  | ||||||
| CONFIG_VDSO32=y |  | ||||||
| # CONFIG_WARP is not set |  | ||||||
| # CONFIG_WNDR4700 is not set |  | ||||||
| CONFIG_WORD_SIZE=32 |  | ||||||
| # 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 |  | ||||||
| # CONFIG_JFFS2_FS is not set |  | ||||||
| # CONFIG_SENSORS_TC654 is not set |  | ||||||
| @@ -1,481 +0,0 @@ | |||||||
| From 5343e674f32fb82b7a80a24b5a84eee62d3fe624 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Christian Lamparter <chunkeey@googlemail.com> |  | ||||||
| Date: Mon, 18 Apr 2016 12:57:41 +0200 |  | ||||||
| Subject: [PATCH] crypto4xx: integrate ppc4xx-rng into crypto4xx |  | ||||||
|  |  | ||||||
| This patch integrates the ppc4xx-rng driver into the existing |  | ||||||
| crypto4xx. This is because the true random number generator |  | ||||||
| is controlled and part of the security core. |  | ||||||
|  |  | ||||||
| Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> |  | ||||||
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |  | ||||||
| --- |  | ||||||
|  drivers/char/hw_random/Kconfig          |  13 --- |  | ||||||
|  drivers/char/hw_random/Makefile         |   1 - |  | ||||||
|  drivers/char/hw_random/ppc4xx-rng.c     | 147 -------------------------------- |  | ||||||
|  drivers/crypto/Kconfig                  |   8 ++ |  | ||||||
|  drivers/crypto/amcc/Makefile            |   1 + |  | ||||||
|  drivers/crypto/amcc/crypto4xx_core.c    |   7 +- |  | ||||||
|  drivers/crypto/amcc/crypto4xx_core.h    |   4 + |  | ||||||
|  drivers/crypto/amcc/crypto4xx_reg_def.h |   1 + |  | ||||||
|  drivers/crypto/amcc/crypto4xx_trng.c    | 131 ++++++++++++++++++++++++++++ |  | ||||||
|  drivers/crypto/amcc/crypto4xx_trng.h    |  34 ++++++++ |  | ||||||
|  10 files changed, 184 insertions(+), 163 deletions(-) |  | ||||||
|  delete mode 100644 drivers/char/hw_random/ppc4xx-rng.c |  | ||||||
|  create mode 100644 drivers/crypto/amcc/crypto4xx_trng.c |  | ||||||
|  create mode 100644 drivers/crypto/amcc/crypto4xx_trng.h |  | ||||||
|  |  | ||||||
| --- a/drivers/char/hw_random/Kconfig |  | ||||||
| +++ b/drivers/char/hw_random/Kconfig |  | ||||||
| @@ -268,19 +268,6 @@ config HW_RANDOM_NOMADIK |  | ||||||
|   |  | ||||||
|  	  If unsure, say Y. |  | ||||||
|   |  | ||||||
| -config HW_RANDOM_PPC4XX |  | ||||||
| -	tristate "PowerPC 4xx generic true random number generator support" |  | ||||||
| -	depends on PPC && 4xx |  | ||||||
| -	default HW_RANDOM |  | ||||||
| -	---help--- |  | ||||||
| -	 This driver provides the kernel-side support for the TRNG hardware |  | ||||||
| -	 found in the security function of some PowerPC 4xx SoCs. |  | ||||||
| - |  | ||||||
| -	 To compile this driver as a module, choose M here: the |  | ||||||
| -	 module will be called ppc4xx-rng. |  | ||||||
| - |  | ||||||
| -	 If unsure, say N. |  | ||||||
| - |  | ||||||
|  config HW_RANDOM_PSERIES |  | ||||||
|  	tristate "pSeries HW Random Number Generator support" |  | ||||||
|  	depends on PPC64 && IBMVIO |  | ||||||
| --- a/drivers/char/hw_random/Makefile |  | ||||||
| +++ b/drivers/char/hw_random/Makefile |  | ||||||
| @@ -22,7 +22,6 @@ obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939 |  | ||||||
|  obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o |  | ||||||
|  obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o |  | ||||||
|  obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o |  | ||||||
| -obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o |  | ||||||
|  obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o |  | ||||||
|  obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o |  | ||||||
|  obj-$(CONFIG_HW_RANDOM_EXYNOS)	+= exynos-rng.o |  | ||||||
| --- a/drivers/char/hw_random/ppc4xx-rng.c |  | ||||||
| +++ /dev/null |  | ||||||
| @@ -1,147 +0,0 @@ |  | ||||||
| -/* |  | ||||||
| - * Generic PowerPC 44x RNG driver |  | ||||||
| - * |  | ||||||
| - * Copyright 2011 IBM Corporation |  | ||||||
| - * |  | ||||||
| - * 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. |  | ||||||
| - */ |  | ||||||
| - |  | ||||||
| -#include <linux/module.h> |  | ||||||
| -#include <linux/kernel.h> |  | ||||||
| -#include <linux/platform_device.h> |  | ||||||
| -#include <linux/hw_random.h> |  | ||||||
| -#include <linux/delay.h> |  | ||||||
| -#include <linux/of_address.h> |  | ||||||
| -#include <linux/of_platform.h> |  | ||||||
| -#include <asm/io.h> |  | ||||||
| - |  | ||||||
| -#define PPC4XX_TRNG_DEV_CTRL 0x60080 |  | ||||||
| - |  | ||||||
| -#define PPC4XX_TRNGE 0x00020000 |  | ||||||
| -#define PPC4XX_TRNG_CTRL 0x0008 |  | ||||||
| -#define PPC4XX_TRNG_CTRL_DALM 0x20 |  | ||||||
| -#define PPC4XX_TRNG_STAT 0x0004 |  | ||||||
| -#define PPC4XX_TRNG_STAT_B 0x1 |  | ||||||
| -#define PPC4XX_TRNG_DATA 0x0000 |  | ||||||
| - |  | ||||||
| -#define MODULE_NAME "ppc4xx_rng" |  | ||||||
| - |  | ||||||
| -static int ppc4xx_rng_data_present(struct hwrng *rng, int wait) |  | ||||||
| -{ |  | ||||||
| -	void __iomem *rng_regs = (void __iomem *) rng->priv; |  | ||||||
| -	int busy, i, present = 0; |  | ||||||
| - |  | ||||||
| -	for (i = 0; i < 20; i++) { |  | ||||||
| -		busy = (in_le32(rng_regs + PPC4XX_TRNG_STAT) & PPC4XX_TRNG_STAT_B); |  | ||||||
| -		if (!busy || !wait) { |  | ||||||
| -			present = 1; |  | ||||||
| -			break; |  | ||||||
| -		} |  | ||||||
| -		udelay(10); |  | ||||||
| -	} |  | ||||||
| -	return present; |  | ||||||
| -} |  | ||||||
| - |  | ||||||
| -static int ppc4xx_rng_data_read(struct hwrng *rng, u32 *data) |  | ||||||
| -{ |  | ||||||
| -	void __iomem *rng_regs = (void __iomem *) rng->priv; |  | ||||||
| -	*data = in_le32(rng_regs + PPC4XX_TRNG_DATA); |  | ||||||
| -	return 4; |  | ||||||
| -} |  | ||||||
| - |  | ||||||
| -static int ppc4xx_rng_enable(int enable) |  | ||||||
| -{ |  | ||||||
| -	struct device_node *ctrl; |  | ||||||
| -	void __iomem *ctrl_reg; |  | ||||||
| -	int err = 0; |  | ||||||
| -	u32 val; |  | ||||||
| - |  | ||||||
| -	/* Find the main crypto device node and map it to turn the TRNG on */ |  | ||||||
| -	ctrl = of_find_compatible_node(NULL, NULL, "amcc,ppc4xx-crypto"); |  | ||||||
| -	if (!ctrl) |  | ||||||
| -		return -ENODEV; |  | ||||||
| - |  | ||||||
| -	ctrl_reg = of_iomap(ctrl, 0); |  | ||||||
| -	if (!ctrl_reg) { |  | ||||||
| -		err = -ENODEV; |  | ||||||
| -		goto out; |  | ||||||
| -	} |  | ||||||
| - |  | ||||||
| -	val = in_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL); |  | ||||||
| - |  | ||||||
| -	if (enable) |  | ||||||
| -		val |= PPC4XX_TRNGE; |  | ||||||
| -	else |  | ||||||
| -		val = val & ~PPC4XX_TRNGE; |  | ||||||
| - |  | ||||||
| -	out_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL, val); |  | ||||||
| -	iounmap(ctrl_reg); |  | ||||||
| - |  | ||||||
| -out: |  | ||||||
| -	of_node_put(ctrl); |  | ||||||
| - |  | ||||||
| -	return err; |  | ||||||
| -} |  | ||||||
| - |  | ||||||
| -static struct hwrng ppc4xx_rng = { |  | ||||||
| -	.name = MODULE_NAME, |  | ||||||
| -	.data_present = ppc4xx_rng_data_present, |  | ||||||
| -	.data_read = ppc4xx_rng_data_read, |  | ||||||
| -}; |  | ||||||
| - |  | ||||||
| -static int ppc4xx_rng_probe(struct platform_device *dev) |  | ||||||
| -{ |  | ||||||
| -	void __iomem *rng_regs; |  | ||||||
| -	int err = 0; |  | ||||||
| - |  | ||||||
| -	rng_regs = of_iomap(dev->dev.of_node, 0); |  | ||||||
| -	if (!rng_regs) |  | ||||||
| -		return -ENODEV; |  | ||||||
| - |  | ||||||
| -	err = ppc4xx_rng_enable(1); |  | ||||||
| -	if (err) |  | ||||||
| -		return err; |  | ||||||
| - |  | ||||||
| -	out_le32(rng_regs + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM); |  | ||||||
| -	ppc4xx_rng.priv = (unsigned long) rng_regs; |  | ||||||
| - |  | ||||||
| -	err = hwrng_register(&ppc4xx_rng); |  | ||||||
| - |  | ||||||
| -	return err; |  | ||||||
| -} |  | ||||||
| - |  | ||||||
| -static int ppc4xx_rng_remove(struct platform_device *dev) |  | ||||||
| -{ |  | ||||||
| -	void __iomem *rng_regs = (void __iomem *) ppc4xx_rng.priv; |  | ||||||
| - |  | ||||||
| -	hwrng_unregister(&ppc4xx_rng); |  | ||||||
| -	ppc4xx_rng_enable(0); |  | ||||||
| -	iounmap(rng_regs); |  | ||||||
| - |  | ||||||
| -	return 0; |  | ||||||
| -} |  | ||||||
| - |  | ||||||
| -static const struct of_device_id ppc4xx_rng_match[] = { |  | ||||||
| -	{ .compatible = "ppc4xx-rng", }, |  | ||||||
| -	{ .compatible = "amcc,ppc460ex-rng", }, |  | ||||||
| -	{ .compatible = "amcc,ppc440epx-rng", }, |  | ||||||
| -	{}, |  | ||||||
| -}; |  | ||||||
| -MODULE_DEVICE_TABLE(of, ppc4xx_rng_match); |  | ||||||
| - |  | ||||||
| -static struct platform_driver ppc4xx_rng_driver = { |  | ||||||
| -	.driver = { |  | ||||||
| -		.name = MODULE_NAME, |  | ||||||
| -		.of_match_table = ppc4xx_rng_match, |  | ||||||
| -	}, |  | ||||||
| -	.probe = ppc4xx_rng_probe, |  | ||||||
| -	.remove = ppc4xx_rng_remove, |  | ||||||
| -}; |  | ||||||
| - |  | ||||||
| -module_platform_driver(ppc4xx_rng_driver); |  | ||||||
| - |  | ||||||
| -MODULE_LICENSE("GPL"); |  | ||||||
| -MODULE_AUTHOR("Josh Boyer <jwboyer@linux.vnet.ibm.com>"); |  | ||||||
| -MODULE_DESCRIPTION("HW RNG driver for PPC 4xx processors"); |  | ||||||
| --- a/drivers/crypto/Kconfig |  | ||||||
| +++ b/drivers/crypto/Kconfig |  | ||||||
| @@ -277,6 +277,14 @@ config CRYPTO_DEV_PPC4XX |  | ||||||
|  	help |  | ||||||
|  	  This option allows you to have support for AMCC crypto acceleration. |  | ||||||
|   |  | ||||||
| +config HW_RANDOM_PPC4XX |  | ||||||
| +	bool "PowerPC 4xx generic true random number generator support" |  | ||||||
| +	depends on CRYPTO_DEV_PPC4XX && HW_RANDOM |  | ||||||
| +	default y |  | ||||||
| +	---help--- |  | ||||||
| +	 This option provides the kernel-side support for the TRNG hardware |  | ||||||
| +	 found in the security function of some PowerPC 4xx SoCs. |  | ||||||
| + |  | ||||||
|  config CRYPTO_DEV_OMAP_SHAM |  | ||||||
|  	tristate "Support for OMAP MD5/SHA1/SHA2 hw accelerator" |  | ||||||
|  	depends on ARCH_OMAP2PLUS |  | ||||||
| --- a/drivers/crypto/amcc/Makefile |  | ||||||
| +++ b/drivers/crypto/amcc/Makefile |  | ||||||
| @@ -1,2 +1,3 @@ |  | ||||||
|  obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += crypto4xx.o |  | ||||||
|  crypto4xx-y :=  crypto4xx_core.o crypto4xx_alg.o crypto4xx_sa.o |  | ||||||
| +crypto4xx-$(CONFIG_HW_RANDOM_PPC4XX) += crypto4xx_trng.o |  | ||||||
| --- a/drivers/crypto/amcc/crypto4xx_core.c |  | ||||||
| +++ b/drivers/crypto/amcc/crypto4xx_core.c |  | ||||||
| @@ -40,6 +40,7 @@ |  | ||||||
|  #include "crypto4xx_reg_def.h" |  | ||||||
|  #include "crypto4xx_core.h" |  | ||||||
|  #include "crypto4xx_sa.h" |  | ||||||
| +#include "crypto4xx_trng.h" |  | ||||||
|   |  | ||||||
|  #define PPC4XX_SEC_VERSION_STR			"0.5" |  | ||||||
|   |  | ||||||
| @@ -1221,6 +1222,7 @@ static int crypto4xx_probe(struct platfo |  | ||||||
|  	if (rc) |  | ||||||
|  		goto err_start_dev; |  | ||||||
|   |  | ||||||
| +	ppc4xx_trng_probe(core_dev); |  | ||||||
|  	return 0; |  | ||||||
|   |  | ||||||
|  err_start_dev: |  | ||||||
| @@ -1248,6 +1250,8 @@ static int crypto4xx_remove(struct platf |  | ||||||
|  	struct device *dev = &ofdev->dev; |  | ||||||
|  	struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev); |  | ||||||
|   |  | ||||||
| +	ppc4xx_trng_remove(core_dev); |  | ||||||
| + |  | ||||||
|  	free_irq(core_dev->irq, dev); |  | ||||||
|  	irq_dispose_mapping(core_dev->irq); |  | ||||||
|   |  | ||||||
| @@ -1268,7 +1272,7 @@ MODULE_DEVICE_TABLE(of, crypto4xx_match) |  | ||||||
|   |  | ||||||
|  static struct platform_driver crypto4xx_driver = { |  | ||||||
|  	.driver = { |  | ||||||
| -		.name = "crypto4xx", |  | ||||||
| +		.name = MODULE_NAME, |  | ||||||
|  		.of_match_table = crypto4xx_match, |  | ||||||
|  	}, |  | ||||||
|  	.probe		= crypto4xx_probe, |  | ||||||
| @@ -1280,4 +1284,3 @@ module_platform_driver(crypto4xx_driver) |  | ||||||
|  MODULE_LICENSE("GPL"); |  | ||||||
|  MODULE_AUTHOR("James Hsiao <jhsiao@amcc.com>"); |  | ||||||
|  MODULE_DESCRIPTION("Driver for AMCC PPC4xx crypto accelerator"); |  | ||||||
| - |  | ||||||
| --- a/drivers/crypto/amcc/crypto4xx_core.h |  | ||||||
| +++ b/drivers/crypto/amcc/crypto4xx_core.h |  | ||||||
| @@ -24,6 +24,8 @@ |  | ||||||
|   |  | ||||||
|  #include <crypto/internal/hash.h> |  | ||||||
|   |  | ||||||
| +#define MODULE_NAME "crypto4xx" |  | ||||||
| + |  | ||||||
|  #define PPC460SX_SDR0_SRST                      0x201 |  | ||||||
|  #define PPC405EX_SDR0_SRST                      0x200 |  | ||||||
|  #define PPC460EX_SDR0_SRST                      0x201 |  | ||||||
| @@ -72,6 +74,7 @@ struct crypto4xx_device { |  | ||||||
|  	char *name; |  | ||||||
|  	u64  ce_phy_address; |  | ||||||
|  	void __iomem *ce_base; |  | ||||||
| +	void __iomem *trng_base; |  | ||||||
|   |  | ||||||
|  	void *pdr;			/* base address of packet |  | ||||||
|  					descriptor ring */ |  | ||||||
| @@ -106,6 +109,7 @@ struct crypto4xx_core_device { |  | ||||||
|  	struct device *device; |  | ||||||
|  	struct platform_device *ofdev; |  | ||||||
|  	struct crypto4xx_device *dev; |  | ||||||
| +	struct hwrng *trng; |  | ||||||
|  	u32 int_status; |  | ||||||
|  	u32 irq; |  | ||||||
|  	struct tasklet_struct tasklet; |  | ||||||
| --- a/drivers/crypto/amcc/crypto4xx_reg_def.h |  | ||||||
| +++ b/drivers/crypto/amcc/crypto4xx_reg_def.h |  | ||||||
| @@ -125,6 +125,7 @@ |  | ||||||
|  #define PPC4XX_INTERRUPT_CLR			0x3ffff |  | ||||||
|  #define PPC4XX_PRNG_CTRL_AUTO_EN		0x3 |  | ||||||
|  #define PPC4XX_DC_3DES_EN			1 |  | ||||||
| +#define PPC4XX_TRNG_EN				0x00020000 |  | ||||||
|  #define PPC4XX_INT_DESCR_CNT			4 |  | ||||||
|  #define PPC4XX_INT_TIMEOUT_CNT			0 |  | ||||||
|  #define PPC4XX_INT_CFG				1 |  | ||||||
| --- /dev/null |  | ||||||
| +++ b/drivers/crypto/amcc/crypto4xx_trng.c |  | ||||||
| @@ -0,0 +1,131 @@ |  | ||||||
| +/* |  | ||||||
| + * Generic PowerPC 44x RNG driver |  | ||||||
| + * |  | ||||||
| + * Copyright 2011 IBM Corporation |  | ||||||
| + * |  | ||||||
| + * 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. |  | ||||||
| + */ |  | ||||||
| + |  | ||||||
| +#include <linux/module.h> |  | ||||||
| +#include <linux/kernel.h> |  | ||||||
| +#include <linux/interrupt.h> |  | ||||||
| +#include <linux/platform_device.h> |  | ||||||
| +#include <linux/hw_random.h> |  | ||||||
| +#include <linux/delay.h> |  | ||||||
| +#include <linux/of_address.h> |  | ||||||
| +#include <linux/of_platform.h> |  | ||||||
| +#include <linux/io.h> |  | ||||||
| + |  | ||||||
| +#include "crypto4xx_core.h" |  | ||||||
| +#include "crypto4xx_trng.h" |  | ||||||
| +#include "crypto4xx_reg_def.h" |  | ||||||
| + |  | ||||||
| +#define PPC4XX_TRNG_CTRL	0x0008 |  | ||||||
| +#define PPC4XX_TRNG_CTRL_DALM	0x20 |  | ||||||
| +#define PPC4XX_TRNG_STAT	0x0004 |  | ||||||
| +#define PPC4XX_TRNG_STAT_B	0x1 |  | ||||||
| +#define PPC4XX_TRNG_DATA	0x0000 |  | ||||||
| + |  | ||||||
| +static int ppc4xx_trng_data_present(struct hwrng *rng, int wait) |  | ||||||
| +{ |  | ||||||
| +	struct crypto4xx_device *dev = (void *)rng->priv; |  | ||||||
| +	int busy, i, present = 0; |  | ||||||
| + |  | ||||||
| +	for (i = 0; i < 20; i++) { |  | ||||||
| +		busy = (in_le32(dev->trng_base + PPC4XX_TRNG_STAT) & |  | ||||||
| +			PPC4XX_TRNG_STAT_B); |  | ||||||
| +		if (!busy || !wait) { |  | ||||||
| +			present = 1; |  | ||||||
| +			break; |  | ||||||
| +		} |  | ||||||
| +		udelay(10); |  | ||||||
| +	} |  | ||||||
| +	return present; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int ppc4xx_trng_data_read(struct hwrng *rng, u32 *data) |  | ||||||
| +{ |  | ||||||
| +	struct crypto4xx_device *dev = (void *)rng->priv; |  | ||||||
| +	*data = in_le32(dev->trng_base + PPC4XX_TRNG_DATA); |  | ||||||
| +	return 4; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static void ppc4xx_trng_enable(struct crypto4xx_device *dev, bool enable) |  | ||||||
| +{ |  | ||||||
| +	u32 device_ctrl; |  | ||||||
| + |  | ||||||
| +	device_ctrl = readl(dev->ce_base + CRYPTO4XX_DEVICE_CTRL); |  | ||||||
| +	if (enable) |  | ||||||
| +		device_ctrl |= PPC4XX_TRNG_EN; |  | ||||||
| +	else |  | ||||||
| +		device_ctrl &= ~PPC4XX_TRNG_EN; |  | ||||||
| +	writel(device_ctrl, dev->ce_base + CRYPTO4XX_DEVICE_CTRL); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static const struct of_device_id ppc4xx_trng_match[] = { |  | ||||||
| +	{ .compatible = "ppc4xx-rng", }, |  | ||||||
| +	{ .compatible = "amcc,ppc460ex-rng", }, |  | ||||||
| +	{ .compatible = "amcc,ppc440epx-rng", }, |  | ||||||
| +	{}, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +void ppc4xx_trng_probe(struct crypto4xx_core_device *core_dev) |  | ||||||
| +{ |  | ||||||
| +	struct crypto4xx_device *dev = core_dev->dev; |  | ||||||
| +	struct device_node *trng = NULL; |  | ||||||
| +	struct hwrng *rng = NULL; |  | ||||||
| +	int err; |  | ||||||
| + |  | ||||||
| +	/* Find the TRNG device node and map it */ |  | ||||||
| +	trng = of_find_matching_node(NULL, ppc4xx_trng_match); |  | ||||||
| +	if (!trng || !of_device_is_available(trng)) |  | ||||||
| +		return; |  | ||||||
| + |  | ||||||
| +	dev->trng_base = of_iomap(trng, 0); |  | ||||||
| +	of_node_put(trng); |  | ||||||
| +	if (!dev->trng_base) |  | ||||||
| +		goto err_out; |  | ||||||
| + |  | ||||||
| +	rng = kzalloc(sizeof(*rng), GFP_KERNEL); |  | ||||||
| +	if (!rng) |  | ||||||
| +		goto err_out; |  | ||||||
| + |  | ||||||
| +	rng->name = MODULE_NAME; |  | ||||||
| +	rng->data_present = ppc4xx_trng_data_present; |  | ||||||
| +	rng->data_read = ppc4xx_trng_data_read; |  | ||||||
| +	rng->priv = (unsigned long) dev; |  | ||||||
| +	core_dev->trng = rng; |  | ||||||
| +	ppc4xx_trng_enable(dev, true); |  | ||||||
| +	out_le32(dev->trng_base + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM); |  | ||||||
| +	err = devm_hwrng_register(core_dev->device, core_dev->trng); |  | ||||||
| +	if (err) { |  | ||||||
| +		ppc4xx_trng_enable(dev, false); |  | ||||||
| +		dev_err(core_dev->device, "failed to register hwrng (%d).\n", |  | ||||||
| +			err); |  | ||||||
| +		goto err_out; |  | ||||||
| +	} |  | ||||||
| +	return; |  | ||||||
| + |  | ||||||
| +err_out: |  | ||||||
| +	of_node_put(trng); |  | ||||||
| +	iounmap(dev->trng_base); |  | ||||||
| +	kfree(rng); |  | ||||||
| +	dev->trng_base = NULL; |  | ||||||
| +	core_dev->trng = NULL; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +void ppc4xx_trng_remove(struct crypto4xx_core_device *core_dev) |  | ||||||
| +{ |  | ||||||
| +	if (core_dev && core_dev->trng) { |  | ||||||
| +		struct crypto4xx_device *dev = core_dev->dev; |  | ||||||
| + |  | ||||||
| +		devm_hwrng_unregister(core_dev->device, core_dev->trng); |  | ||||||
| +		ppc4xx_trng_enable(dev, false); |  | ||||||
| +		iounmap(dev->trng_base); |  | ||||||
| +		kfree(core_dev->trng); |  | ||||||
| +	} |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +MODULE_ALIAS("ppc4xx_rng"); |  | ||||||
| --- /dev/null |  | ||||||
| +++ b/drivers/crypto/amcc/crypto4xx_trng.h |  | ||||||
| @@ -0,0 +1,34 @@ |  | ||||||
| +/** |  | ||||||
| + * AMCC SoC PPC4xx Crypto Driver |  | ||||||
| + * |  | ||||||
| + * Copyright (c) 2008 Applied Micro Circuits Corporation. |  | ||||||
| + * All rights reserved. James Hsiao <jhsiao@amcc.com> |  | ||||||
| + * |  | ||||||
| + * This program is free software; you can redistribute it and/or modify |  | ||||||
| + * it under the terms of the GNU General Public License as published by |  | ||||||
| + * the Free Software Foundation; either version 2 of the License, or |  | ||||||
| + * (at your option) any later version. |  | ||||||
| + * |  | ||||||
| + * This program is distributed in the hope that it will be useful, |  | ||||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| + * GNU General Public License for more details. |  | ||||||
| + * |  | ||||||
| + * This file defines the security context |  | ||||||
| + * associate format. |  | ||||||
| + */ |  | ||||||
| + |  | ||||||
| +#ifndef __CRYPTO4XX_TRNG_H__ |  | ||||||
| +#define __CRYPTO4XX_TRNG_H__ |  | ||||||
| + |  | ||||||
| +#ifdef CONFIG_HW_RANDOM_PPC4XX |  | ||||||
| +void ppc4xx_trng_probe(struct crypto4xx_core_device *core_dev); |  | ||||||
| +void ppc4xx_trng_remove(struct crypto4xx_core_device *core_dev); |  | ||||||
| +#else |  | ||||||
| +static inline void ppc4xx_trng_probe( |  | ||||||
| +	struct crypto4xx_device *dev __maybe_unused) { } |  | ||||||
| +static inline void ppc4xx_trng_remove( |  | ||||||
| +	struct crypto4xx_device *dev __maybe_unused) { } |  | ||||||
| +#endif |  | ||||||
| + |  | ||||||
| +#endif |  | ||||||
| @@ -1,57 +0,0 @@ | |||||||
| From 0c13957a43a90b1522eb616f3c9967ec44e4da1d Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Christian Lamparter <chunkeey@googlemail.com> |  | ||||||
| Date: Tue, 3 May 2016 13:58:24 +0200 |  | ||||||
| Subject: [PATCH] drivers: net: emac: add Atheros AR8035 phy initialization |  | ||||||
|  code |  | ||||||
| To: netdev@vger.kernel.org |  | ||||||
|  |  | ||||||
| This patch adds the phy initialization code for Qualcomm |  | ||||||
| Atheros AR8035 phy. This configuration is found in the |  | ||||||
| Cisco Meraki MR24. |  | ||||||
|  |  | ||||||
| Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> |  | ||||||
| --- |  | ||||||
|  drivers/net/ethernet/ibm/emac/phy.c | 26 ++++++++++++++++++++++++++ |  | ||||||
|  1 file changed, 26 insertions(+) |  | ||||||
|  |  | ||||||
| --- a/drivers/net/ethernet/ibm/emac/phy.c |  | ||||||
| +++ b/drivers/net/ethernet/ibm/emac/phy.c |  | ||||||
| @@ -470,12 +470,38 @@ static struct mii_phy_def m88e1112_phy_d |  | ||||||
|  	.ops		= &m88e1112_phy_ops, |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
| +static int ar8035_init(struct mii_phy *phy) |  | ||||||
| +{ |  | ||||||
| +	phy_write(phy, 0x1d, 0x5); /* Address debug register 5 */ |  | ||||||
| +	phy_write(phy, 0x1e, 0x2d47); /* Value copied from u-boot */ |  | ||||||
| +	phy_write(phy, 0x1d, 0xb);    /* Address hib ctrl */ |  | ||||||
| +	phy_write(phy, 0x1e, 0xbc20); /* Value copied from u-boot */ |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static struct mii_phy_ops ar8035_phy_ops = { |  | ||||||
| +	.init		= ar8035_init, |  | ||||||
| +	.setup_aneg	= genmii_setup_aneg, |  | ||||||
| +	.setup_forced	= genmii_setup_forced, |  | ||||||
| +	.poll_link	= genmii_poll_link, |  | ||||||
| +	.read_link	= genmii_read_link, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +static struct mii_phy_def ar8035_phy_def = { |  | ||||||
| +	.phy_id		= 0x004dd070, |  | ||||||
| +	.phy_id_mask	= 0xfffffff0, |  | ||||||
| +	.name		= "Atheros 8035 Gigabit Ethernet", |  | ||||||
| +	.ops		= &ar8035_phy_ops, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
|  static struct mii_phy_def *mii_phy_table[] = { |  | ||||||
|  	&et1011c_phy_def, |  | ||||||
|  	&cis8201_phy_def, |  | ||||||
|  	&bcm5248_phy_def, |  | ||||||
|  	&m88e1111_phy_def, |  | ||||||
|  	&m88e1112_phy_def, |  | ||||||
| +	&ar8035_phy_def, |  | ||||||
|  	&genmii_phy_def, |  | ||||||
|  	NULL |  | ||||||
|  }; |  | ||||||
| @@ -1,138 +0,0 @@ | |||||||
| From 7bd903c5ca47fde5ad52370a47776491813c772e Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Peter Ujfalusi <peter.ujfalusi@ti.com> |  | ||||||
| Date: Mon, 14 Dec 2015 22:47:39 +0200 |  | ||||||
| Subject: [PATCH 1/3] dmaengine: core: Move and merge the code paths using |  | ||||||
|  private_candidate |  | ||||||
|  |  | ||||||
| Channel matching with private_candidate() is used in two paths, the error |  | ||||||
| checking is slightly different in them and they are duplicating code also. |  | ||||||
| Move the code under find_candidate() to provide consistent execution and |  | ||||||
| going to allow us to reuse this mode of channel lookup later. |  | ||||||
|  |  | ||||||
| Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> |  | ||||||
| Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> |  | ||||||
| Reviewed-by: Arnd Bergmann <arnd@arndb.de> |  | ||||||
| Signed-off-by: Vinod Koul <vinod.koul@intel.com> |  | ||||||
| --- |  | ||||||
|  drivers/dma/dmaengine.c | 81 +++++++++++++++++++++++++------------------------ |  | ||||||
|  1 file changed, 42 insertions(+), 39 deletions(-) |  | ||||||
|  |  | ||||||
| --- a/drivers/dma/dmaengine.c |  | ||||||
| +++ b/drivers/dma/dmaengine.c |  | ||||||
| @@ -542,6 +542,42 @@ static struct dma_chan *private_candidat |  | ||||||
|  	return NULL; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| +static struct dma_chan *find_candidate(struct dma_device *device, |  | ||||||
| +				       const dma_cap_mask_t *mask, |  | ||||||
| +				       dma_filter_fn fn, void *fn_param) |  | ||||||
| +{ |  | ||||||
| +	struct dma_chan *chan = private_candidate(mask, device, fn, fn_param); |  | ||||||
| +	int err; |  | ||||||
| + |  | ||||||
| +	if (chan) { |  | ||||||
| +		/* Found a suitable channel, try to grab, prep, and return it. |  | ||||||
| +		 * We first set DMA_PRIVATE to disable balance_ref_count as this |  | ||||||
| +		 * channel will not be published in the general-purpose |  | ||||||
| +		 * allocator |  | ||||||
| +		 */ |  | ||||||
| +		dma_cap_set(DMA_PRIVATE, device->cap_mask); |  | ||||||
| +		device->privatecnt++; |  | ||||||
| +		err = dma_chan_get(chan); |  | ||||||
| + |  | ||||||
| +		if (err) { |  | ||||||
| +			if (err == -ENODEV) { |  | ||||||
| +				pr_debug("%s: %s module removed\n", __func__, |  | ||||||
| +					 dma_chan_name(chan)); |  | ||||||
| +				list_del_rcu(&device->global_node); |  | ||||||
| +			} else |  | ||||||
| +				pr_debug("%s: failed to get %s: (%d)\n", |  | ||||||
| +					 __func__, dma_chan_name(chan), err); |  | ||||||
| + |  | ||||||
| +			if (--device->privatecnt == 0) |  | ||||||
| +				dma_cap_clear(DMA_PRIVATE, device->cap_mask); |  | ||||||
| + |  | ||||||
| +			chan = ERR_PTR(err); |  | ||||||
| +		} |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	return chan ? chan : ERR_PTR(-EPROBE_DEFER); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
|  /** |  | ||||||
|   * dma_get_slave_channel - try to get specific channel exclusively |  | ||||||
|   * @chan: target channel |  | ||||||
| @@ -580,7 +616,6 @@ struct dma_chan *dma_get_any_slave_chann |  | ||||||
|  { |  | ||||||
|  	dma_cap_mask_t mask; |  | ||||||
|  	struct dma_chan *chan; |  | ||||||
| -	int err; |  | ||||||
|   |  | ||||||
|  	dma_cap_zero(mask); |  | ||||||
|  	dma_cap_set(DMA_SLAVE, mask); |  | ||||||
| @@ -588,23 +623,11 @@ struct dma_chan *dma_get_any_slave_chann |  | ||||||
|  	/* lock against __dma_request_channel */ |  | ||||||
|  	mutex_lock(&dma_list_mutex); |  | ||||||
|   |  | ||||||
| -	chan = private_candidate(&mask, device, NULL, NULL); |  | ||||||
| -	if (chan) { |  | ||||||
| -		dma_cap_set(DMA_PRIVATE, device->cap_mask); |  | ||||||
| -		device->privatecnt++; |  | ||||||
| -		err = dma_chan_get(chan); |  | ||||||
| -		if (err) { |  | ||||||
| -			pr_debug("%s: failed to get %s: (%d)\n", |  | ||||||
| -				__func__, dma_chan_name(chan), err); |  | ||||||
| -			chan = NULL; |  | ||||||
| -			if (--device->privatecnt == 0) |  | ||||||
| -				dma_cap_clear(DMA_PRIVATE, device->cap_mask); |  | ||||||
| -		} |  | ||||||
| -	} |  | ||||||
| +	chan = find_candidate(device, &mask, NULL, NULL); |  | ||||||
|   |  | ||||||
|  	mutex_unlock(&dma_list_mutex); |  | ||||||
|   |  | ||||||
| -	return chan; |  | ||||||
| +	return IS_ERR(chan) ? NULL : chan; |  | ||||||
|  } |  | ||||||
|  EXPORT_SYMBOL_GPL(dma_get_any_slave_channel); |  | ||||||
|   |  | ||||||
| @@ -621,35 +644,15 @@ struct dma_chan *__dma_request_channel(c |  | ||||||
|  { |  | ||||||
|  	struct dma_device *device, *_d; |  | ||||||
|  	struct dma_chan *chan = NULL; |  | ||||||
| -	int err; |  | ||||||
|   |  | ||||||
|  	/* Find a channel */ |  | ||||||
|  	mutex_lock(&dma_list_mutex); |  | ||||||
|  	list_for_each_entry_safe(device, _d, &dma_device_list, global_node) { |  | ||||||
| -		chan = private_candidate(mask, device, fn, fn_param); |  | ||||||
| -		if (chan) { |  | ||||||
| -			/* Found a suitable channel, try to grab, prep, and |  | ||||||
| -			 * return it.  We first set DMA_PRIVATE to disable |  | ||||||
| -			 * balance_ref_count as this channel will not be |  | ||||||
| -			 * published in the general-purpose allocator |  | ||||||
| -			 */ |  | ||||||
| -			dma_cap_set(DMA_PRIVATE, device->cap_mask); |  | ||||||
| -			device->privatecnt++; |  | ||||||
| -			err = dma_chan_get(chan); |  | ||||||
| +		chan = find_candidate(device, mask, fn, fn_param); |  | ||||||
| +		if (!IS_ERR(chan)) |  | ||||||
| +			break; |  | ||||||
|   |  | ||||||
| -			if (err == -ENODEV) { |  | ||||||
| -				pr_debug("%s: %s module removed\n", |  | ||||||
| -					 __func__, dma_chan_name(chan)); |  | ||||||
| -				list_del_rcu(&device->global_node); |  | ||||||
| -			} else if (err) |  | ||||||
| -				pr_debug("%s: failed to get %s: (%d)\n", |  | ||||||
| -					 __func__, dma_chan_name(chan), err); |  | ||||||
| -			else |  | ||||||
| -				break; |  | ||||||
| -			if (--device->privatecnt == 0) |  | ||||||
| -				dma_cap_clear(DMA_PRIVATE, device->cap_mask); |  | ||||||
| -			chan = NULL; |  | ||||||
| -		} |  | ||||||
| +		chan = NULL; |  | ||||||
|  	} |  | ||||||
|  	mutex_unlock(&dma_list_mutex); |  | ||||||
|   |  | ||||||
| @@ -1,335 +0,0 @@ | |||||||
| From a8135d0d79e9d0ad3a4ff494fceeaae838becf38 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Peter Ujfalusi <peter.ujfalusi@ti.com> |  | ||||||
| Date: Mon, 14 Dec 2015 22:47:40 +0200 |  | ||||||
| Subject: [PATCH 2/3] dmaengine: core: Introduce new, universal API to request |  | ||||||
|  a channel |  | ||||||
|  |  | ||||||
| The two API function can cover most, if not all current APIs used to |  | ||||||
| request a channel. With minimal effort dmaengine drivers, platforms and |  | ||||||
| dmaengine user drivers can be converted to use the two function. |  | ||||||
|  |  | ||||||
| struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); |  | ||||||
|  |  | ||||||
| To request any channel matching with the requested capabilities, can be |  | ||||||
| used to request channel for memcpy, memset, xor, etc where no hardware |  | ||||||
| synchronization is needed. |  | ||||||
|  |  | ||||||
| struct dma_chan *dma_request_chan(struct device *dev, const char *name); |  | ||||||
| To request a slave channel. The dma_request_chan() will try to find the |  | ||||||
| channel via DT, ACPI or in case if the kernel booted in non DT/ACPI mode |  | ||||||
| it will use a filter lookup table and retrieves the needed information from |  | ||||||
| the dma_slave_map provided by the DMA drivers. |  | ||||||
| This legacy mode needs changes in platform code, in dmaengine drivers and |  | ||||||
| finally the dmaengine user drivers can be converted: |  | ||||||
|  |  | ||||||
| For each dmaengine driver an array of DMA device, slave and the parameter |  | ||||||
| for the filter function needs to be added: |  | ||||||
|  |  | ||||||
| static const struct dma_slave_map da830_edma_map[] = { |  | ||||||
| 	{ "davinci-mcasp.0", "rx", EDMA_FILTER_PARAM(0, 0) }, |  | ||||||
| 	{ "davinci-mcasp.0", "tx", EDMA_FILTER_PARAM(0, 1) }, |  | ||||||
| 	{ "davinci-mcasp.1", "rx", EDMA_FILTER_PARAM(0, 2) }, |  | ||||||
| 	{ "davinci-mcasp.1", "tx", EDMA_FILTER_PARAM(0, 3) }, |  | ||||||
| 	{ "davinci-mcasp.2", "rx", EDMA_FILTER_PARAM(0, 4) }, |  | ||||||
| 	{ "davinci-mcasp.2", "tx", EDMA_FILTER_PARAM(0, 5) }, |  | ||||||
| 	{ "spi_davinci.0", "rx", EDMA_FILTER_PARAM(0, 14) }, |  | ||||||
| 	{ "spi_davinci.0", "tx", EDMA_FILTER_PARAM(0, 15) }, |  | ||||||
| 	{ "da830-mmc.0", "rx", EDMA_FILTER_PARAM(0, 16) }, |  | ||||||
| 	{ "da830-mmc.0", "tx", EDMA_FILTER_PARAM(0, 17) }, |  | ||||||
| 	{ "spi_davinci.1", "rx", EDMA_FILTER_PARAM(0, 18) }, |  | ||||||
| 	{ "spi_davinci.1", "tx", EDMA_FILTER_PARAM(0, 19) }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| This information is going to be needed by the dmaengine driver, so |  | ||||||
| modification to the platform_data is needed, and the driver map should be |  | ||||||
| added to the pdata of the DMA driver: |  | ||||||
|  |  | ||||||
| da8xx_edma0_pdata.slave_map = da830_edma_map; |  | ||||||
| da8xx_edma0_pdata.slavecnt = ARRAY_SIZE(da830_edma_map); |  | ||||||
|  |  | ||||||
| The DMA driver then needs to configure the needed device -> filter_fn |  | ||||||
| mapping before it registers with dma_async_device_register() : |  | ||||||
|  |  | ||||||
| ecc->dma_slave.filter_map.map = info->slave_map; |  | ||||||
| ecc->dma_slave.filter_map.mapcnt = info->slavecnt; |  | ||||||
| ecc->dma_slave.filter_map.fn = edma_filter_fn; |  | ||||||
|  |  | ||||||
| When neither DT or ACPI lookup is available the dma_request_chan() will |  | ||||||
| try to match the requester's device name with the filter_map's list of |  | ||||||
| device names, when a match found it will use the information from the |  | ||||||
| dma_slave_map to get the channel with the dma_get_channel() internal |  | ||||||
| function. |  | ||||||
|  |  | ||||||
| Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> |  | ||||||
| Reviewed-by: Arnd Bergmann <arnd@arndb.de> |  | ||||||
| Signed-off-by: Vinod Koul <vinod.koul@intel.com> |  | ||||||
| --- |  | ||||||
|  Documentation/dmaengine/client.txt | 23 +++------- |  | ||||||
|  drivers/dma/dmaengine.c            | 89 +++++++++++++++++++++++++++++++++----- |  | ||||||
|  include/linux/dmaengine.h          | 51 +++++++++++++++++++--- |  | ||||||
|  3 files changed, 127 insertions(+), 36 deletions(-) |  | ||||||
|  |  | ||||||
| --- a/Documentation/dmaengine/client.txt |  | ||||||
| +++ b/Documentation/dmaengine/client.txt |  | ||||||
| @@ -22,25 +22,14 @@ The slave DMA usage consists of followin |  | ||||||
|     Channel allocation is slightly different in the slave DMA context, |  | ||||||
|     client drivers typically need a channel from a particular DMA |  | ||||||
|     controller only and even in some cases a specific channel is desired. |  | ||||||
| -   To request a channel dma_request_channel() API is used. |  | ||||||
| +   To request a channel dma_request_chan() API is used. |  | ||||||
|   |  | ||||||
|     Interface: |  | ||||||
| -	struct dma_chan *dma_request_channel(dma_cap_mask_t mask, |  | ||||||
| -			dma_filter_fn filter_fn, |  | ||||||
| -			void *filter_param); |  | ||||||
| -   where dma_filter_fn is defined as: |  | ||||||
| -	typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param); |  | ||||||
| +	struct dma_chan *dma_request_chan(struct device *dev, const char *name); |  | ||||||
|   |  | ||||||
| -   The 'filter_fn' parameter is optional, but highly recommended for |  | ||||||
| -   slave and cyclic channels as they typically need to obtain a specific |  | ||||||
| -   DMA channel. |  | ||||||
| - |  | ||||||
| -   When the optional 'filter_fn' parameter is NULL, dma_request_channel() |  | ||||||
| -   simply returns the first channel that satisfies the capability mask. |  | ||||||
| - |  | ||||||
| -   Otherwise, the 'filter_fn' routine will be called once for each free |  | ||||||
| -   channel which has a capability in 'mask'.  'filter_fn' is expected to |  | ||||||
| -   return 'true' when the desired DMA channel is found. |  | ||||||
| +   Which will find and return the 'name' DMA channel associated with the 'dev' |  | ||||||
| +   device. The association is done via DT, ACPI or board file based |  | ||||||
| +   dma_slave_map matching table. |  | ||||||
|   |  | ||||||
|     A channel allocated via this interface is exclusive to the caller, |  | ||||||
|     until dma_release_channel() is called. |  | ||||||
| --- a/drivers/dma/dmaengine.c |  | ||||||
| +++ b/drivers/dma/dmaengine.c |  | ||||||
| @@ -43,6 +43,7 @@ |  | ||||||
|   |  | ||||||
|  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |  | ||||||
|   |  | ||||||
| +#include <linux/platform_device.h> |  | ||||||
|  #include <linux/dma-mapping.h> |  | ||||||
|  #include <linux/init.h> |  | ||||||
|  #include <linux/module.h> |  | ||||||
| @@ -665,27 +666,73 @@ struct dma_chan *__dma_request_channel(c |  | ||||||
|  } |  | ||||||
|  EXPORT_SYMBOL_GPL(__dma_request_channel); |  | ||||||
|   |  | ||||||
| +static const struct dma_slave_map *dma_filter_match(struct dma_device *device, |  | ||||||
| +						    const char *name, |  | ||||||
| +						    struct device *dev) |  | ||||||
| +{ |  | ||||||
| +	int i; |  | ||||||
| + |  | ||||||
| +	if (!device->filter.mapcnt) |  | ||||||
| +		return NULL; |  | ||||||
| + |  | ||||||
| +	for (i = 0; i < device->filter.mapcnt; i++) { |  | ||||||
| +		const struct dma_slave_map *map = &device->filter.map[i]; |  | ||||||
| + |  | ||||||
| +		if (!strcmp(map->devname, dev_name(dev)) && |  | ||||||
| +		    !strcmp(map->slave, name)) |  | ||||||
| +			return map; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	return NULL; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
|  /** |  | ||||||
| - * dma_request_slave_channel_reason - try to allocate an exclusive slave channel |  | ||||||
| + * dma_request_chan - try to allocate an exclusive slave channel |  | ||||||
|   * @dev:	pointer to client device structure |  | ||||||
|   * @name:	slave channel name |  | ||||||
|   * |  | ||||||
|   * Returns pointer to appropriate DMA channel on success or an error pointer. |  | ||||||
|   */ |  | ||||||
| -struct dma_chan *dma_request_slave_channel_reason(struct device *dev, |  | ||||||
| -						  const char *name) |  | ||||||
| +struct dma_chan *dma_request_chan(struct device *dev, const char *name) |  | ||||||
|  { |  | ||||||
| +	struct dma_device *d, *_d; |  | ||||||
| +	struct dma_chan *chan = NULL; |  | ||||||
| + |  | ||||||
|  	/* If device-tree is present get slave info from here */ |  | ||||||
|  	if (dev->of_node) |  | ||||||
| -		return of_dma_request_slave_channel(dev->of_node, name); |  | ||||||
| +		chan = of_dma_request_slave_channel(dev->of_node, name); |  | ||||||
|   |  | ||||||
|  	/* If device was enumerated by ACPI get slave info from here */ |  | ||||||
| -	if (ACPI_HANDLE(dev)) |  | ||||||
| -		return acpi_dma_request_slave_chan_by_name(dev, name); |  | ||||||
| +	if (has_acpi_companion(dev) && !chan) |  | ||||||
| +		chan = acpi_dma_request_slave_chan_by_name(dev, name); |  | ||||||
| + |  | ||||||
| +	if (chan) { |  | ||||||
| +		/* Valid channel found or requester need to be deferred */ |  | ||||||
| +		if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) |  | ||||||
| +			return chan; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	/* Try to find the channel via the DMA filter map(s) */ |  | ||||||
| +	mutex_lock(&dma_list_mutex); |  | ||||||
| +	list_for_each_entry_safe(d, _d, &dma_device_list, global_node) { |  | ||||||
| +		dma_cap_mask_t mask; |  | ||||||
| +		const struct dma_slave_map *map = dma_filter_match(d, name, dev); |  | ||||||
| + |  | ||||||
| +		if (!map) |  | ||||||
| +			continue; |  | ||||||
| + |  | ||||||
| +		dma_cap_zero(mask); |  | ||||||
| +		dma_cap_set(DMA_SLAVE, mask); |  | ||||||
|   |  | ||||||
| -	return ERR_PTR(-ENODEV); |  | ||||||
| +		chan = find_candidate(d, &mask, d->filter.fn, map->param); |  | ||||||
| +		if (!IS_ERR(chan)) |  | ||||||
| +			break; |  | ||||||
| +	} |  | ||||||
| +	mutex_unlock(&dma_list_mutex); |  | ||||||
| + |  | ||||||
| +	return chan ? chan : ERR_PTR(-EPROBE_DEFER); |  | ||||||
|  } |  | ||||||
| -EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason); |  | ||||||
| +EXPORT_SYMBOL_GPL(dma_request_chan); |  | ||||||
|   |  | ||||||
|  /** |  | ||||||
|   * dma_request_slave_channel - try to allocate an exclusive slave channel |  | ||||||
| @@ -697,17 +744,35 @@ EXPORT_SYMBOL_GPL(dma_request_slave_chan |  | ||||||
|  struct dma_chan *dma_request_slave_channel(struct device *dev, |  | ||||||
|  					   const char *name) |  | ||||||
|  { |  | ||||||
| -	struct dma_chan *ch = dma_request_slave_channel_reason(dev, name); |  | ||||||
| +	struct dma_chan *ch = dma_request_chan(dev, name); |  | ||||||
|  	if (IS_ERR(ch)) |  | ||||||
|  		return NULL; |  | ||||||
|   |  | ||||||
| -	dma_cap_set(DMA_PRIVATE, ch->device->cap_mask); |  | ||||||
| -	ch->device->privatecnt++; |  | ||||||
| - |  | ||||||
|  	return ch; |  | ||||||
|  } |  | ||||||
|  EXPORT_SYMBOL_GPL(dma_request_slave_channel); |  | ||||||
|   |  | ||||||
| +/** |  | ||||||
| + * dma_request_chan_by_mask - allocate a channel satisfying certain capabilities |  | ||||||
| + * @mask: capabilities that the channel must satisfy |  | ||||||
| + * |  | ||||||
| + * Returns pointer to appropriate DMA channel on success or an error pointer. |  | ||||||
| + */ |  | ||||||
| +struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask) |  | ||||||
| +{ |  | ||||||
| +	struct dma_chan *chan; |  | ||||||
| + |  | ||||||
| +	if (!mask) |  | ||||||
| +		return ERR_PTR(-ENODEV); |  | ||||||
| + |  | ||||||
| +	chan = __dma_request_channel(mask, NULL, NULL); |  | ||||||
| +	if (!chan) |  | ||||||
| +		chan = ERR_PTR(-ENODEV); |  | ||||||
| + |  | ||||||
| +	return chan; |  | ||||||
| +} |  | ||||||
| +EXPORT_SYMBOL_GPL(dma_request_chan_by_mask); |  | ||||||
| + |  | ||||||
|  void dma_release_channel(struct dma_chan *chan) |  | ||||||
|  { |  | ||||||
|  	mutex_lock(&dma_list_mutex); |  | ||||||
| --- a/include/linux/dmaengine.h |  | ||||||
| +++ b/include/linux/dmaengine.h |  | ||||||
| @@ -607,11 +607,38 @@ enum dmaengine_alignment { |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
|  /** |  | ||||||
| + * struct dma_slave_map - associates slave device and it's slave channel with |  | ||||||
| + * parameter to be used by a filter function |  | ||||||
| + * @devname: name of the device |  | ||||||
| + * @slave: slave channel name |  | ||||||
| + * @param: opaque parameter to pass to struct dma_filter.fn |  | ||||||
| + */ |  | ||||||
| +struct dma_slave_map { |  | ||||||
| +	const char *devname; |  | ||||||
| +	const char *slave; |  | ||||||
| +	void *param; |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +/** |  | ||||||
| + * struct dma_filter - information for slave device/channel to filter_fn/param |  | ||||||
| + * mapping |  | ||||||
| + * @fn: filter function callback |  | ||||||
| + * @mapcnt: number of slave device/channel in the map |  | ||||||
| + * @map: array of channel to filter mapping data |  | ||||||
| + */ |  | ||||||
| +struct dma_filter { |  | ||||||
| +	dma_filter_fn fn; |  | ||||||
| +	int mapcnt; |  | ||||||
| +	const struct dma_slave_map *map; |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +/** |  | ||||||
|   * struct dma_device - info on the entity supplying DMA services |  | ||||||
|   * @chancnt: how many DMA channels are supported |  | ||||||
|   * @privatecnt: how many DMA channels are requested by dma_request_channel |  | ||||||
|   * @channels: the list of struct dma_chan |  | ||||||
|   * @global_node: list_head for global dma_device_list |  | ||||||
| + * @filter: information for device/slave to filter function/param mapping |  | ||||||
|   * @cap_mask: one or more dma_capability flags |  | ||||||
|   * @max_xor: maximum number of xor sources, 0 if no capability |  | ||||||
|   * @max_pq: maximum number of PQ sources and PQ-continue capability |  | ||||||
| @@ -666,6 +693,7 @@ struct dma_device { |  | ||||||
|  	unsigned int privatecnt; |  | ||||||
|  	struct list_head channels; |  | ||||||
|  	struct list_head global_node; |  | ||||||
| +	struct dma_filter filter; |  | ||||||
|  	dma_cap_mask_t  cap_mask; |  | ||||||
|  	unsigned short max_xor; |  | ||||||
|  	unsigned short max_pq; |  | ||||||
| @@ -1140,9 +1168,11 @@ enum dma_status dma_wait_for_async_tx(st |  | ||||||
|  void dma_issue_pending_all(void); |  | ||||||
|  struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, |  | ||||||
|  					dma_filter_fn fn, void *fn_param); |  | ||||||
| -struct dma_chan *dma_request_slave_channel_reason(struct device *dev, |  | ||||||
| -						  const char *name); |  | ||||||
|  struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); |  | ||||||
| + |  | ||||||
| +struct dma_chan *dma_request_chan(struct device *dev, const char *name); |  | ||||||
| +struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); |  | ||||||
| + |  | ||||||
|  void dma_release_channel(struct dma_chan *chan); |  | ||||||
|  int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); |  | ||||||
|  #else |  | ||||||
| @@ -1166,16 +1196,21 @@ static inline struct dma_chan *__dma_req |  | ||||||
|  { |  | ||||||
|  	return NULL; |  | ||||||
|  } |  | ||||||
| -static inline struct dma_chan *dma_request_slave_channel_reason( |  | ||||||
| -					struct device *dev, const char *name) |  | ||||||
| -{ |  | ||||||
| -	return ERR_PTR(-ENODEV); |  | ||||||
| -} |  | ||||||
|  static inline struct dma_chan *dma_request_slave_channel(struct device *dev, |  | ||||||
|  							 const char *name) |  | ||||||
|  { |  | ||||||
|  	return NULL; |  | ||||||
|  } |  | ||||||
| +static inline struct dma_chan *dma_request_chan(struct device *dev, |  | ||||||
| +						const char *name) |  | ||||||
| +{ |  | ||||||
| +	return ERR_PTR(-ENODEV); |  | ||||||
| +} |  | ||||||
| +static inline struct dma_chan *dma_request_chan_by_mask( |  | ||||||
| +						const dma_cap_mask_t *mask) |  | ||||||
| +{ |  | ||||||
| +	return ERR_PTR(-ENODEV); |  | ||||||
| +} |  | ||||||
|  static inline void dma_release_channel(struct dma_chan *chan) |  | ||||||
|  { |  | ||||||
|  } |  | ||||||
| @@ -1186,6 +1221,8 @@ static inline int dma_get_slave_caps(str |  | ||||||
|  } |  | ||||||
|  #endif |  | ||||||
|   |  | ||||||
| +#define dma_request_slave_channel_reason(dev, name) dma_request_chan(dev, name) |  | ||||||
| + |  | ||||||
|  static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx) |  | ||||||
|  { |  | ||||||
|  	struct dma_slave_caps caps; |  | ||||||
| @@ -1,282 +0,0 @@ | |||||||
| From b36f09c3c441a6e59eab9315032e7d546571de3f Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Lars-Peter Clausen <lars@metafoo.de> |  | ||||||
| Date: Tue, 20 Oct 2015 11:46:28 +0200 |  | ||||||
| Subject: [PATCH] dmaengine: Add transfer termination synchronization support |  | ||||||
|  |  | ||||||
| The DMAengine API has a long standing race condition that is inherent to |  | ||||||
| the API itself. Calling dmaengine_terminate_all() is supposed to stop and |  | ||||||
| abort any pending or active transfers that have previously been submitted. |  | ||||||
| Unfortunately it is possible that this operation races against a currently |  | ||||||
| running (or with some drivers also scheduled) completion callback. |  | ||||||
|  |  | ||||||
| Since the API allows dmaengine_terminate_all() to be called from atomic |  | ||||||
| context as well as from within a completion callback it is not possible to |  | ||||||
| synchronize to the execution of the completion callback from within |  | ||||||
| dmaengine_terminate_all() itself. |  | ||||||
|  |  | ||||||
| This means that a user of the DMAengine API does not know when it is safe |  | ||||||
| to free resources used in the completion callback, which can result in a |  | ||||||
| use-after-free race condition. |  | ||||||
|  |  | ||||||
| This patch addresses the issue by introducing an explicit synchronization |  | ||||||
| primitive to the DMAengine API called dmaengine_synchronize(). |  | ||||||
|  |  | ||||||
| The existing dmaengine_terminate_all() is deprecated in favor of |  | ||||||
| dmaengine_terminate_sync() and dmaengine_terminate_async(). The former |  | ||||||
| aborts all pending and active transfers and synchronizes to the current |  | ||||||
| context, meaning it will wait until all running completion callbacks have |  | ||||||
| finished. This means it is only possible to call this function from |  | ||||||
| non-atomic context. The later function does not synchronize, but can still |  | ||||||
| be used in atomic context or from within a complete callback. It has to be |  | ||||||
| followed up by dmaengine_synchronize() before a client can free the |  | ||||||
| resources used in a completion callback. |  | ||||||
|  |  | ||||||
| In addition to this the semantics of the device_terminate_all() callback |  | ||||||
| are slightly relaxed by this patch. It is now OK for a driver to only |  | ||||||
| schedule the termination of the active transfer, but does not necessarily |  | ||||||
| have to wait until the DMA controller has completely stopped. The driver |  | ||||||
| must ensure though that the controller has stopped and no longer accesses |  | ||||||
| any memory when the device_synchronize() callback returns. |  | ||||||
|  |  | ||||||
| This was in part done since most drivers do not pay attention to this |  | ||||||
| anyway at the moment and to emphasize that this needs to be done when the |  | ||||||
| device_synchronize() callback is implemented. But it also helps with |  | ||||||
| implementing support for devices where stopping the controller can require |  | ||||||
| operations that may sleep. |  | ||||||
|  |  | ||||||
| Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> |  | ||||||
| Signed-off-by: Vinod Koul <vinod.koul@intel.com> |  | ||||||
| --- |  | ||||||
|  Documentation/dmaengine/client.txt   | 38 ++++++++++++++- |  | ||||||
|  Documentation/dmaengine/provider.txt | 20 +++++++- |  | ||||||
|  drivers/dma/dmaengine.c              |  5 +- |  | ||||||
|  include/linux/dmaengine.h            | 90 ++++++++++++++++++++++++++++++++++++ |  | ||||||
|  4 files changed, 148 insertions(+), 5 deletions(-) |  | ||||||
|  |  | ||||||
| --- a/Documentation/dmaengine/client.txt |  | ||||||
| +++ b/Documentation/dmaengine/client.txt |  | ||||||
| @@ -117,7 +117,7 @@ The slave DMA usage consists of followin |  | ||||||
|  	transaction. |  | ||||||
|   |  | ||||||
|  	For cyclic DMA, a callback function may wish to terminate the |  | ||||||
| -	DMA via dmaengine_terminate_all(). |  | ||||||
| +	DMA via dmaengine_terminate_async(). |  | ||||||
|   |  | ||||||
|  	Therefore, it is important that DMA engine drivers drop any |  | ||||||
|  	locks before calling the callback function which may cause a |  | ||||||
| @@ -155,12 +155,29 @@ The slave DMA usage consists of followin |  | ||||||
|   |  | ||||||
|  Further APIs: |  | ||||||
|   |  | ||||||
| -1. int dmaengine_terminate_all(struct dma_chan *chan) |  | ||||||
| +1. int dmaengine_terminate_sync(struct dma_chan *chan) |  | ||||||
| +   int dmaengine_terminate_async(struct dma_chan *chan) |  | ||||||
| +   int dmaengine_terminate_all(struct dma_chan *chan) /* DEPRECATED */ |  | ||||||
|   |  | ||||||
|     This causes all activity for the DMA channel to be stopped, and may |  | ||||||
|     discard data in the DMA FIFO which hasn't been fully transferred. |  | ||||||
|     No callback functions will be called for any incomplete transfers. |  | ||||||
|   |  | ||||||
| +   Two variants of this function are available. |  | ||||||
| + |  | ||||||
| +   dmaengine_terminate_async() might not wait until the DMA has been fully |  | ||||||
| +   stopped or until any running complete callbacks have finished. But it is |  | ||||||
| +   possible to call dmaengine_terminate_async() from atomic context or from |  | ||||||
| +   within a complete callback. dmaengine_synchronize() must be called before it |  | ||||||
| +   is safe to free the memory accessed by the DMA transfer or free resources |  | ||||||
| +   accessed from within the complete callback. |  | ||||||
| + |  | ||||||
| +   dmaengine_terminate_sync() will wait for the transfer and any running |  | ||||||
| +   complete callbacks to finish before it returns. But the function must not be |  | ||||||
| +   called from atomic context or from within a complete callback. |  | ||||||
| + |  | ||||||
| +   dmaengine_terminate_all() is deprecated and should not be used in new code. |  | ||||||
| + |  | ||||||
|  2. int dmaengine_pause(struct dma_chan *chan) |  | ||||||
|   |  | ||||||
|     This pauses activity on the DMA channel without data loss. |  | ||||||
| @@ -186,3 +203,20 @@ Further APIs: |  | ||||||
|  	a running DMA channel.  It is recommended that DMA engine users |  | ||||||
|  	pause or stop (via dmaengine_terminate_all()) the channel before |  | ||||||
|  	using this API. |  | ||||||
| + |  | ||||||
| +5. void dmaengine_synchronize(struct dma_chan *chan) |  | ||||||
| + |  | ||||||
| +  Synchronize the termination of the DMA channel to the current context. |  | ||||||
| + |  | ||||||
| +  This function should be used after dmaengine_terminate_async() to synchronize |  | ||||||
| +  the termination of the DMA channel to the current context. The function will |  | ||||||
| +  wait for the transfer and any running complete callbacks to finish before it |  | ||||||
| +  returns. |  | ||||||
| + |  | ||||||
| +  If dmaengine_terminate_async() is used to stop the DMA channel this function |  | ||||||
| +  must be called before it is safe to free memory accessed by previously |  | ||||||
| +  submitted descriptors or to free any resources accessed within the complete |  | ||||||
| +  callback of previously submitted descriptors. |  | ||||||
| + |  | ||||||
| +  The behavior of this function is undefined if dma_async_issue_pending() has |  | ||||||
| +  been called between dmaengine_terminate_async() and this function. |  | ||||||
| --- a/Documentation/dmaengine/provider.txt |  | ||||||
| +++ b/Documentation/dmaengine/provider.txt |  | ||||||
| @@ -327,8 +327,24 @@ supported. |  | ||||||
|   |  | ||||||
|     * device_terminate_all |  | ||||||
|       - Aborts all the pending and ongoing transfers on the channel |  | ||||||
| -     - This command should operate synchronously on the channel, |  | ||||||
| -       terminating right away all the channels |  | ||||||
| +     - For aborted transfers the complete callback should not be called |  | ||||||
| +     - Can be called from atomic context or from within a complete |  | ||||||
| +       callback of a descriptor. Must not sleep. Drivers must be able |  | ||||||
| +       to handle this correctly. |  | ||||||
| +     - Termination may be asynchronous. The driver does not have to |  | ||||||
| +       wait until the currently active transfer has completely stopped. |  | ||||||
| +       See device_synchronize. |  | ||||||
| + |  | ||||||
| +   * device_synchronize |  | ||||||
| +     - Must synchronize the termination of a channel to the current |  | ||||||
| +       context. |  | ||||||
| +     - Must make sure that memory for previously submitted |  | ||||||
| +       descriptors is no longer accessed by the DMA controller. |  | ||||||
| +     - Must make sure that all complete callbacks for previously |  | ||||||
| +       submitted descriptors have finished running and none are |  | ||||||
| +       scheduled to run. |  | ||||||
| +     - May sleep. |  | ||||||
| + |  | ||||||
|   |  | ||||||
|  Misc notes (stuff that should be documented, but don't really know |  | ||||||
|  where to put them) |  | ||||||
| --- a/drivers/dma/dmaengine.c |  | ||||||
| +++ b/drivers/dma/dmaengine.c |  | ||||||
| @@ -266,8 +266,11 @@ static void dma_chan_put(struct dma_chan |  | ||||||
|  	module_put(dma_chan_to_owner(chan)); |  | ||||||
|   |  | ||||||
|  	/* This channel is not in use anymore, free it */ |  | ||||||
| -	if (!chan->client_count && chan->device->device_free_chan_resources) |  | ||||||
| +	if (!chan->client_count && chan->device->device_free_chan_resources) { |  | ||||||
| +		/* Make sure all operations have completed */ |  | ||||||
| +		dmaengine_synchronize(chan); |  | ||||||
|  		chan->device->device_free_chan_resources(chan); |  | ||||||
| +	} |  | ||||||
|   |  | ||||||
|  	/* If the channel is used via a DMA request router, free the mapping */ |  | ||||||
|  	if (chan->router && chan->router->route_free) { |  | ||||||
| --- a/include/linux/dmaengine.h |  | ||||||
| +++ b/include/linux/dmaengine.h |  | ||||||
| @@ -681,6 +681,8 @@ struct dma_filter { |  | ||||||
|   *	paused. Returns 0 or an error code |  | ||||||
|   * @device_terminate_all: Aborts all transfers on a channel. Returns 0 |  | ||||||
|   *	or an error code |  | ||||||
| + * @device_synchronize: Synchronizes the termination of a transfers to the |  | ||||||
| + *  current context. |  | ||||||
|   * @device_tx_status: poll for transaction completion, the optional |  | ||||||
|   *	txstate parameter can be supplied with a pointer to get a |  | ||||||
|   *	struct with auxiliary transfer status information, otherwise the call |  | ||||||
| @@ -765,6 +767,7 @@ struct dma_device { |  | ||||||
|  	int (*device_pause)(struct dma_chan *chan); |  | ||||||
|  	int (*device_resume)(struct dma_chan *chan); |  | ||||||
|  	int (*device_terminate_all)(struct dma_chan *chan); |  | ||||||
| +	void (*device_synchronize)(struct dma_chan *chan); |  | ||||||
|   |  | ||||||
|  	enum dma_status (*device_tx_status)(struct dma_chan *chan, |  | ||||||
|  					    dma_cookie_t cookie, |  | ||||||
| @@ -856,6 +859,13 @@ static inline struct dma_async_tx_descri |  | ||||||
|  			src_sg, src_nents, flags); |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| +/** |  | ||||||
| + * dmaengine_terminate_all() - Terminate all active DMA transfers |  | ||||||
| + * @chan: The channel for which to terminate the transfers |  | ||||||
| + * |  | ||||||
| + * This function is DEPRECATED use either dmaengine_terminate_sync() or |  | ||||||
| + * dmaengine_terminate_async() instead. |  | ||||||
| + */ |  | ||||||
|  static inline int dmaengine_terminate_all(struct dma_chan *chan) |  | ||||||
|  { |  | ||||||
|  	if (chan->device->device_terminate_all) |  | ||||||
| @@ -864,6 +874,86 @@ static inline int dmaengine_terminate_al |  | ||||||
|  	return -ENOSYS; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| +/** |  | ||||||
| + * dmaengine_terminate_async() - Terminate all active DMA transfers |  | ||||||
| + * @chan: The channel for which to terminate the transfers |  | ||||||
| + * |  | ||||||
| + * Calling this function will terminate all active and pending descriptors |  | ||||||
| + * that have previously been submitted to the channel. It is not guaranteed |  | ||||||
| + * though that the transfer for the active descriptor has stopped when the |  | ||||||
| + * function returns. Furthermore it is possible the complete callback of a |  | ||||||
| + * submitted transfer is still running when this function returns. |  | ||||||
| + * |  | ||||||
| + * dmaengine_synchronize() needs to be called before it is safe to free |  | ||||||
| + * any memory that is accessed by previously submitted descriptors or before |  | ||||||
| + * freeing any resources accessed from within the completion callback of any |  | ||||||
| + * perviously submitted descriptors. |  | ||||||
| + * |  | ||||||
| + * This function can be called from atomic context as well as from within a |  | ||||||
| + * complete callback of a descriptor submitted on the same channel. |  | ||||||
| + * |  | ||||||
| + * If none of the two conditions above apply consider using |  | ||||||
| + * dmaengine_terminate_sync() instead. |  | ||||||
| + */ |  | ||||||
| +static inline int dmaengine_terminate_async(struct dma_chan *chan) |  | ||||||
| +{ |  | ||||||
| +	if (chan->device->device_terminate_all) |  | ||||||
| +		return chan->device->device_terminate_all(chan); |  | ||||||
| + |  | ||||||
| +	return -EINVAL; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +/** |  | ||||||
| + * dmaengine_synchronize() - Synchronize DMA channel termination |  | ||||||
| + * @chan: The channel to synchronize |  | ||||||
| + * |  | ||||||
| + * Synchronizes to the DMA channel termination to the current context. When this |  | ||||||
| + * function returns it is guaranteed that all transfers for previously issued |  | ||||||
| + * descriptors have stopped and and it is safe to free the memory assoicated |  | ||||||
| + * with them. Furthermore it is guaranteed that all complete callback functions |  | ||||||
| + * for a previously submitted descriptor have finished running and it is safe to |  | ||||||
| + * free resources accessed from within the complete callbacks. |  | ||||||
| + * |  | ||||||
| + * The behavior of this function is undefined if dma_async_issue_pending() has |  | ||||||
| + * been called between dmaengine_terminate_async() and this function. |  | ||||||
| + * |  | ||||||
| + * This function must only be called from non-atomic context and must not be |  | ||||||
| + * called from within a complete callback of a descriptor submitted on the same |  | ||||||
| + * channel. |  | ||||||
| + */ |  | ||||||
| +static inline void dmaengine_synchronize(struct dma_chan *chan) |  | ||||||
| +{ |  | ||||||
| +	if (chan->device->device_synchronize) |  | ||||||
| +		chan->device->device_synchronize(chan); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +/** |  | ||||||
| + * dmaengine_terminate_sync() - Terminate all active DMA transfers |  | ||||||
| + * @chan: The channel for which to terminate the transfers |  | ||||||
| + * |  | ||||||
| + * Calling this function will terminate all active and pending transfers |  | ||||||
| + * that have previously been submitted to the channel. It is similar to |  | ||||||
| + * dmaengine_terminate_async() but guarantees that the DMA transfer has actually |  | ||||||
| + * stopped and that all complete callbacks have finished running when the |  | ||||||
| + * function returns. |  | ||||||
| + * |  | ||||||
| + * This function must only be called from non-atomic context and must not be |  | ||||||
| + * called from within a complete callback of a descriptor submitted on the same |  | ||||||
| + * channel. |  | ||||||
| + */ |  | ||||||
| +static inline int dmaengine_terminate_sync(struct dma_chan *chan) |  | ||||||
| +{ |  | ||||||
| +	int ret; |  | ||||||
| + |  | ||||||
| +	ret = dmaengine_terminate_async(chan); |  | ||||||
| +	if (ret) |  | ||||||
| +		return ret; |  | ||||||
| + |  | ||||||
| +	dmaengine_synchronize(chan); |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
|  static inline int dmaengine_pause(struct dma_chan *chan) |  | ||||||
|  { |  | ||||||
|  	if (chan->device->device_pause) |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,209 +0,0 @@ | |||||||
| From eb25cb9956cc9384b7fa0d75dec908c9fac8c444 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Stephan Linz <linz@li-pro.net> |  | ||||||
| Date: Fri, 10 Jun 2016 07:59:56 +0200 |  | ||||||
| Subject: [PATCH] leds: convert IDE trigger to common disk trigger |  | ||||||
| MIME-Version: 1.0 |  | ||||||
| Content-Type: text/plain; charset=UTF-8 |  | ||||||
| Content-Transfer-Encoding: 8bit |  | ||||||
|  |  | ||||||
| This patch converts the IDE specific LED trigger to a generic disk |  | ||||||
| activity LED trigger. The libata core is now a trigger source just |  | ||||||
| like before the IDE disk driver. It's merely a replacement of the |  | ||||||
| string ide by disk. |  | ||||||
|  |  | ||||||
| The patch is taken from http://dev.gentoo.org/~josejx/ata.patch and is |  | ||||||
| widely used by any ibook/powerbook owners with great satisfaction. |  | ||||||
| Likewise, it is very often used successfully on different ARM platforms. |  | ||||||
|  |  | ||||||
| Unlike the original patch, the existing 'ide-disk' trigger is still |  | ||||||
| available for backward compatibility. That reduce the amount of patches |  | ||||||
| in affected device trees out of the mainline kernel. For further |  | ||||||
| development, the new name 'disk-activity' should be used. |  | ||||||
|  |  | ||||||
| Cc: Joseph Jezak <josejx@gentoo.org> |  | ||||||
| Cc: Jörg Sommer <joerg@alea.gnuu.de> |  | ||||||
| Cc: Richard Purdie <rpurdie@rpsys.net> |  | ||||||
| Signed-off-by: Stephan Linz <linz@li-pro.net> |  | ||||||
| Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com> |  | ||||||
| --- |  | ||||||
|  drivers/ata/libata-core.c               |  4 ++++ |  | ||||||
|  drivers/ide/ide-disk.c                  |  2 +- |  | ||||||
|  drivers/leds/trigger/Kconfig            |  8 +++---- |  | ||||||
|  drivers/leds/trigger/Makefile           |  2 +- |  | ||||||
|  drivers/leds/trigger/ledtrig-disk.c     | 41 +++++++++++++++++++++++++++++++++ |  | ||||||
|  drivers/leds/trigger/ledtrig-ide-disk.c | 36 ----------------------------- |  | ||||||
|  include/linux/leds.h                    |  6 ++--- |  | ||||||
|  8 files changed, 55 insertions(+), 46 deletions(-) |  | ||||||
|  create mode 100644 drivers/leds/trigger/ledtrig-disk.c |  | ||||||
|  delete mode 100644 drivers/leds/trigger/ledtrig-ide-disk.c |  | ||||||
|  |  | ||||||
| --- a/drivers/ata/libata-core.c |  | ||||||
| +++ b/drivers/ata/libata-core.c |  | ||||||
| @@ -67,6 +67,7 @@ |  | ||||||
|  #include <asm/byteorder.h> |  | ||||||
|  #include <linux/cdrom.h> |  | ||||||
|  #include <linux/ratelimit.h> |  | ||||||
| +#include <linux/leds.h> |  | ||||||
|  #include <linux/pm_runtime.h> |  | ||||||
|  #include <linux/platform_device.h> |  | ||||||
|   |  | ||||||
| @@ -4914,6 +4915,9 @@ void ata_qc_complete(struct ata_queued_c |  | ||||||
|  { |  | ||||||
|  	struct ata_port *ap = qc->ap; |  | ||||||
|   |  | ||||||
| +	/* Trigger the LED (if available) */ |  | ||||||
| +	ledtrig_disk_activity(); |  | ||||||
| + |  | ||||||
|  	/* XXX: New EH and old EH use different mechanisms to |  | ||||||
|  	 * synchronize EH with regular execution path. |  | ||||||
|  	 * |  | ||||||
| --- a/drivers/ide/ide-disk.c |  | ||||||
| +++ b/drivers/ide/ide-disk.c |  | ||||||
| @@ -186,7 +186,7 @@ static ide_startstop_t ide_do_rw_disk(id |  | ||||||
|  	BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED); |  | ||||||
|  	BUG_ON(rq->cmd_type != REQ_TYPE_FS); |  | ||||||
|   |  | ||||||
| -	ledtrig_ide_activity(); |  | ||||||
| +	ledtrig_disk_activity(); |  | ||||||
|   |  | ||||||
|  	pr_debug("%s: %sing: block=%llu, sectors=%u\n", |  | ||||||
|  		 drive->name, rq_data_dir(rq) == READ ? "read" : "writ", |  | ||||||
| --- a/drivers/leds/trigger/Kconfig |  | ||||||
| +++ b/drivers/leds/trigger/Kconfig |  | ||||||
| @@ -33,12 +33,12 @@ config LEDS_TRIGGER_ONESHOT |  | ||||||
|   |  | ||||||
|  	  If unsure, say Y. |  | ||||||
|   |  | ||||||
| -config LEDS_TRIGGER_IDE_DISK |  | ||||||
| -	bool "LED IDE Disk Trigger" |  | ||||||
| -	depends on IDE_GD_ATA |  | ||||||
| +config LEDS_TRIGGER_DISK |  | ||||||
| +	bool "LED Disk Trigger" |  | ||||||
| +	depends on IDE_GD_ATA || ATA |  | ||||||
|  	depends on LEDS_TRIGGERS |  | ||||||
|  	help |  | ||||||
| -	  This allows LEDs to be controlled by IDE disk activity. |  | ||||||
| +	  This allows LEDs to be controlled by disk activity. |  | ||||||
|  	  If unsure, say Y. |  | ||||||
|   |  | ||||||
|  config LEDS_TRIGGER_HEARTBEAT |  | ||||||
| --- a/drivers/leds/trigger/Makefile |  | ||||||
| +++ b/drivers/leds/trigger/Makefile |  | ||||||
| @@ -1,6 +1,6 @@ |  | ||||||
|  obj-$(CONFIG_LEDS_TRIGGER_TIMER)	+= ledtrig-timer.o |  | ||||||
|  obj-$(CONFIG_LEDS_TRIGGER_ONESHOT)	+= ledtrig-oneshot.o |  | ||||||
| -obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)	+= ledtrig-ide-disk.o |  | ||||||
| +obj-$(CONFIG_LEDS_TRIGGER_DISK)		+= ledtrig-disk.o |  | ||||||
|  obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o |  | ||||||
|  obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o |  | ||||||
|  obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o |  | ||||||
| --- /dev/null |  | ||||||
| +++ b/drivers/leds/trigger/ledtrig-disk.c |  | ||||||
| @@ -0,0 +1,41 @@ |  | ||||||
| +/* |  | ||||||
| + * LED Disk Activity Trigger |  | ||||||
| + * |  | ||||||
| + * Copyright 2006 Openedhand Ltd. |  | ||||||
| + * |  | ||||||
| + * Author: Richard Purdie <rpurdie@openedhand.com> |  | ||||||
| + * |  | ||||||
| + * 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 <linux/kernel.h> |  | ||||||
| +#include <linux/init.h> |  | ||||||
| +#include <linux/leds.h> |  | ||||||
| + |  | ||||||
| +#define BLINK_DELAY 30 |  | ||||||
| + |  | ||||||
| +DEFINE_LED_TRIGGER(ledtrig_disk); |  | ||||||
| +DEFINE_LED_TRIGGER(ledtrig_ide); |  | ||||||
| + |  | ||||||
| +void ledtrig_disk_activity(void) |  | ||||||
| +{ |  | ||||||
| +	unsigned long blink_delay = BLINK_DELAY; |  | ||||||
| + |  | ||||||
| +	led_trigger_blink_oneshot(ledtrig_disk, |  | ||||||
| +				  &blink_delay, &blink_delay, 0); |  | ||||||
| +	led_trigger_blink_oneshot(ledtrig_ide, |  | ||||||
| +				  &blink_delay, &blink_delay, 0); |  | ||||||
| +} |  | ||||||
| +EXPORT_SYMBOL(ledtrig_disk_activity); |  | ||||||
| + |  | ||||||
| +static int __init ledtrig_disk_init(void) |  | ||||||
| +{ |  | ||||||
| +	led_trigger_register_simple("disk-activity", &ledtrig_disk); |  | ||||||
| +	led_trigger_register_simple("ide-disk", &ledtrig_ide); |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| +device_initcall(ledtrig_disk_init); |  | ||||||
| --- a/drivers/leds/trigger/ledtrig-ide-disk.c |  | ||||||
| +++ /dev/null |  | ||||||
| @@ -1,47 +0,0 @@ |  | ||||||
| -/* |  | ||||||
| - * LED IDE-Disk Activity Trigger |  | ||||||
| - * |  | ||||||
| - * Copyright 2006 Openedhand Ltd. |  | ||||||
| - * |  | ||||||
| - * Author: Richard Purdie <rpurdie@openedhand.com> |  | ||||||
| - * |  | ||||||
| - * 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 <linux/module.h> |  | ||||||
| -#include <linux/kernel.h> |  | ||||||
| -#include <linux/init.h> |  | ||||||
| -#include <linux/leds.h> |  | ||||||
| - |  | ||||||
| -#define BLINK_DELAY 30 |  | ||||||
| - |  | ||||||
| -DEFINE_LED_TRIGGER(ledtrig_ide); |  | ||||||
| -static unsigned long ide_blink_delay = BLINK_DELAY; |  | ||||||
| - |  | ||||||
| -void ledtrig_ide_activity(void) |  | ||||||
| -{ |  | ||||||
| -	led_trigger_blink_oneshot(ledtrig_ide, |  | ||||||
| -				  &ide_blink_delay, &ide_blink_delay, 0); |  | ||||||
| -} |  | ||||||
| -EXPORT_SYMBOL(ledtrig_ide_activity); |  | ||||||
| - |  | ||||||
| -static int __init ledtrig_ide_init(void) |  | ||||||
| -{ |  | ||||||
| -	led_trigger_register_simple("ide-disk", &ledtrig_ide); |  | ||||||
| -	return 0; |  | ||||||
| -} |  | ||||||
| - |  | ||||||
| -static void __exit ledtrig_ide_exit(void) |  | ||||||
| -{ |  | ||||||
| -	led_trigger_unregister_simple(ledtrig_ide); |  | ||||||
| -} |  | ||||||
| - |  | ||||||
| -module_init(ledtrig_ide_init); |  | ||||||
| -module_exit(ledtrig_ide_exit); |  | ||||||
| - |  | ||||||
| -MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); |  | ||||||
| -MODULE_DESCRIPTION("LED IDE Disk Activity Trigger"); |  | ||||||
| -MODULE_LICENSE("GPL"); |  | ||||||
| --- a/include/linux/leds.h |  | ||||||
| +++ b/include/linux/leds.h |  | ||||||
| @@ -308,10 +308,10 @@ static inline void *led_get_trigger_data |  | ||||||
|  #endif /* CONFIG_LEDS_TRIGGERS */ |  | ||||||
|   |  | ||||||
|  /* Trigger specific functions */ |  | ||||||
| -#ifdef CONFIG_LEDS_TRIGGER_IDE_DISK |  | ||||||
| -extern void ledtrig_ide_activity(void); |  | ||||||
| +#ifdef CONFIG_LEDS_TRIGGER_DISK |  | ||||||
| +extern void ledtrig_disk_activity(void); |  | ||||||
|  #else |  | ||||||
| -static inline void ledtrig_ide_activity(void) {} |  | ||||||
| +static inline void ledtrig_disk_activity(void) {} |  | ||||||
|  #endif |  | ||||||
|   |  | ||||||
|  #if defined(CONFIG_LEDS_TRIGGER_CAMERA) || defined(CONFIG_LEDS_TRIGGER_CAMERA_MODULE) |  | ||||||
| @@ -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" |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
| @@ -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", |  | ||||||
| @@ -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 |  | ||||||
| @@ -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" |  | ||||||
|  }; |  | ||||||
| @@ -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) | |  | ||||||
| @@ -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); |  | ||||||
|   |  | ||||||
| @@ -1,52 +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 |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| @@ -2587,7 +2596,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_460EX_PHY_CLK_FIX); |  | ||||||
| +					  EMAC_FTR_APM821XX_PHY_CLK_FIX); |  | ||||||
|  		} |  | ||||||
|  	} else if (of_device_is_compatible(np, "ibm,emac4")) { |  | ||||||
|  		dev->features |= EMAC_FTR_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. |  | ||||||
| @@ -1,328 +0,0 @@ | |||||||
| From 59b394d0d2b2e11687e757820c52d6470d15a9c5 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Christian Lamparter <chunkeey@gmail.com> |  | ||||||
| Date: Mon, 13 Jun 2016 15:42:21 +0200 |  | ||||||
| Subject: [PATCH] phy device tree support for emac |  | ||||||
|  |  | ||||||
| --- |  | ||||||
|  drivers/net/ethernet/ibm/emac/core.c | 261 +++++++++++++++++++++++++++++++++++ |  | ||||||
|  drivers/net/ethernet/ibm/emac/core.h |   4 + |  | ||||||
|  2 files changed, 265 insertions(+) |  | ||||||
|  |  | ||||||
| --- a/drivers/net/ethernet/ibm/emac/core.c |  | ||||||
| +++ b/drivers/net/ethernet/ibm/emac/core.c |  | ||||||
| @@ -42,6 +42,7 @@ |  | ||||||
|  #include <linux/of_address.h> |  | ||||||
|  #include <linux/of_irq.h> |  | ||||||
|  #include <linux/of_net.h> |  | ||||||
| +#include <linux/of_mdio.h> |  | ||||||
|  #include <linux/slab.h> |  | ||||||
|   |  | ||||||
|  #include <asm/processor.h> |  | ||||||
| @@ -2392,6 +2393,246 @@ 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); |  | ||||||
| + |  | ||||||
| +	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->ndev->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 void emac_dt_phy_mdio_cleanup(struct emac_instance *dev) |  | ||||||
| +{ |  | ||||||
| +	if (dev->mii_bus) { |  | ||||||
| +		if (dev->mii_bus->state == MDIOBUS_REGISTERED) |  | ||||||
| +			mdiobus_unregister(dev->mii_bus); |  | ||||||
| +		mdiobus_free(dev->mii_bus); |  | ||||||
| +		dev->mii_bus = NULL; |  | ||||||
| +	} |  | ||||||
| +	kfree(dev->phy.def); |  | ||||||
| +	dev->phy.def = NULL; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +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->ndev->dev, "no mdio definition found."); |  | ||||||
| +		return -ENODEV; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	if (!of_device_is_available(mii_np)) { |  | ||||||
| +		res = 1; |  | ||||||
| +		goto err_put_node; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	dev->mii_bus = mdiobus_alloc(); |  | ||||||
| +	if (!dev->mii_bus) { |  | ||||||
| +		res = -ENOMEM; |  | ||||||
| +		goto err_cleanup_mdio; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	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->mii_bus->name); |  | ||||||
| + |  | ||||||
| +	res = of_mdiobus_register(dev->mii_bus, mii_np); |  | ||||||
| +	if (res) { |  | ||||||
| +		dev_err(&dev->ndev->dev, "cannot register MDIO bus %s (%d)", |  | ||||||
| +			dev->mii_bus->name, res); |  | ||||||
| +		goto err_cleanup_mdio; |  | ||||||
| +	} |  | ||||||
| +	of_node_put(mii_np); |  | ||||||
| +	return 0; |  | ||||||
| + |  | ||||||
| + err_cleanup_mdio: |  | ||||||
| +	emac_dt_phy_mdio_cleanup(dev); |  | ||||||
| + err_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 = kzalloc(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->ndev->dev, "failed to connect to PHY."); |  | ||||||
| +		kfree(dev->phy.dev); |  | ||||||
| +		dev->phy.dev = NULL; |  | ||||||
| +		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->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) |  | ||||||
| +			goto out; |  | ||||||
| + |  | ||||||
| +		res = emac_dt_phy_probe(dev, phy_handle); |  | ||||||
| +		if (res) { |  | ||||||
| +			emac_dt_phy_mdio_cleanup(dev); |  | ||||||
| +			goto out; |  | ||||||
| +		} |  | ||||||
| + |  | ||||||
| +		return 1; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| + out: |  | ||||||
| +	of_node_put(phy_handle); |  | ||||||
| +	/* if no phy device was specifie 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; |  | ||||||
| @@ -2462,6 +2703,18 @@ 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) |  | ||||||
| +			goto init_phy; |  | ||||||
| +		if (res < 0) |  | ||||||
| +			dev_err(&dev->ndev->dev, "failed to attach dt phy (%d).", |  | ||||||
| +				res); |  | ||||||
| + |  | ||||||
| +		/* continue with old code */ |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
|  	if (dev->phy_address != 0xffffffff) |  | ||||||
|  		phy_map = ~(1 << dev->phy_address); |  | ||||||
|   |  | ||||||
| @@ -2489,6 +2742,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); |  | ||||||
| @@ -2907,6 +3161,8 @@ static int emac_probe(struct platform_de |  | ||||||
|  	/* I have a bad feeling about this ... */ |  | ||||||
|   |  | ||||||
|   err_detach_tah: |  | ||||||
| +	emac_dt_phy_mdio_cleanup(dev); |  | ||||||
| + |  | ||||||
|  	if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) |  | ||||||
|  		tah_detach(dev->tah_dev, dev->tah_port); |  | ||||||
|   err_detach_rgmii: |  | ||||||
| @@ -2957,6 +3213,11 @@ 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); |  | ||||||
| + |  | ||||||
| +	emac_dt_phy_mdio_cleanup(dev); |  | ||||||
| + |  | ||||||
|  	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; |  | ||||||
| @@ -1,545 +0,0 @@ | |||||||
| From 419992bae5aaa4e06402e0b7c79fcf7bcb6b4764 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Christian Lamparter <chunkeey@googlemail.com> |  | ||||||
| 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 <yoshihiro.shimoda.uh@renesas.com> |  | ||||||
| Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> |  | ||||||
| --- |  | ||||||
|  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 <linux/slab.h> |  | ||||||
|  #include <linux/module.h> |  | ||||||
|  #include <linux/acpi.h> |  | ||||||
| +#include <linux/firmware.h> |  | ||||||
| +#include <asm/unaligned.h> |  | ||||||
|   |  | ||||||
|  #include "xhci.h" |  | ||||||
|  #include "xhci-trace.h" |  | ||||||
| @@ -218,6 +220,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) |  | ||||||
|  { |  | ||||||
| @@ -257,6 +711,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 */ |  | ||||||
| @@ -314,6 +784,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) { |  | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| From a0dc613140bab907a3d5787a7ae7b0638bf674d0 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Christian Lamparter <chunkeey@gmail.com> |  | ||||||
| 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 |  | ||||||
| @@ -185,7 +185,7 @@ static void xhci_pci_quirks(struct devic |  | ||||||
|  	} |  | ||||||
|  	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 |  | ||||||
| @@ -388,10 +388,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 |  | ||||||
| @@ -1652,6 +1652,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; |  | ||||||
| @@ -1,48 +0,0 @@ | |||||||
| --- a/drivers/usb/dwc2/platform.c |  | ||||||
| +++ b/drivers/usb/dwc2/platform.c |  | ||||||
| @@ -115,6 +115,37 @@ static const struct dwc2_core_params par |  | ||||||
|  	.hibernation			= -1, |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
| +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); |  | ||||||
| @@ -307,6 +338,7 @@ static int dwc2_driver_remove(struct pla |  | ||||||
|  } |  | ||||||
|   |  | ||||||
|  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 = "rockchip,rk3066-usb", .data = ¶ms_rk3066 }, |  | ||||||
|  	{ .compatible = "snps,dwc2", .data = NULL }, |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,292 +0,0 @@ | |||||||
| From: Wei Ni <wni@nvidia.com> |  | ||||||
| Subject: hwmon: lm90: split set and show temp as common codes |  | ||||||
|  |  | ||||||
| Split set and show temp codes as common functions, so we can use |  | ||||||
| it directly when implement linux thermal framework. |  | ||||||
| And handle error return value for the lm90_select_remote_channel |  | ||||||
| and write_tempx, then set_temp8 and set_temp11 could return it |  | ||||||
| to user-space. |  | ||||||
|  |  | ||||||
| Discussed in: |  | ||||||
| http://www.spinics.net/lists/linux-tegra/msg14020.html . |  | ||||||
| Applied with Jean. |  | ||||||
|  |  | ||||||
| BUG=chrome-os-partner:30834 |  | ||||||
| TEST=None |  | ||||||
|  |  | ||||||
| Signed-off-by: Wei Ni <wni@nvidia.com> |  | ||||||
| Signed-off-by: Jean Delvare <khali@linux-fr.org> |  | ||||||
| Reviewed-on: https://chromium-review.googlesource.com/175114 |  | ||||||
| Tested-by: Wei Ni <wni.nvidia@gmail.com> |  | ||||||
| Commit-Queue: Dylan Reid <dgreid@chromium.org> |  | ||||||
| Reviewed-by: Dylan Reid <dgreid@chromium.org> |  | ||||||
| (cherry picked from commit 614a96decdc7a3784128c9f21c5471367e2c627d) |  | ||||||
| Change-Id: Idbe3948812c6737cba94810cd147c29cc527c3cf |  | ||||||
| Reviewed-on: https://chromium-review.googlesource.com/212413 |  | ||||||
| Reviewed-by: Olof Johansson <olofj@chromium.org> |  | ||||||
| Commit-Queue: Olof Johansson <olofj@chromium.org> |  | ||||||
| --- |  | ||||||
| --- a/drivers/hwmon/lm90.c |  | ||||||
| +++ b/drivers/hwmon/lm90.c |  | ||||||
| @@ -473,20 +473,29 @@ static int lm90_read16(struct i2c_client |  | ||||||
|   * various registers have different meanings as a result of selecting a |  | ||||||
|   * non-default remote channel. |  | ||||||
|   */ |  | ||||||
| -static inline void lm90_select_remote_channel(struct i2c_client *client, |  | ||||||
| -					      struct lm90_data *data, |  | ||||||
| -					      int channel) |  | ||||||
| +static inline int lm90_select_remote_channel(struct i2c_client *client, |  | ||||||
| +					     struct lm90_data *data, |  | ||||||
| +					     int channel) |  | ||||||
|  { |  | ||||||
|  	u8 config; |  | ||||||
| +	int err; |  | ||||||
|   |  | ||||||
|  	if (data->kind == max6696) { |  | ||||||
|  		lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); |  | ||||||
|  		config &= ~0x08; |  | ||||||
|  		if (channel) |  | ||||||
|  			config |= 0x08; |  | ||||||
| -		i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, |  | ||||||
| -					  config); |  | ||||||
| +		err = i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, |  | ||||||
| +						config); |  | ||||||
| +		if (err < 0) { |  | ||||||
| +			dev_err(&client->dev, |  | ||||||
| +				"Failed to select remote channel %d, err %d\n", |  | ||||||
| +				channel, err); |  | ||||||
| +			return err; |  | ||||||
| +		} |  | ||||||
|  	} |  | ||||||
| + |  | ||||||
| +	return 0; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
|  /* |  | ||||||
| @@ -759,29 +768,34 @@ static u16 temp_to_u16_adt7461(struct lm |  | ||||||
|   * Sysfs stuff |  | ||||||
|   */ |  | ||||||
|   |  | ||||||
| -static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, |  | ||||||
| -			  char *buf) |  | ||||||
| +static int read_temp8(struct device *dev, int index) |  | ||||||
|  { |  | ||||||
| -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |  | ||||||
|  	struct lm90_data *data = lm90_update_device(dev); |  | ||||||
|  	int temp; |  | ||||||
|   |  | ||||||
|  	if (data->kind == adt7461 || data->kind == tmp451) |  | ||||||
| -		temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); |  | ||||||
| +		temp = temp_from_u8_adt7461(data, data->temp8[index]); |  | ||||||
|  	else if (data->kind == max6646) |  | ||||||
| -		temp = temp_from_u8(data->temp8[attr->index]); |  | ||||||
| +		temp = temp_from_u8(data->temp8[index]); |  | ||||||
|  	else |  | ||||||
| -		temp = temp_from_s8(data->temp8[attr->index]); |  | ||||||
| +		temp = temp_from_s8(data->temp8[index]); |  | ||||||
|   |  | ||||||
|  	/* +16 degrees offset for temp2 for the LM99 */ |  | ||||||
| -	if (data->kind == lm99 && attr->index == 3) |  | ||||||
| +	if (data->kind == lm99 && index == 3) |  | ||||||
|  		temp += 16000; |  | ||||||
|   |  | ||||||
| -	return sprintf(buf, "%d\n", temp); |  | ||||||
| +	return temp; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| -static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, |  | ||||||
| -			 const char *buf, size_t count) |  | ||||||
| +static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, |  | ||||||
| +			  char *buf) |  | ||||||
| +{ |  | ||||||
| +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |  | ||||||
| + |  | ||||||
| +	return sprintf(buf, "%d\n", read_temp8(dev, attr->index)); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int write_temp8(struct device *dev, int index, long val) |  | ||||||
|  { |  | ||||||
|  	static const u8 reg[TEMP8_REG_NUM] = { |  | ||||||
|  		LM90_REG_W_LOCAL_LOW, |  | ||||||
| @@ -794,60 +808,79 @@ static ssize_t set_temp8(struct device * |  | ||||||
|  		MAX6659_REG_W_REMOTE_EMERG, |  | ||||||
|  	}; |  | ||||||
|   |  | ||||||
| -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |  | ||||||
|  	struct lm90_data *data = dev_get_drvdata(dev); |  | ||||||
|  	struct i2c_client *client = data->client; |  | ||||||
| -	int nr = attr->index; |  | ||||||
| -	long val; |  | ||||||
|  	int err; |  | ||||||
|   |  | ||||||
| -	err = kstrtol(buf, 10, &val); |  | ||||||
| -	if (err < 0) |  | ||||||
| -		return err; |  | ||||||
| - |  | ||||||
|  	/* +16 degrees offset for temp2 for the LM99 */ |  | ||||||
| -	if (data->kind == lm99 && attr->index == 3) |  | ||||||
| +	if (data->kind == lm99 && index == 3) |  | ||||||
|  		val -= 16000; |  | ||||||
|   |  | ||||||
|  	mutex_lock(&data->update_lock); |  | ||||||
|  	if (data->kind == adt7461 || data->kind == tmp451) |  | ||||||
| -		data->temp8[nr] = temp_to_u8_adt7461(data, val); |  | ||||||
| +		data->temp8[index] = temp_to_u8_adt7461(data, val); |  | ||||||
|  	else if (data->kind == max6646) |  | ||||||
| -		data->temp8[nr] = temp_to_u8(val); |  | ||||||
| +		data->temp8[index] = temp_to_u8(val); |  | ||||||
|  	else |  | ||||||
| -		data->temp8[nr] = temp_to_s8(val); |  | ||||||
| - |  | ||||||
| -	lm90_select_remote_channel(client, data, nr >= 6); |  | ||||||
| -	i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); |  | ||||||
| -	lm90_select_remote_channel(client, data, 0); |  | ||||||
| +		data->temp8[index] = temp_to_s8(val); |  | ||||||
|   |  | ||||||
| +	if ((err = lm90_select_remote_channel(client, data, index >= 6)) || |  | ||||||
| +	    (err = i2c_smbus_write_byte_data(client, reg[index], |  | ||||||
| +					     data->temp8[index])) || |  | ||||||
| +	    (err = lm90_select_remote_channel(client, data, 0))) |  | ||||||
| +		dev_err(dev, "write_temp8 failed, err %d\n", err); |  | ||||||
|  	mutex_unlock(&data->update_lock); |  | ||||||
| + |  | ||||||
| +	return err; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, |  | ||||||
| +			 const char *buf, size_t count) |  | ||||||
| +{ |  | ||||||
| +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |  | ||||||
| +	int index = attr->index; |  | ||||||
| +	long val; |  | ||||||
| +	int err; |  | ||||||
| + |  | ||||||
| +	err = kstrtol(buf, 10, &val); |  | ||||||
| +	if (err < 0) |  | ||||||
| +		return err; |  | ||||||
| + |  | ||||||
| +	err = write_temp8(dev, index, val); |  | ||||||
| +	if (err < 0) |  | ||||||
| +		return err; |  | ||||||
| + |  | ||||||
|  	return count; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| -static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, |  | ||||||
| -			   char *buf) |  | ||||||
| +static int read_temp11(struct device *dev, int index) |  | ||||||
|  { |  | ||||||
| -	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); |  | ||||||
|  	struct lm90_data *data = lm90_update_device(dev); |  | ||||||
|  	int temp; |  | ||||||
|   |  | ||||||
|  	if (data->kind == adt7461 || data->kind == tmp451) |  | ||||||
| -		temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); |  | ||||||
| +		temp = temp_from_u16_adt7461(data, data->temp11[index]); |  | ||||||
|  	else if (data->kind == max6646) |  | ||||||
| -		temp = temp_from_u16(data->temp11[attr->index]); |  | ||||||
| +		temp = temp_from_u16(data->temp11[index]); |  | ||||||
|  	else |  | ||||||
| -		temp = temp_from_s16(data->temp11[attr->index]); |  | ||||||
| +		temp = temp_from_s16(data->temp11[index]); |  | ||||||
|   |  | ||||||
|  	/* +16 degrees offset for temp2 for the LM99 */ |  | ||||||
| -	if (data->kind == lm99 &&  attr->index <= 2) |  | ||||||
| +	if (data->kind == lm99 && index <= 2) |  | ||||||
|  		temp += 16000; |  | ||||||
|   |  | ||||||
| -	return sprintf(buf, "%d\n", temp); |  | ||||||
| +	return temp; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| -static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, |  | ||||||
| -			  const char *buf, size_t count) |  | ||||||
| +static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, |  | ||||||
| +			   char *buf) |  | ||||||
| +{ |  | ||||||
| +	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); |  | ||||||
| + |  | ||||||
| +	return sprintf(buf, "%d\n", read_temp11(dev, attr->index)); |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int write_temp11(struct device *dev, int nr, int index, long val) |  | ||||||
|  { |  | ||||||
|  	struct { |  | ||||||
|  		u8 high; |  | ||||||
| @@ -861,18 +894,10 @@ static ssize_t set_temp11(struct device |  | ||||||
|  		{ LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 } |  | ||||||
|  	}; |  | ||||||
|   |  | ||||||
| -	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); |  | ||||||
|  	struct lm90_data *data = dev_get_drvdata(dev); |  | ||||||
|  	struct i2c_client *client = data->client; |  | ||||||
| -	int nr = attr->nr; |  | ||||||
| -	int index = attr->index; |  | ||||||
| -	long val; |  | ||||||
|  	int err; |  | ||||||
|   |  | ||||||
| -	err = kstrtol(buf, 10, &val); |  | ||||||
| -	if (err < 0) |  | ||||||
| -		return err; |  | ||||||
| - |  | ||||||
|  	/* +16 degrees offset for temp2 for the LM99 */ |  | ||||||
|  	if (data->kind == lm99 && index <= 2) |  | ||||||
|  		val -= 16000; |  | ||||||
| @@ -887,15 +912,50 @@ static ssize_t set_temp11(struct device |  | ||||||
|  	else |  | ||||||
|  		data->temp11[index] = temp_to_s8(val) << 8; |  | ||||||
|   |  | ||||||
| -	lm90_select_remote_channel(client, data, reg[nr].channel); |  | ||||||
| -	i2c_smbus_write_byte_data(client, reg[nr].high, |  | ||||||
| -				  data->temp11[index] >> 8); |  | ||||||
| -	if (data->flags & LM90_HAVE_REM_LIMIT_EXT) |  | ||||||
| -		i2c_smbus_write_byte_data(client, reg[nr].low, |  | ||||||
| -					  data->temp11[index] & 0xff); |  | ||||||
| -	lm90_select_remote_channel(client, data, 0); |  | ||||||
| +	err = lm90_select_remote_channel(client, data, reg[nr].channel); |  | ||||||
| +	if (err) |  | ||||||
| +		goto error; |  | ||||||
| + |  | ||||||
| +	err = i2c_smbus_write_byte_data(client, reg[nr].high, |  | ||||||
| +					data->temp11[index] >> 8); |  | ||||||
| +	if (err) |  | ||||||
| +		goto error; |  | ||||||
| + |  | ||||||
| +	if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { |  | ||||||
| +		err = i2c_smbus_write_byte_data(client, reg[nr].low, |  | ||||||
| +						data->temp11[index] & 0xff); |  | ||||||
| +		if (err) |  | ||||||
| +			goto error; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
| +	err = lm90_select_remote_channel(client, data, 0); |  | ||||||
| + |  | ||||||
| +error: |  | ||||||
| +	if (err) |  | ||||||
| +		dev_err(dev, "write_temp11 failed, err %d\n", err); |  | ||||||
|   |  | ||||||
|  	mutex_unlock(&data->update_lock); |  | ||||||
| + |  | ||||||
| +	return err; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, |  | ||||||
| +			  const char *buf, size_t count) |  | ||||||
| +{ |  | ||||||
| +	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); |  | ||||||
| +	int nr = attr->nr; |  | ||||||
| +	int index = attr->index; |  | ||||||
| +	long val; |  | ||||||
| +	int err; |  | ||||||
| + |  | ||||||
| +	err = kstrtol(buf, 10, &val); |  | ||||||
| +	if (err < 0) |  | ||||||
| +		return err; |  | ||||||
| + |  | ||||||
| +	err = write_temp11(dev, nr, index, val); |  | ||||||
| +	if (err < 0) |  | ||||||
| +		return err; |  | ||||||
| + |  | ||||||
|  	return count; |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| @@ -1,149 +0,0 @@ | |||||||
| From: Wei Ni <wni@nvidia.com> |  | ||||||
| Subject: hwmon: lm90: expose to thermal fw via DT nodes |  | ||||||
|  |  | ||||||
| This patch adds to lm90 temperature sensor the possibility |  | ||||||
| to expose itself as thermal zone device, registered on the |  | ||||||
| thermal framework. |  | ||||||
|  |  | ||||||
| The thermal zone is built only if a device tree node |  | ||||||
| describing a thermal zone for this sensor is present |  | ||||||
| inside the lm90 DT node. Otherwise, the driver behavior |  | ||||||
| will be the same. |  | ||||||
|  |  | ||||||
| Discussed in: |  | ||||||
| http://www.gossamer-threads.com/lists/linux/kernel/1992853 |  | ||||||
|  |  | ||||||
| BUG=chrome-os-partner:30834 |  | ||||||
| TEST=Verified. Build and boot up system. |  | ||||||
|  |  | ||||||
| Signed-off-by: Wei Ni <wni@nvidia.com> |  | ||||||
| Reviewed-on: https://chromium-review.googlesource.com/181447 |  | ||||||
| Reviewed-by: Dylan Reid <dgreid@chromium.org> |  | ||||||
| Tested-by: Dylan Reid <dgreid@chromium.org> |  | ||||||
| Commit-Queue: Dylan Reid <dgreid@chromium.org> |  | ||||||
| Change-Id: Id356b94d7e8f4b49ec15e46b17a1fa2ff0cbf8cf |  | ||||||
| Reviewed-on: https://chromium-review.googlesource.com/212414 |  | ||||||
| Tested-by: Wei Ni <wni.nvidia@gmail.com> |  | ||||||
| Reviewed-by: Olof Johansson <olofj@chromium.org> |  | ||||||
| Commit-Queue: Olof Johansson <olofj@chromium.org> |  | ||||||
| --- |  | ||||||
| --- a/drivers/hwmon/lm90.c |  | ||||||
| +++ b/drivers/hwmon/lm90.c |  | ||||||
| @@ -96,6 +96,8 @@ |  | ||||||
|  #include <linux/sysfs.h> |  | ||||||
|  #include <linux/interrupt.h> |  | ||||||
|  #include <linux/regulator/consumer.h> |  | ||||||
| +#include <linux/of.h> |  | ||||||
| +#include <linux/thermal.h> |  | ||||||
|   |  | ||||||
|  /* |  | ||||||
|   * Addresses to scan |  | ||||||
| @@ -119,6 +121,13 @@ static const unsigned short normal_i2c[] |  | ||||||
|  enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, |  | ||||||
|  	max6646, w83l771, max6696, sa56004, g781, tmp451 }; |  | ||||||
|   |  | ||||||
| +enum sensor_id { |  | ||||||
| +	LOCAL = 0, |  | ||||||
| +	REMOTE, |  | ||||||
| +	REMOTE2, |  | ||||||
| +	SENSOR_ID_END, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
|  /* |  | ||||||
|   * The LM90 registers |  | ||||||
|   */ |  | ||||||
| @@ -368,6 +377,7 @@ struct lm90_data { |  | ||||||
|  	struct i2c_client *client; |  | ||||||
|  	struct device *hwmon_dev; |  | ||||||
|  	const struct attribute_group *groups[6]; |  | ||||||
| +	struct thermal_zone_device *tz[SENSOR_ID_END]; |  | ||||||
|  	struct mutex update_lock; |  | ||||||
|  	struct regulator *regulator; |  | ||||||
|  	char valid; /* zero until following fields are valid */ |  | ||||||
| @@ -880,6 +890,24 @@ static ssize_t show_temp11(struct device |  | ||||||
|  	return sprintf(buf, "%d\n", read_temp11(dev, attr->index)); |  | ||||||
|  } |  | ||||||
|   |  | ||||||
| +static int lm90_read_local_temp(void *dev, int *temp) |  | ||||||
| +{ |  | ||||||
| +	*temp = read_temp11(dev, 4); |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int lm90_read_remote_temp(void *dev, int *temp) |  | ||||||
| +{ |  | ||||||
| +	*temp = read_temp11(dev, 0); |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
| +static int lm90_read_remote2_temp(void *dev, int *temp) |  | ||||||
| +{ |  | ||||||
| +	*temp = read_temp11(dev, 5); |  | ||||||
| +	return 0; |  | ||||||
| +} |  | ||||||
| + |  | ||||||
|  static int write_temp11(struct device *dev, int nr, int index, long val) |  | ||||||
|  { |  | ||||||
|  	struct { |  | ||||||
| @@ -1210,6 +1238,18 @@ static const struct attribute_group lm90 |  | ||||||
|  	.attrs = lm90_temp3_attributes, |  | ||||||
|  }; |  | ||||||
|   |  | ||||||
| +static const struct thermal_zone_of_device_ops local_temp_sensor = { |  | ||||||
| +	.get_temp = lm90_read_local_temp, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +static const struct thermal_zone_of_device_ops remote_temp_sensor = { |  | ||||||
| +	.get_temp = lm90_read_remote_temp, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
| +static const struct thermal_zone_of_device_ops remote2_temp_sensor = { |  | ||||||
| +	.get_temp = lm90_read_remote2_temp, |  | ||||||
| +}; |  | ||||||
| + |  | ||||||
|  /* pec used for ADM1032 only */ |  | ||||||
|  static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, |  | ||||||
|  			char *buf) |  | ||||||
| @@ -1659,6 +1699,30 @@ static int lm90_probe(struct i2c_client |  | ||||||
|  		} |  | ||||||
|  	} |  | ||||||
|   |  | ||||||
| +	data->tz[LOCAL] = thermal_zone_of_sensor_register(&client->dev, |  | ||||||
| +							LOCAL, |  | ||||||
| +							&client->dev, |  | ||||||
| +							&local_temp_sensor); |  | ||||||
| +	if (IS_ERR(data->tz[LOCAL])) |  | ||||||
| +		data->tz[LOCAL] = NULL; |  | ||||||
| + |  | ||||||
| +	data->tz[REMOTE] = thermal_zone_of_sensor_register(&client->dev, |  | ||||||
| +							REMOTE, |  | ||||||
| +							&client->dev, |  | ||||||
| +							&remote_temp_sensor); |  | ||||||
| +	if (IS_ERR(data->tz[REMOTE])) |  | ||||||
| +		data->tz[REMOTE] = NULL; |  | ||||||
| + |  | ||||||
| +	if (data->flags & LM90_HAVE_TEMP3) { |  | ||||||
| +		data->tz[REMOTE2] = thermal_zone_of_sensor_register( |  | ||||||
| +							&client->dev, |  | ||||||
| +							REMOTE2, |  | ||||||
| +							&client->dev, |  | ||||||
| +							&remote2_temp_sensor); |  | ||||||
| +		if (IS_ERR(data->tz[REMOTE2])) |  | ||||||
| +			data->tz[REMOTE2] = NULL; |  | ||||||
| +	} |  | ||||||
| + |  | ||||||
|  	return 0; |  | ||||||
|   |  | ||||||
|  exit_unregister: |  | ||||||
| @@ -1674,8 +1738,11 @@ exit_restore: |  | ||||||
|   |  | ||||||
|  static int lm90_remove(struct i2c_client *client) |  | ||||||
|  { |  | ||||||
| +	int i; |  | ||||||
|  	struct lm90_data *data = i2c_get_clientdata(client); |  | ||||||
|   |  | ||||||
| +	for (i = 0; i < SENSOR_ID_END; i++) |  | ||||||
| +		thermal_zone_of_sensor_unregister(&client->dev, data->tz[i]); |  | ||||||
|  	hwmon_device_unregister(data->hwmon_dev); |  | ||||||
|  	device_remove_file(&client->dev, &dev_attr_pec); |  | ||||||
|  	lm90_restore_conf(client, data); |  | ||||||
		Reference in New Issue
	
	Block a user