apm821xx: add linux 4.19 apm821xx patches
This patch updates the apm821xx target to use the 4.19 kernel. 4.19 ships with all the crypto4xx driver patches. Furthermore, the DW-DMA fix for the SATA controller has been backported from 4.20 and integrated. Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
This commit is contained in:
		
							
								
								
									
										337
									
								
								target/linux/apm821xx/config-4.19
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								target/linux/apm821xx/config-4.19
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,337 @@ | ||||
| # CONFIG_40x is not set | ||||
| CONFIG_44x=y | ||||
| CONFIG_460EX=y | ||||
| CONFIG_4xx=y | ||||
| CONFIG_4xx_SOC=y | ||||
| # CONFIG_ADVANCED_OPTIONS is not set | ||||
| CONFIG_APM821xx=y | ||||
| CONFIG_APOLLO3G=y | ||||
| # CONFIG_ARCHES is not set | ||||
| CONFIG_ARCH_DMA_ADDR_T_64BIT=y | ||||
| CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y | ||||
| CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y | ||||
| CONFIG_ARCH_HAS_DMA_SET_COHERENT_MASK=y | ||||
| CONFIG_ARCH_HAS_ELF_RANDOMIZE=y | ||||
| CONFIG_ARCH_HAS_FORTIFY_SOURCE=y | ||||
| CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y | ||||
| CONFIG_ARCH_HAS_MEMBARRIER_CALLBACKS=y | ||||
| CONFIG_ARCH_HAS_PHYS_TO_DMA=y | ||||
| CONFIG_ARCH_HAS_PTE_SPECIAL=y | ||||
| CONFIG_ARCH_HAS_SG_CHAIN=y | ||||
| CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=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_MMAP_RND_BITS=11 | ||||
| CONFIG_ARCH_MMAP_RND_BITS_MAX=17 | ||||
| CONFIG_ARCH_MMAP_RND_BITS_MIN=11 | ||||
| CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=17 | ||||
| CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 | ||||
| CONFIG_ARCH_OPTIONAL_KERNEL_RWX=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_WEAK_RELEASE_ACQUIRE=y | ||||
| CONFIG_AUDIT_ARCH=y | ||||
| # CONFIG_BAMBOO is not set | ||||
| CONFIG_BCH=y | ||||
| # CONFIG_BLK_DEV_INITRD is not set | ||||
| CONFIG_BLK_MQ_PCI=y | ||||
| CONFIG_BLUESTONE=y | ||||
| CONFIG_BOOKE=y | ||||
| CONFIG_BOOKE_WDT=y | ||||
| # CONFIG_BOUNCE is not set | ||||
| CONFIG_BUILD_SALT="" | ||||
| # CONFIG_CANYONLANDS is not set | ||||
| CONFIG_CC_HAS_SANCOV_TRACE_PC=y | ||||
| CONFIG_CC_HAS_STACKPROTECTOR_NONE=y | ||||
| CONFIG_CC_IS_GCC=y | ||||
| CONFIG_CLANG_VERSION=0 | ||||
| CONFIG_CLONE_BACKWARDS=y | ||||
| CONFIG_CMDLINE="rootfstype=squashfs noinitrd" | ||||
| CONFIG_CMDLINE_BOOL=y | ||||
| CONFIG_CONSISTENT_SIZE=0x00200000 | ||||
| CONFIG_CONSOLE_LOGLEVEL_QUIET=4 | ||||
| CONFIG_CPU_BIG_ENDIAN=y | ||||
| CONFIG_CRC16=y | ||||
| CONFIG_CRYPTO_ACOMP2=y | ||||
| CONFIG_CRYPTO_AEAD=y | ||||
| CONFIG_CRYPTO_AEAD2=y | ||||
| CONFIG_CRYPTO_AKCIPHER2=y | ||||
| CONFIG_CRYPTO_CCM=y | ||||
| CONFIG_CRYPTO_CFB=y | ||||
| CONFIG_CRYPTO_CTR=y | ||||
| CONFIG_CRYPTO_DEFLATE=y | ||||
| CONFIG_CRYPTO_DEV_PPC4XX=y | ||||
| CONFIG_CRYPTO_DRBG=y | ||||
| CONFIG_CRYPTO_DRBG_HMAC=y | ||||
| CONFIG_CRYPTO_DRBG_MENU=y | ||||
| CONFIG_CRYPTO_ECB=y | ||||
| CONFIG_CRYPTO_GCM=y | ||||
| CONFIG_CRYPTO_GF128MUL=y | ||||
| CONFIG_CRYPTO_GHASH=y | ||||
| CONFIG_CRYPTO_HASH=y | ||||
| CONFIG_CRYPTO_HASH2=y | ||||
| CONFIG_CRYPTO_HMAC=y | ||||
| CONFIG_CRYPTO_HW=y | ||||
| CONFIG_CRYPTO_JITTERENTROPY=y | ||||
| CONFIG_CRYPTO_KPP2=y | ||||
| CONFIG_CRYPTO_LZO=y | ||||
| CONFIG_CRYPTO_MANAGER=y | ||||
| CONFIG_CRYPTO_MANAGER2=y | ||||
| CONFIG_CRYPTO_MD5_PPC=y | ||||
| CONFIG_CRYPTO_NULL=y | ||||
| CONFIG_CRYPTO_NULL2=y | ||||
| CONFIG_CRYPTO_RNG=y | ||||
| CONFIG_CRYPTO_RNG2=y | ||||
| CONFIG_CRYPTO_RNG_DEFAULT=y | ||||
| CONFIG_CRYPTO_SEQIV=y | ||||
| CONFIG_CRYPTO_SHA1_PPC=y | ||||
| CONFIG_CRYPTO_SHA256=y | ||||
| CONFIG_CRYPTO_WORKQUEUE=y | ||||
| CONFIG_DECOMPRESS_GZIP=y | ||||
| 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_EXTRA_TARGETS="uImage" | ||||
| CONFIG_FIXED_PHY=y | ||||
| CONFIG_FREEZER=y | ||||
| # CONFIG_FSL_LBC is not set | ||||
| CONFIG_GCC_VERSION=70300 | ||||
| CONFIG_GENERIC_ATOMIC64=y | ||||
| CONFIG_GENERIC_BUG=y | ||||
| CONFIG_GENERIC_CLOCKEVENTS=y | ||||
| CONFIG_GENERIC_CMOS_UPDATE=y | ||||
| CONFIG_GENERIC_CPU=y | ||||
| CONFIG_GENERIC_CPU_AUTOPROBE=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_TIME_VSYSCALL=y | ||||
| # CONFIG_GEN_RTC is not set | ||||
| # CONFIG_GLACIER is not set | ||||
| CONFIG_GPIOLIB=y | ||||
| CONFIG_GPIOLIB_FASTPATH_LIMIT=512 | ||||
| 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_HAVE_ARCH_AUDITSYSCALL=y | ||||
| CONFIG_HAVE_ARCH_JUMP_LABEL=y | ||||
| CONFIG_HAVE_ARCH_KGDB=y | ||||
| CONFIG_HAVE_ARCH_SECCOMP_FILTER=y | ||||
| CONFIG_HAVE_ARCH_TRACEHOOK=y | ||||
| CONFIG_HAVE_CBPF_JIT=y | ||||
| CONFIG_HAVE_DEBUG_KMEMLEAK=y | ||||
| CONFIG_HAVE_DEBUG_STACKOVERFLOW=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_GUP=y | ||||
| CONFIG_HAVE_IDE=y | ||||
| CONFIG_HAVE_IOREMAP_PROT=y | ||||
| CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y | ||||
| CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y | ||||
| CONFIG_HAVE_KPROBES_ON_FTRACE=y | ||||
| CONFIG_HAVE_LD_DEAD_CODE_DATA_ELIMINATION=y | ||||
| CONFIG_HAVE_MEMBLOCK=y | ||||
| CONFIG_HAVE_MEMBLOCK_NODE_MAP=y | ||||
| CONFIG_HAVE_MOD_ARCH_SPECIFIC=y | ||||
| CONFIG_HAVE_NET_DSA=y | ||||
| CONFIG_HAVE_OPROFILE=y | ||||
| CONFIG_HAVE_PERF_EVENTS=y | ||||
| CONFIG_HAVE_PERF_REGS=y | ||||
| CONFIG_HAVE_PERF_USER_STACK_DUMP=y | ||||
| CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y | ||||
| CONFIG_HAVE_RSEQ=y | ||||
| CONFIG_HAVE_SYSCALL_TRACEPOINTS=y | ||||
| CONFIG_HAVE_VIRT_CPU_ACCOUNTING=y | ||||
| CONFIG_HW_RANDOM=y | ||||
| CONFIG_HW_RANDOM_PPC4XX=y | ||||
| CONFIG_HZ=1000 | ||||
| # CONFIG_HZ_100 is not set | ||||
| CONFIG_HZ_1000=y | ||||
| CONFIG_HZ_PERIODIC=y | ||||
| CONFIG_I2C=y | ||||
| CONFIG_I2C_BOARDINFO=y | ||||
| CONFIG_I2C_CHARDEV=y | ||||
| CONFIG_I2C_IBM_IIC=y | ||||
| CONFIG_IBM_EMAC=y | ||||
| CONFIG_IBM_EMAC_EMAC4=y | ||||
| CONFIG_IBM_EMAC_POLL_WEIGHT=32 | ||||
| CONFIG_IBM_EMAC_RGMII=y | ||||
| CONFIG_IBM_EMAC_RXB=128 | ||||
| CONFIG_IBM_EMAC_RX_COPY_THRESHOLD=256 | ||||
| CONFIG_IBM_EMAC_RX_SKB_HEADROOM=0 | ||||
| CONFIG_IBM_EMAC_TAH=y | ||||
| CONFIG_IBM_EMAC_TXB=128 | ||||
| # CONFIG_ICE is not set | ||||
| # CONFIG_ICON is not set | ||||
| CONFIG_ILLEGAL_POINTER_VALUE=0 | ||||
| CONFIG_INITRAMFS_SOURCE="" | ||||
| CONFIG_IRQCHIP=y | ||||
| CONFIG_IRQ_DOMAIN=y | ||||
| CONFIG_IRQ_FORCED_THREADING=y | ||||
| CONFIG_IRQ_WORK=y | ||||
| CONFIG_ISA_DMA_API=y | ||||
| # CONFIG_JFFS2_FS is not set | ||||
| # CONFIG_KATMAI is not set | ||||
| CONFIG_KERNEL_GZIP=y | ||||
| CONFIG_KERNEL_START=0xc0000000 | ||||
| CONFIG_LEDS_TRIGGER_MTD=y | ||||
| CONFIG_LIBFDT=y | ||||
| CONFIG_LOCK_DEBUGGING_SUPPORT=y | ||||
| CONFIG_LOWMEM_SIZE=0x30000000 | ||||
| CONFIG_LZO_COMPRESS=y | ||||
| CONFIG_LZO_DECOMPRESS=y | ||||
| # CONFIG_MATH_EMULATION is not set | ||||
| CONFIG_MDIO_BUS=y | ||||
| CONFIG_MDIO_DEVICE=y | ||||
| CONFIG_MEMFD_CREATE=y | ||||
| CONFIG_MODULES_USE_ELF_RELA=y | ||||
| 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_SQUASHFS_ROOT is not set | ||||
| CONFIG_NEED_DMA_MAP_STATE=y | ||||
| CONFIG_NEED_PER_CPU_KM=y | ||||
| CONFIG_NEED_SG_DMA_LENGTH=y | ||||
| # CONFIG_NF_LOG_BRIDGE 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_EARLY_FLATTREE=y | ||||
| CONFIG_OF_FLATTREE=y | ||||
| CONFIG_OF_GPIO=y | ||||
| CONFIG_OF_IRQ=y | ||||
| CONFIG_OF_KOBJ=y | ||||
| CONFIG_OF_MDIO=y | ||||
| CONFIG_OF_NET=y | ||||
| CONFIG_OF_RESERVED_MEM=y | ||||
| CONFIG_OLD_SIGACTION=y | ||||
| CONFIG_OLD_SIGSUSPEND=y | ||||
| # CONFIG_OVERLAY_FS_METACOPY is not set | ||||
| CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y | ||||
| # CONFIG_OVERLAY_FS_XINO_AUTO is not set | ||||
| CONFIG_PAGE_OFFSET=0xc0000000 | ||||
| CONFIG_PCI=y | ||||
| CONFIG_PCIEAER=y | ||||
| CONFIG_PCIEPORTBUS=y | ||||
| CONFIG_PCIE_PME=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=y | ||||
| CONFIG_PPC4xx_CPM=y | ||||
| CONFIG_PPC4xx_GPIO=y | ||||
| CONFIG_PPC4xx_MSI=y | ||||
| CONFIG_PPC4xx_OCM=y | ||||
| CONFIG_PPC4xx_PCI_EXPRESS=y | ||||
| # CONFIG_PPC64 is not set | ||||
| # CONFIG_PPC_47x is not set | ||||
| # CONFIG_PPC_85xx is not set | ||||
| # CONFIG_PPC_8xx is not set | ||||
| CONFIG_PPC_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_DCR=y | ||||
| CONFIG_PPC_DCR_NATIVE=y | ||||
| # CONFIG_PPC_EARLY_DEBUG is not set | ||||
| CONFIG_PPC_FPU=y | ||||
| CONFIG_PPC_INDIRECT_PCI=y | ||||
| # CONFIG_PPC_IRQ_SOFT_MASK_DEBUG is not set | ||||
| CONFIG_PPC_LIB_RHEAP=y | ||||
| CONFIG_PPC_MMU_NOHASH=y | ||||
| CONFIG_PPC_MSI_BITMAP=y | ||||
| CONFIG_PPC_PCI_CHOICE=y | ||||
| # CONFIG_PPC_PTDUMP is not set | ||||
| CONFIG_PPC_UDBG_16550=y | ||||
| CONFIG_PPC_WERROR=y | ||||
| CONFIG_PTE_64BIT=y | ||||
| # CONFIG_RAINIER is not set | ||||
| # CONFIG_RANDOM_TRUST_CPU is not set | ||||
| CONFIG_RAS=y | ||||
| CONFIG_RD_GZIP=y | ||||
| # CONFIG_RENESAS_PHY is not set | ||||
| # CONFIG_RPMSG_VIRTIO is not set | ||||
| CONFIG_RSEQ=y | ||||
| CONFIG_RWSEM_XCHGADD_ALGORITHM=y | ||||
| # CONFIG_SAM440EP 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_SGL_ALLOC=y | ||||
| CONFIG_SIMPLE_GPIO=y | ||||
| CONFIG_SPARSE_IRQ=y | ||||
| CONFIG_SRCU=y | ||||
| CONFIG_SUSPEND=y | ||||
| CONFIG_SUSPEND_FREEZER=y | ||||
| CONFIG_SWPHY=y | ||||
| CONFIG_SYSCTL_EXCEPTION_TRACE=y | ||||
| # CONFIG_TAISHAN is not set | ||||
| CONFIG_TASK_SIZE=0xc0000000 | ||||
| CONFIG_THREAD_SHIFT=13 | ||||
| CONFIG_TICK_CPU_ACCOUNTING=y | ||||
| CONFIG_TINY_SRCU=y | ||||
| CONFIG_USB_SUPPORT=y | ||||
| CONFIG_VDSO32=y | ||||
| # CONFIG_VIRTIO_MENU is not set | ||||
| # CONFIG_VIRT_CPU_ACCOUNTING_NATIVE is not set | ||||
| # CONFIG_WARP is not set | ||||
| CONFIG_WATCHDOG_CORE=y | ||||
| # CONFIG_WNDR4700 is not set | ||||
| # CONFIG_XILINX_SYSACE is not set | ||||
| # CONFIG_XILINX_VCU is not set | ||||
| # CONFIG_XILINX_VIRTEX440_GENERIC_BOARD is not set | ||||
| # CONFIG_XIL_AXIS_FIFO 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 | ||||
| @@ -21,6 +21,7 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <dt-bindings/dma/dw-dmac.h> | ||||
| #include <dt-bindings/input/input.h> | ||||
| #include <dt-bindings/interrupt-controller/irq.h> | ||||
| #include <dt-bindings/gpio/gpio.h> | ||||
| @@ -384,7 +385,20 @@ | ||||
| 			interrupt-parent = <&UIC0>; | ||||
| 			interrupts = <0x19 IRQ_TYPE_LEVEL_HIGH>; | ||||
| 			#dma-cells = <3>; | ||||
| 			/* use autoconfiguration for the dma setup */ | ||||
|  | ||||
| 			dma-channels = <2>; | ||||
| 			dma-masters = <3>; | ||||
| 			block_size = <4095>; | ||||
| 			data-width = <4>, <4>, <4>; | ||||
| 			multi-block = <1>, <1>; | ||||
|  | ||||
| 			chan_allocation_order = <1>; | ||||
| 			chan_priority = <1>; | ||||
|  | ||||
| 			snps,dma-protection-control = | ||||
| 				<(DW_DMAC_HPROT1_PRIVILEGED_MODE | | ||||
| 				  DW_DMAC_HPROT2_BUFFERABLE)>; | ||||
| 			is_memcpy; | ||||
| 		}; | ||||
|  | ||||
| 		SATA0: sata@bffd1000 { | ||||
|   | ||||
| @@ -0,0 +1,155 @@ | ||||
| From 7b0c03ecc42fb223baf015877fee9d517c2c8af1 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Lamparter <chunkeey@gmail.com> | ||||
| Date: Sat, 17 Nov 2018 17:17:21 +0100 | ||||
| Subject: dmaengine: dw-dmac: implement dma protection control setting | ||||
|  | ||||
| This patch adds a new device-tree property that allows to | ||||
| specify the dma protection control bits for the all of the | ||||
| DMA controller's channel uniformly. | ||||
|  | ||||
| Setting the "correct" bits can have a huge impact on the | ||||
| PPC460EX and APM82181 that use this DMA engine in combination | ||||
| with a DesignWare' SATA-II core (sata_dwc_460ex driver). | ||||
|  | ||||
| In the OpenWrt Forum, the user takimata reported that: | ||||
| |It seems your patch unleashed the full power of the SATA port. | ||||
| |Where I was previously hitting a really hard limit at around | ||||
| |82 MB/s for reading and 27 MB/s for writing, I am now getting this: | ||||
| | | ||||
| |root@OpenWrt:/mnt# time dd if=/dev/zero of=tempfile bs=1M count=1024 | ||||
| |1024+0 records in | ||||
| |1024+0 records out | ||||
| |real    0m 13.65s | ||||
| |user    0m 0.01s | ||||
| |sys     0m 11.89s | ||||
| | | ||||
| |root@OpenWrt:/mnt# time dd if=tempfile of=/dev/null bs=1M count=1024 | ||||
| |1024+0 records in | ||||
| |1024+0 records out | ||||
| |real    0m 8.41s | ||||
| |user    0m 0.01s | ||||
| |sys     0m 4.70s | ||||
| | | ||||
| |This means: 121 MB/s reading and 75 MB/s writing! | ||||
| | | ||||
| |The drive is a WD Green WD10EARX taken from an older MBL Single. | ||||
| |I repeated the test a few times with even larger files to rule out | ||||
| |any caching, I'm still seeing the same great performance. OpenWrt is | ||||
| |now completely on par with the original MBL firmware's performance. | ||||
|  | ||||
| Another user And.short reported: | ||||
| |I can report that your fix worked! Boots up fine with two | ||||
| |drives even with more partitions, and no more reboot on | ||||
| |concurrent disk access! | ||||
|  | ||||
| A closer look into the sata_dwc_460ex code revealed that | ||||
| the driver did initally set the correct protection control | ||||
| bits. However, this feature was lost when the sata_dwc_460ex | ||||
| driver was converted to the generic DMA driver framework. | ||||
|  | ||||
| BugLink: https://forum.openwrt.org/t/wd-mybook-live-duo-two-disks/16195/55 | ||||
| BugLink: https://forum.openwrt.org/t/wd-mybook-live-duo-two-disks/16195/50 | ||||
| Fixes: 8b3444852a2b ("sata_dwc_460ex: move to generic DMA driver") | ||||
| Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||||
| Signed-off-by: Christian Lamparter <chunkeey@gmail.com> | ||||
| Signed-off-by: Vinod Koul <vkoul@kernel.org> | ||||
| --- | ||||
|  | ||||
| diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c | ||||
| index d0c3e50b39fb..2c5ca1961256 100644 | ||||
| --- a/drivers/dma/dw/core.c | ||||
| +++ b/drivers/dma/dw/core.c | ||||
| @@ -160,12 +160,14 @@ static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc) | ||||
|   | ||||
|  static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc) | ||||
|  { | ||||
| +	struct dw_dma *dw = to_dw_dma(dwc->chan.device); | ||||
|  	u32 cfghi = DWC_CFGH_FIFO_MODE; | ||||
|  	u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); | ||||
|  	bool hs_polarity = dwc->dws.hs_polarity; | ||||
|   | ||||
|  	cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); | ||||
|  	cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); | ||||
| +	cfghi |= DWC_CFGH_PROTCTL(dw->pdata->protctl); | ||||
|   | ||||
|  	/* Set polarity of handshake interface */ | ||||
|  	cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0; | ||||
| diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c | ||||
| index f01b2c173fa6..31ff8113c3de 100644 | ||||
| --- a/drivers/dma/dw/platform.c | ||||
| +++ b/drivers/dma/dw/platform.c | ||||
| @@ -162,6 +162,12 @@ dw_dma_parse_dt(struct platform_device *pdev) | ||||
|  			pdata->multi_block[tmp] = 1; | ||||
|  	} | ||||
|   | ||||
| +	if (!of_property_read_u32(np, "snps,dma-protection-control", &tmp)) { | ||||
| +		if (tmp > CHAN_PROTCTL_MASK) | ||||
| +			return NULL; | ||||
| +		pdata->protctl = tmp; | ||||
| +	} | ||||
| + | ||||
|  	return pdata; | ||||
|  } | ||||
|  #else | ||||
| diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h | ||||
| index 09e7dfdbb790..646c9c960c07 100644 | ||||
| --- a/drivers/dma/dw/regs.h | ||||
| +++ b/drivers/dma/dw/regs.h | ||||
| @@ -200,6 +200,10 @@ enum dw_dma_msize { | ||||
|  #define DWC_CFGH_FCMODE		(1 << 0) | ||||
|  #define DWC_CFGH_FIFO_MODE	(1 << 1) | ||||
|  #define DWC_CFGH_PROTCTL(x)	((x) << 2) | ||||
| +#define DWC_CFGH_PROTCTL_DATA	(0 << 2)	/* data access - always set */ | ||||
| +#define DWC_CFGH_PROTCTL_PRIV	(1 << 2)	/* privileged -> AHB HPROT[1] */ | ||||
| +#define DWC_CFGH_PROTCTL_BUFFER	(2 << 2)	/* bufferable -> AHB HPROT[2] */ | ||||
| +#define DWC_CFGH_PROTCTL_CACHE	(4 << 2)	/* cacheable  -> AHB HPROT[3] */ | ||||
|  #define DWC_CFGH_DS_UPD_EN	(1 << 5) | ||||
|  #define DWC_CFGH_SS_UPD_EN	(1 << 6) | ||||
|  #define DWC_CFGH_SRC_PER(x)	((x) << 7) | ||||
| diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h | ||||
| index 896cb71a382c..1a1d58ebffbf 100644 | ||||
| --- a/include/linux/platform_data/dma-dw.h | ||||
| +++ b/include/linux/platform_data/dma-dw.h | ||||
| @@ -49,6 +49,7 @@  struct dw_dma_slave { | ||||
|   * @data_width: Maximum data width supported by hardware per AHB master | ||||
|   *		(in bytes, power of 2) | ||||
|   * @multi_block: Multi block transfers supported by hardware per channel. | ||||
| + * @protctl: Protection control signals setting per channel. | ||||
|   */ | ||||
|  struct dw_dma_platform_data { | ||||
|  	unsigned int	nr_channels; | ||||
| @@ -65,6 +66,11 @@  struct dw_dma_platform_data { | ||||
|  	unsigned char	nr_masters; | ||||
|  	unsigned char	data_width[DW_DMA_MAX_NR_MASTERS]; | ||||
|  	unsigned char	multi_block[DW_DMA_MAX_NR_CHANNELS]; | ||||
| +#define CHAN_PROTCTL_PRIVILEGED		BIT(0) | ||||
| +#define CHAN_PROTCTL_BUFFERABLE		BIT(1) | ||||
| +#define CHAN_PROTCTL_CACHEABLE		BIT(2) | ||||
| +#define CHAN_PROTCTL_MASK		GENMASK(2, 0) | ||||
| +	unsigned char	protctl; | ||||
|  }; | ||||
|   | ||||
|  #endif /* _PLATFORM_DATA_DMA_DW_H */ | ||||
| diff --git a/include/dt-bindings/dma/dw-dmac.h b/include/dt-bindings/dma/dw-dmac.h | ||||
| new file mode 100644 | ||||
| index 000000000000..d1ca705c95b3 | ||||
| --- /dev/null | ||||
| +++ b/include/dt-bindings/dma/dw-dmac.h | ||||
| @@ -0,0 +1,14 @@  | ||||
| +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ | ||||
| + | ||||
| +#ifndef __DT_BINDINGS_DMA_DW_DMAC_H__ | ||||
| +#define __DT_BINDINGS_DMA_DW_DMAC_H__ | ||||
| + | ||||
| +/* | ||||
| + * Protection Control bits provide protection against illegal transactions. | ||||
| + * The protection bits[0:2] are one-to-one mapped to AHB HPROT[3:1] signals. | ||||
| + */ | ||||
| +#define DW_DMAC_HPROT1_PRIVILEGED_MODE	(1 << 0)	/* Privileged Mode */ | ||||
| +#define DW_DMAC_HPROT2_BUFFERABLE	(1 << 1)	/* DMA is bufferable */ | ||||
| +#define DW_DMAC_HPROT3_CACHEABLE	(1 << 2)	/* DMA is cacheable */ | ||||
| + | ||||
| +#endif /* __DT_BINDINGS_DMA_DW_DMAC_H__ */ | ||||
| -- | ||||
| cgit 1.2-0.3.lf.el7 | ||||
|  | ||||
| @@ -0,0 +1,169 @@ | ||||
| From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001 | ||||
| From: John Crispin <blogic@openwrt.org> | ||||
| Date: Tue, 12 Aug 2014 20:49:27 +0200 | ||||
| Subject: [PATCH 30/36] GPIO: add named gpio exports | ||||
|  | ||||
| Signed-off-by: John Crispin <blogic@openwrt.org> | ||||
| --- | ||||
|  drivers/gpio/gpiolib-of.c     |   68 +++++++++++++++++++++++++++++++++++++++++ | ||||
|  drivers/gpio/gpiolib.c        |   11 +++++-- | ||||
|  include/asm-generic/gpio.h    |    5 +++ | ||||
|  include/linux/gpio/consumer.h |    8 +++++ | ||||
|  4 files changed, 90 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/gpio/gpiolib-of.c | ||||
| +++ b/drivers/gpio/gpiolib-of.c | ||||
| @@ -23,6 +23,8 @@ | ||||
|  #include <linux/pinctrl/pinctrl.h> | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/gpio/machine.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/platform_device.h> | ||||
|   | ||||
|  #include "gpiolib.h" | ||||
|   | ||||
| @@ -654,3 +656,72 @@ void of_gpiochip_remove(struct gpio_chip | ||||
|  	gpiochip_remove_pin_ranges(chip); | ||||
|  	of_node_put(chip->of_node); | ||||
|  } | ||||
| + | ||||
| +#ifdef CONFIG_GPIO_SYSFS | ||||
| + | ||||
| +static struct of_device_id gpio_export_ids[] = { | ||||
| +	{ .compatible = "gpio-export" }, | ||||
| +	{ /* sentinel */ } | ||||
| +}; | ||||
| + | ||||
| +static int of_gpio_export_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct device_node *np = pdev->dev.of_node; | ||||
| +	struct device_node *cnp; | ||||
| +	u32 val; | ||||
| +	int nb = 0; | ||||
| + | ||||
| +	for_each_child_of_node(np, cnp) { | ||||
| +		const char *name = NULL; | ||||
| +		int gpio; | ||||
| +		bool dmc; | ||||
| +		int max_gpio = 1; | ||||
| +		int i; | ||||
| + | ||||
| +		of_property_read_string(cnp, "gpio-export,name", &name); | ||||
| + | ||||
| +		if (!name) | ||||
| +			max_gpio = of_gpio_count(cnp); | ||||
| + | ||||
| +		for (i = 0; i < max_gpio; i++) { | ||||
| +			unsigned flags = 0; | ||||
| +			enum of_gpio_flags of_flags; | ||||
| + | ||||
| +			gpio = of_get_gpio_flags(cnp, i, &of_flags); | ||||
| +			if (!gpio_is_valid(gpio)) | ||||
| +				return gpio; | ||||
| + | ||||
| +			if (of_flags == OF_GPIO_ACTIVE_LOW) | ||||
| +				flags |= GPIOF_ACTIVE_LOW; | ||||
| + | ||||
| +			if (!of_property_read_u32(cnp, "gpio-export,output", &val)) | ||||
| +				flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; | ||||
| +			else | ||||
| +				flags |= GPIOF_IN; | ||||
| + | ||||
| +			if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) | ||||
| +				continue; | ||||
| + | ||||
| +			dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); | ||||
| +			gpio_export_with_name(gpio, dmc, name); | ||||
| +			nb++; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static struct platform_driver gpio_export_driver = { | ||||
| +	.driver		= { | ||||
| +		.name		= "gpio-export", | ||||
| +		.owner	= THIS_MODULE, | ||||
| +		.of_match_table	= of_match_ptr(gpio_export_ids), | ||||
| +	}, | ||||
| +	.probe		= of_gpio_export_probe, | ||||
| +}; | ||||
| + | ||||
| +module_platform_driver(gpio_export_driver); | ||||
| + | ||||
| +#endif | ||||
| --- a/include/asm-generic/gpio.h | ||||
| +++ b/include/asm-generic/gpio.h | ||||
| @@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g | ||||
|  	return gpiod_export(gpio_to_desc(gpio), direction_may_change); | ||||
|  } | ||||
|   | ||||
| +int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); | ||||
| +static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) | ||||
| +{ | ||||
| +	return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); | ||||
| +} | ||||
| + | ||||
|  static inline int gpio_export_link(struct device *dev, const char *name, | ||||
|  				   unsigned gpio) | ||||
|  { | ||||
| --- a/include/linux/gpio/consumer.h | ||||
| +++ b/include/linux/gpio/consumer.h | ||||
| @@ -531,6 +531,7 @@ struct gpio_desc *devm_fwnode_get_gpiod_ | ||||
|   | ||||
|  #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) | ||||
|   | ||||
| +int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); | ||||
|  int gpiod_export(struct gpio_desc *desc, bool direction_may_change); | ||||
|  int gpiod_export_link(struct device *dev, const char *name, | ||||
|  		      struct gpio_desc *desc); | ||||
| @@ -538,6 +539,13 @@ void gpiod_unexport(struct gpio_desc *de | ||||
|   | ||||
|  #else  /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ | ||||
|   | ||||
| +static inline int _gpiod_export(struct gpio_desc *desc, | ||||
| +			       bool direction_may_change, | ||||
| +			       const char *name) | ||||
| +{ | ||||
| +	return -ENOSYS; | ||||
| +} | ||||
| + | ||||
|  static inline int gpiod_export(struct gpio_desc *desc, | ||||
|  			       bool direction_may_change) | ||||
|  { | ||||
| --- a/drivers/gpio/gpiolib-sysfs.c | ||||
| +++ b/drivers/gpio/gpiolib-sysfs.c | ||||
| @@ -568,7 +568,7 @@ static struct class gpio_class = { | ||||
|   * | ||||
|   * Returns zero on success, else an error. | ||||
|   */ | ||||
| -int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | ||||
| +int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) | ||||
|  { | ||||
|  	struct gpio_chip	*chip; | ||||
|  	struct gpio_device	*gdev; | ||||
| @@ -630,6 +630,8 @@ int gpiod_export(struct gpio_desc *desc, | ||||
|  	offset = gpio_chip_hwgpio(desc); | ||||
|  	if (chip->names && chip->names[offset]) | ||||
|  		ioname = chip->names[offset]; | ||||
| +	if (name) | ||||
| +		ioname = name; | ||||
|   | ||||
|  	dev = device_create_with_groups(&gpio_class, &gdev->dev, | ||||
|  					MKDEV(0, 0), data, gpio_groups, | ||||
| @@ -651,6 +653,12 @@ err_unlock: | ||||
|  	gpiod_dbg(desc, "%s: status %d\n", __func__, status); | ||||
|  	return status; | ||||
|  } | ||||
| +EXPORT_SYMBOL_GPL(__gpiod_export); | ||||
| + | ||||
| +int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | ||||
| +{ | ||||
| +	return __gpiod_export(desc, direction_may_change, NULL); | ||||
| +} | ||||
|  EXPORT_SYMBOL_GPL(gpiod_export); | ||||
|   | ||||
|  static int match_export(struct device *dev, const void *desc) | ||||
| @@ -0,0 +1,30 @@ | ||||
| --- a/arch/powerpc/platforms/44x/Kconfig | ||||
| +++ b/arch/powerpc/platforms/44x/Kconfig | ||||
| @@ -144,6 +144,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", | ||||
| @@ -0,0 +1,32 @@ | ||||
| --- a/arch/powerpc/platforms/44x/Makefile | ||||
| +++ b/arch/powerpc/platforms/44x/Makefile | ||||
| @@ -4,6 +4,7 @@ ifneq ($(CONFIG_PPC4xx_CPM),y) | ||||
|  obj-y	+= 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 | ||||
| @@ -273,6 +273,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 | ||||
| @@ -0,0 +1,51 @@ | ||||
| --- a/arch/powerpc/platforms/4xx/pci.c | ||||
| +++ b/arch/powerpc/platforms/4xx/pci.c | ||||
| @@ -1060,15 +1060,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; | ||||
| @@ -1085,9 +1094,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) | | ||||
| @@ -0,0 +1,14 @@ | ||||
| --- a/arch/powerpc/platforms/4xx/pci.c | ||||
| +++ b/arch/powerpc/platforms/4xx/pci.c | ||||
| @@ -1905,9 +1905,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); | ||||
|   | ||||
| @@ -0,0 +1,545 @@ | ||||
| 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 | ||||
| @@ -12,6 +12,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" | ||||
| @@ -248,6 +250,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) | ||||
|  { | ||||
| @@ -286,6 +740,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 */ | ||||
| @@ -347,6 +817,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) { | ||||
| @@ -0,0 +1,53 @@ | ||||
| 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 | ||||
| @@ -206,6 +206,7 @@ static void xhci_pci_quirks(struct devic | ||||
|  	    pdev->device == 0x0015) { | ||||
|  		xhci->quirks |= XHCI_RESET_ON_RESUME; | ||||
|  		xhci->quirks |= XHCI_ZERO_64B_REGS; | ||||
| +		xhci->quirks |= 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 | ||||
| @@ -424,10 +424,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->msi_enabled = 1; | ||||
| --- a/drivers/usb/host/xhci.h | ||||
| +++ b/drivers/usb/host/xhci.h | ||||
| @@ -1857,6 +1857,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; | ||||
| @@ -0,0 +1,65 @@ | ||||
| From 694f9bfb8efaef8a33e8992015ff9d0866faf4a2 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Lamparter <chunkeey@gmail.com> | ||||
| Date: Sun, 17 Dec 2017 17:27:15 +0100 | ||||
| Subject: [PATCH 1/2] hwmon: tc654 add detection routine | ||||
|  | ||||
| This patch adds a detection routine for the TC654/TC655 | ||||
| chips.  Both IDs are listed in the Datasheet. | ||||
|  | ||||
| Signed-off-by: Christian Lamparter <chunkeey@gmail.com> | ||||
| --- | ||||
|  drivers/hwmon/tc654.c | 29 +++++++++++++++++++++++++++++ | ||||
|  1 file changed, 29 insertions(+) | ||||
|  | ||||
| --- a/drivers/hwmon/tc654.c | ||||
| +++ b/drivers/hwmon/tc654.c | ||||
| @@ -64,6 +64,11 @@ enum tc654_regs { | ||||
|  /* Register data is read (and cached) at most once per second. */ | ||||
|  #define TC654_UPDATE_INTERVAL		HZ | ||||
|   | ||||
| +/* Manufacturer and Version Identification Register Values */ | ||||
| +#define TC654_MFR_ID_MICROCHIP		0x84 | ||||
| +#define TC654_VER_ID			0x00 | ||||
| +#define TC655_VER_ID			0x01 | ||||
| + | ||||
|  struct tc654_data { | ||||
|  	struct i2c_client *client; | ||||
|   | ||||
| @@ -497,6 +502,29 @@ static const struct i2c_device_id tc654_ | ||||
|  	{} | ||||
|  }; | ||||
|   | ||||
| +static int | ||||
| +tc654_detect(struct i2c_client *new_client, struct i2c_board_info *info) | ||||
| +{ | ||||
| +	struct i2c_adapter *adapter = new_client->adapter; | ||||
| +	int manufacturer, product; | ||||
| + | ||||
| +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	manufacturer = i2c_smbus_read_byte_data(new_client, TC654_REG_MFR_ID); | ||||
| +	if (manufacturer != TC654_MFR_ID_MICROCHIP) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	product = i2c_smbus_read_byte_data(new_client, TC654_REG_VER_ID); | ||||
| +	if (!((product == TC654_VER_ID) || (product == TC655_VER_ID))) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	strlcpy(info->type, product == TC654_VER_ID ? "tc654" : "tc655", | ||||
| +		I2C_NAME_SIZE); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| + | ||||
|  MODULE_DEVICE_TABLE(i2c, tc654_id); | ||||
|   | ||||
|  static struct i2c_driver tc654_driver = { | ||||
| @@ -505,6 +533,7 @@ static struct i2c_driver tc654_driver = | ||||
|  		   }, | ||||
|  	.probe = tc654_probe, | ||||
|  	.id_table = tc654_id, | ||||
| +	.detect = tc654_detect, | ||||
|  }; | ||||
|   | ||||
|  module_i2c_driver(tc654_driver); | ||||
| @@ -0,0 +1,174 @@ | ||||
| From 15ae701189744d321d3a1264ff46f8871e8765ee Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Lamparter <chunkeey@gmail.com> | ||||
| Date: Sun, 17 Dec 2017 17:29:13 +0100 | ||||
| Subject: [PATCH] hwmon: tc654: add thermal_cooling device | ||||
|  | ||||
| This patch adds a thermaL_cooling device to the tc654 driver. | ||||
| This allows the chip to be used for DT-based cooling. | ||||
|  | ||||
| Signed-off-by: Christian Lamparter <chunkeey@gmail.com> | ||||
| --- | ||||
|  drivers/hwmon/tc654.c | 103 +++++++++++++++++++++++++++++++++++++++++--------- | ||||
|  1 file changed, 86 insertions(+), 17 deletions(-) | ||||
|  | ||||
| --- a/drivers/hwmon/tc654.c | ||||
| +++ b/drivers/hwmon/tc654.c | ||||
| @@ -24,6 +24,7 @@ | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/mutex.h> | ||||
|  #include <linux/slab.h> | ||||
| +#include <linux/thermal.h> | ||||
|  #include <linux/util_macros.h> | ||||
|   | ||||
|  enum tc654_regs { | ||||
| @@ -141,6 +142,9 @@ struct tc654_data { | ||||
|  			 * writable register used to control the duty | ||||
|  			 * cycle of the V OUT output. | ||||
|  			 */ | ||||
| + | ||||
| +	/* optional cooling device */ | ||||
| +	struct thermal_cooling_device *cdev; | ||||
|  }; | ||||
|   | ||||
|  /* helper to grab and cache data, at most one time per second */ | ||||
| @@ -376,36 +380,30 @@ static ssize_t set_pwm_mode(struct devic | ||||
|  static const int tc654_pwm_map[16] = { 77,  88, 102, 112, 124, 136, 148, 160, | ||||
|  				      172, 184, 196, 207, 219, 231, 243, 255}; | ||||
|   | ||||
| +static int get_pwm(struct tc654_data *data) | ||||
| +{ | ||||
| +	if (data->config & TC654_REG_CONFIG_SDM) | ||||
| +		return 0; | ||||
| +	else | ||||
| +		return tc654_pwm_map[data->duty_cycle]; | ||||
| +} | ||||
| + | ||||
|  static ssize_t show_pwm(struct device *dev, struct device_attribute *da, | ||||
|  			char *buf) | ||||
|  { | ||||
|  	struct tc654_data *data = tc654_update_client(dev); | ||||
| -	int pwm; | ||||
|   | ||||
|  	if (IS_ERR(data)) | ||||
|  		return PTR_ERR(data); | ||||
|   | ||||
| -	if (data->config & TC654_REG_CONFIG_SDM) | ||||
| -		pwm = 0; | ||||
| -	else | ||||
| -		pwm = tc654_pwm_map[data->duty_cycle]; | ||||
| - | ||||
| -	return sprintf(buf, "%d\n", pwm); | ||||
| +	return sprintf(buf, "%d\n", get_pwm(data)); | ||||
|  } | ||||
|   | ||||
| -static ssize_t set_pwm(struct device *dev, struct device_attribute *da, | ||||
| -		       const char *buf, size_t count) | ||||
| +static int _set_pwm(struct tc654_data *data, unsigned long val) | ||||
|  { | ||||
| -	struct tc654_data *data = dev_get_drvdata(dev); | ||||
|  	struct i2c_client *client = data->client; | ||||
| -	unsigned long val; | ||||
|  	int ret; | ||||
|   | ||||
| -	if (kstrtoul(buf, 10, &val)) | ||||
| -		return -EINVAL; | ||||
| -	if (val > 255) | ||||
| -		return -EINVAL; | ||||
| - | ||||
|  	mutex_lock(&data->update_lock); | ||||
|   | ||||
|  	if (val == 0) | ||||
| @@ -425,6 +423,22 @@ static ssize_t set_pwm(struct device *de | ||||
|   | ||||
|  out: | ||||
|  	mutex_unlock(&data->update_lock); | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static ssize_t set_pwm(struct device *dev, struct device_attribute *da, | ||||
| +		       const char *buf, size_t count) | ||||
| +{ | ||||
| +	struct tc654_data *data = dev_get_drvdata(dev); | ||||
| +	unsigned long val; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (kstrtoul(buf, 10, &val)) | ||||
| +		return -EINVAL; | ||||
| +	if (val > 255) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	ret = _set_pwm(data, val); | ||||
|  	return ret < 0 ? ret : count; | ||||
|  } | ||||
|   | ||||
| @@ -462,6 +476,47 @@ static struct attribute *tc654_attrs[] = | ||||
|   | ||||
|  ATTRIBUTE_GROUPS(tc654); | ||||
|   | ||||
| +/* cooling device */ | ||||
| + | ||||
| +static int tc654_get_max_state(struct thermal_cooling_device *cdev, | ||||
| +			       unsigned long *state) | ||||
| +{ | ||||
| +	*state = 255; | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int tc654_get_cur_state(struct thermal_cooling_device *cdev, | ||||
| +                              unsigned long *state) | ||||
| +{ | ||||
| +	struct tc654_data *data = tc654_update_client(cdev->devdata); | ||||
| + | ||||
| +	if (IS_ERR(data)) | ||||
| +		return PTR_ERR(data); | ||||
| + | ||||
| +	*state = get_pwm(data); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int tc654_set_cur_state(struct thermal_cooling_device *cdev, | ||||
| +			       unsigned long state) | ||||
| +{ | ||||
| +	struct tc654_data *data = tc654_update_client(cdev->devdata); | ||||
| + | ||||
| +	if (IS_ERR(data)) | ||||
| +		return PTR_ERR(data); | ||||
| + | ||||
| +	if (state > 255) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	return _set_pwm(data, state); | ||||
| +} | ||||
| + | ||||
| +static const struct thermal_cooling_device_ops tc654_fan_cool_ops = { | ||||
| +	.get_max_state = tc654_get_max_state, | ||||
| +	.get_cur_state = tc654_get_cur_state, | ||||
| +	.set_cur_state = tc654_set_cur_state, | ||||
| +}; | ||||
| + | ||||
|  /* | ||||
|   * device probe and removal | ||||
|   */ | ||||
| @@ -493,7 +548,21 @@ static int tc654_probe(struct i2c_client | ||||
|  	hwmon_dev = | ||||
|  	    devm_hwmon_device_register_with_groups(dev, client->name, data, | ||||
|  						   tc654_groups); | ||||
| -	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||
| +	if (IS_ERR(hwmon_dev)) | ||||
| +		return PTR_ERR(hwmon_dev); | ||||
| + | ||||
| +#if IS_ENABLED(CONFIG_OF) | ||||
| +	/* Optional cooling device register for Device tree platforms */ | ||||
| +	data->cdev = thermal_of_cooling_device_register(client->dev.of_node, | ||||
| +							"tc654", hwmon_dev, | ||||
| +							&tc654_fan_cool_ops); | ||||
| +#else /* CONFIG_OF */ | ||||
| +	/* Optional cooling device register for non Device tree platforms */ | ||||
| +	data->cdev = thermal_cooling_device_register("tc654", hwmon_dev, | ||||
| +						     &tc654_fan_cool_ops); | ||||
| +#endif /* CONFIG_OF */ | ||||
| + | ||||
| +	return PTR_ERR_OR_ZERO(data->cdev); | ||||
|  } | ||||
|   | ||||
|  static const struct i2c_device_id tc654_id[] = { | ||||
| @@ -1,4 +1,3 @@ | ||||
| # CONFIG_IKAREM is not set | ||||
| CONFIG_APOLLO3G=y | ||||
| CONFIG_BROADCOM_PHY=y | ||||
| CONFIG_EXT4_FS=y | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Christian Lamparter
					Christian Lamparter