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