ipq806x: add kernel 4.14 support
- Rebased the patches for 4.14
 - Dropped spi-qup and 0027, 0028, 0029
   clk patches since it's already included
   in upstream.
 Tested on IPQ AP148 Board:
  1) NOR boot and NAND boot
  2) Tested USB and PCIe interfaces
  3) WDOG test
  4) cpu frequency scaling
  5) ethernet, 2G and 5G WiFi
  6) ubi sysupgrade
Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
Tested-by: Stefan Lippers-Hollmann <s.l-h@gmx.de>
(cherry picked from commit 93dd2f7211)
			
			
This commit is contained in:
		 Ram Chandra Jangir
					Ram Chandra Jangir
				
			
				
					committed by
					
						 John Crispin
						John Crispin
					
				
			
			
				
	
			
			
			 John Crispin
						John Crispin
					
				
			
						parent
						
							9361c359e4
						
					
				
				
					commit
					d805812e75
				
			
							
								
								
									
										502
									
								
								target/linux/ipq806x/config-4.14
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								target/linux/ipq806x/config-4.14
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,502 @@ | ||||
| CONFIG_ALIGNMENT_TRAP=y | ||||
| # CONFIG_AMBA_PL08X is not set | ||||
| CONFIG_APQ_GCC_8084=y | ||||
| CONFIG_APQ_MMCC_8084=y | ||||
| CONFIG_AR8216_PHY=y | ||||
| CONFIG_ARCH_CLOCKSOURCE_DATA=y | ||||
| CONFIG_ARCH_HAS_ELF_RANDOMIZE=y | ||||
| CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y | ||||
| CONFIG_ARCH_HAS_SG_CHAIN=y | ||||
| CONFIG_ARCH_HAS_TICK_BROADCAST=y | ||||
| CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y | ||||
| CONFIG_ARCH_HIBERNATION_POSSIBLE=y | ||||
| # CONFIG_ARCH_MDM9615 is not set | ||||
| CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y | ||||
| CONFIG_ARCH_MSM8960=y | ||||
| CONFIG_ARCH_MSM8974=y | ||||
| CONFIG_ARCH_MSM8X60=y | ||||
| CONFIG_ARCH_MULTIPLATFORM=y | ||||
| # CONFIG_ARCH_MULTI_CPU_AUTO is not set | ||||
| CONFIG_ARCH_MULTI_V6_V7=y | ||||
| CONFIG_ARCH_MULTI_V7=y | ||||
| CONFIG_ARCH_NR_GPIO=0 | ||||
| CONFIG_ARCH_QCOM=y | ||||
| # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set | ||||
| # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set | ||||
| CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y | ||||
| CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y | ||||
| CONFIG_ARCH_SUPPORTS_UPROBES=y | ||||
| CONFIG_ARCH_SUSPEND_POSSIBLE=y | ||||
| CONFIG_ARCH_USE_BUILTIN_BSWAP=y | ||||
| CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y | ||||
| CONFIG_ARCH_WANT_GENERAL_HUGETLB=y | ||||
| CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y | ||||
| CONFIG_ARM=y | ||||
| CONFIG_ARM_AMBA=y | ||||
| CONFIG_ARM_APPENDED_DTB=y | ||||
| CONFIG_ARM_ARCH_TIMER=y | ||||
| CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y | ||||
| CONFIG_ARM_ATAG_DTB_COMPAT=y | ||||
| # CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set | ||||
| # CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set | ||||
| CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE=y | ||||
| CONFIG_ARM_CPUIDLE=y | ||||
| CONFIG_ARM_CPU_SUSPEND=y | ||||
| # CONFIG_ARM_CPU_TOPOLOGY is not set | ||||
| CONFIG_ARM_GIC=y | ||||
| CONFIG_ARM_HAS_SG_CHAIN=y | ||||
| CONFIG_ARM_L1_CACHE_SHIFT=6 | ||||
| CONFIG_ARM_L1_CACHE_SHIFT_6=y | ||||
| # CONFIG_ARM_LPAE is not set | ||||
| CONFIG_ARM_PATCH_IDIV=y | ||||
| CONFIG_ARM_PATCH_PHYS_VIRT=y | ||||
| CONFIG_ARM_QCOM_CPUFREQ=y | ||||
| CONFIG_ARM_QCOM_CPUIDLE=y | ||||
| # CONFIG_ARM_SMMU is not set | ||||
| # CONFIG_ARM_SP805_WATCHDOG is not set | ||||
| CONFIG_ARM_THUMB=y | ||||
| # CONFIG_ARM_THUMBEE is not set | ||||
| CONFIG_ARM_UNWIND=y | ||||
| CONFIG_ARM_VIRT_EXT=y | ||||
| # CONFIG_BINFMT_FLAT is not set | ||||
| CONFIG_BLK_DEV_LOOP=y | ||||
| CONFIG_BLK_MQ_PCI=y | ||||
| # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set | ||||
| CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 | ||||
| CONFIG_BOUNCE=y | ||||
| CONFIG_BUS_TOPOLOGY_ADHOC=y | ||||
| # CONFIG_CACHE_L2X0 is not set | ||||
| CONFIG_CLKDEV_LOOKUP=y | ||||
| CONFIG_CLKSRC_OF=y | ||||
| CONFIG_CLKSRC_PROBE=y | ||||
| CONFIG_CLKSRC_QCOM=y | ||||
| CONFIG_CLONE_BACKWARDS=y | ||||
| CONFIG_COMMON_CLK=y | ||||
| CONFIG_COMMON_CLK_QCOM=y | ||||
| CONFIG_CPUFREQ_DT=y | ||||
| CONFIG_CPUFREQ_DT_PLATDEV=y | ||||
| CONFIG_CPU_32v6K=y | ||||
| CONFIG_CPU_32v7=y | ||||
| CONFIG_CPU_ABRT_EV7=y | ||||
| # CONFIG_CPU_BIG_ENDIAN is not set | ||||
| # CONFIG_CPU_BPREDICT_DISABLE is not set | ||||
| CONFIG_CPU_CACHE_V7=y | ||||
| CONFIG_CPU_CACHE_VIPT=y | ||||
| CONFIG_CPU_COPY_V6=y | ||||
| CONFIG_CPU_CP15=y | ||||
| CONFIG_CPU_CP15_MMU=y | ||||
| CONFIG_CPU_FREQ=y | ||||
| CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y | ||||
| # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set | ||||
| CONFIG_CPU_FREQ_GOV_ATTR_SET=y | ||||
| CONFIG_CPU_FREQ_GOV_COMMON=y | ||||
| # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set | ||||
| CONFIG_CPU_FREQ_GOV_ONDEMAND=y | ||||
| CONFIG_CPU_FREQ_GOV_PERFORMANCE=y | ||||
| # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set | ||||
| # CONFIG_CPU_FREQ_GOV_USERSPACE is not set | ||||
| CONFIG_CPU_FREQ_STAT=y | ||||
| CONFIG_CPU_HAS_ASID=y | ||||
| # CONFIG_CPU_ICACHE_DISABLE is not set | ||||
| CONFIG_CPU_IDLE=y | ||||
| CONFIG_CPU_IDLE_GOV_LADDER=y | ||||
| CONFIG_CPU_IDLE_GOV_MENU=y | ||||
| CONFIG_CPU_PABRT_V7=y | ||||
| CONFIG_CPU_PM=y | ||||
| CONFIG_CPU_RMAP=y | ||||
| CONFIG_CPU_THERMAL=y | ||||
| CONFIG_CPU_TLB_V7=y | ||||
| CONFIG_CPU_V7=y | ||||
| CONFIG_CRC16=y | ||||
| # CONFIG_CRC32_SARWATE is not set | ||||
| CONFIG_CRC32_SLICEBY8=y | ||||
| CONFIG_CRYPTO_AEAD=y | ||||
| CONFIG_CRYPTO_AEAD2=y | ||||
| CONFIG_CRYPTO_CBC=y | ||||
| CONFIG_CRYPTO_CTR=y | ||||
| CONFIG_CRYPTO_DEFLATE=y | ||||
| CONFIG_CRYPTO_DES=y | ||||
| CONFIG_CRYPTO_DEV_QCE=y | ||||
| CONFIG_CRYPTO_DRBG=y | ||||
| CONFIG_CRYPTO_DRBG_HMAC=y | ||||
| CONFIG_CRYPTO_DRBG_MENU=y | ||||
| CONFIG_CRYPTO_ECB=y | ||||
| CONFIG_CRYPTO_GF128MUL=y | ||||
| CONFIG_CRYPTO_HASH=y | ||||
| CONFIG_CRYPTO_HASH2=y | ||||
| CONFIG_CRYPTO_HMAC=y | ||||
| CONFIG_CRYPTO_HW=y | ||||
| CONFIG_CRYPTO_JITTERENTROPY=y | ||||
| CONFIG_CRYPTO_LZO=y | ||||
| CONFIG_CRYPTO_MANAGER=y | ||||
| CONFIG_CRYPTO_MANAGER2=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_SHA256=y | ||||
| CONFIG_CRYPTO_WORKQUEUE=y | ||||
| CONFIG_CRYPTO_XTS=y | ||||
| CONFIG_DCACHE_WORD_ACCESS=y | ||||
| CONFIG_DEBUG_GPIO=y | ||||
| CONFIG_DEBUG_LL=y | ||||
| CONFIG_DEBUG_LL_INCLUDE="debug/msm.S" | ||||
| CONFIG_DEBUG_QCOM_UARTDM=y | ||||
| # CONFIG_DEBUG_UART_8250 is not set | ||||
| CONFIG_DEBUG_UART_PHYS=0x16340000 | ||||
| CONFIG_DEBUG_UART_VIRT=0xf6340000 | ||||
| CONFIG_DEBUG_UNCOMPRESS=y | ||||
| # CONFIG_DEBUG_USER is not set | ||||
| CONFIG_DMADEVICES=y | ||||
| CONFIG_DMA_ENGINE=y | ||||
| CONFIG_DMA_OF=y | ||||
| CONFIG_DMA_VIRTUAL_CHANNELS=y | ||||
| CONFIG_DTC=y | ||||
| CONFIG_DT_IDLE_STATES=y | ||||
| # CONFIG_DWMAC_DWC_QOS_ETH is not set | ||||
| # CONFIG_DWMAC_GENERIC is not set | ||||
| CONFIG_DWMAC_IPQ806X=y | ||||
| CONFIG_DYNAMIC_DEBUG=y | ||||
| CONFIG_EARLY_PRINTK=y | ||||
| CONFIG_EDAC_ATOMIC_SCRUB=y | ||||
| CONFIG_EDAC_SUPPORT=y | ||||
| CONFIG_ETHERNET_PACKET_MANGLE=y | ||||
| CONFIG_FIXED_PHY=y | ||||
| CONFIG_FIX_EARLYCON_MEM=y | ||||
| CONFIG_GENERIC_ALLOCATOR=y | ||||
| CONFIG_GENERIC_BUG=y | ||||
| CONFIG_GENERIC_CLOCKEVENTS=y | ||||
| CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y | ||||
| CONFIG_GENERIC_EARLY_IOREMAP=y | ||||
| CONFIG_GENERIC_IDLE_POLL_SETUP=y | ||||
| CONFIG_GENERIC_IO=y | ||||
| CONFIG_GENERIC_IRQ_SHOW=y | ||||
| CONFIG_GENERIC_IRQ_SHOW_LEVEL=y | ||||
| CONFIG_GENERIC_MSI_IRQ=y | ||||
| CONFIG_GENERIC_MSI_IRQ_DOMAIN=y | ||||
| CONFIG_GENERIC_PCI_IOMAP=y | ||||
| CONFIG_GENERIC_PHY=y | ||||
| CONFIG_GENERIC_PINCONF=y | ||||
| CONFIG_GENERIC_SCHED_CLOCK=y | ||||
| CONFIG_GENERIC_SMP_IDLE_THREAD=y | ||||
| CONFIG_GENERIC_STRNCPY_FROM_USER=y | ||||
| CONFIG_GENERIC_STRNLEN_USER=y | ||||
| CONFIG_GENERIC_TIME_VSYSCALL=y | ||||
| CONFIG_GPIOLIB=y | ||||
| CONFIG_GPIOLIB_IRQCHIP=y | ||||
| CONFIG_GPIO_SYSFS=y | ||||
| CONFIG_HANDLE_DOMAIN_IRQ=y | ||||
| CONFIG_HARDIRQS_SW_RESEND=y | ||||
| CONFIG_HAS_DMA=y | ||||
| CONFIG_HAS_IOMEM=y | ||||
| CONFIG_HAS_IOPORT_MAP=y | ||||
| # CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set | ||||
| CONFIG_HAVE_ARCH_AUDITSYSCALL=y | ||||
| CONFIG_HAVE_ARCH_BITREVERSE=y | ||||
| CONFIG_HAVE_ARCH_JUMP_LABEL=y | ||||
| CONFIG_HAVE_ARCH_KGDB=y | ||||
| CONFIG_HAVE_ARCH_PFN_VALID=y | ||||
| CONFIG_HAVE_ARCH_SECCOMP_FILTER=y | ||||
| CONFIG_HAVE_ARCH_TRACEHOOK=y | ||||
| CONFIG_HAVE_ARM_ARCH_TIMER=y | ||||
| CONFIG_HAVE_ARM_SMCCC=y | ||||
| # CONFIG_HAVE_BOOTMEM_INFO_NODE is not set | ||||
| CONFIG_HAVE_CBPF_JIT=y | ||||
| CONFIG_HAVE_CC_STACKPROTECTOR=y | ||||
| CONFIG_HAVE_CLK=y | ||||
| CONFIG_HAVE_CLK_PREPARE=y | ||||
| CONFIG_HAVE_CONTEXT_TRACKING=y | ||||
| CONFIG_HAVE_C_RECORDMCOUNT=y | ||||
| CONFIG_HAVE_DEBUG_KMEMLEAK=y | ||||
| CONFIG_HAVE_DMA_API_DEBUG=y | ||||
| CONFIG_HAVE_DMA_CONTIGUOUS=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=y | ||||
| CONFIG_HAVE_IDE=y | ||||
| CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y | ||||
| CONFIG_HAVE_MEMBLOCK=y | ||||
| CONFIG_HAVE_MOD_ARCH_SPECIFIC=y | ||||
| CONFIG_HAVE_NET_DSA=y | ||||
| CONFIG_HAVE_OPROFILE=y | ||||
| CONFIG_HAVE_OPTPROBES=y | ||||
| CONFIG_HAVE_PERF_EVENTS=y | ||||
| CONFIG_HAVE_PERF_REGS=y | ||||
| CONFIG_HAVE_PERF_USER_STACK_DUMP=y | ||||
| CONFIG_HAVE_PROC_CPU=y | ||||
| CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y | ||||
| CONFIG_HAVE_SMP=y | ||||
| CONFIG_HAVE_SYSCALL_TRACEPOINTS=y | ||||
| CONFIG_HAVE_UID16=y | ||||
| CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y | ||||
| CONFIG_HIGHMEM=y | ||||
| # CONFIG_HIGHPTE is not set | ||||
| CONFIG_HWMON=y | ||||
| CONFIG_HWSPINLOCK=y | ||||
| CONFIG_HWSPINLOCK_QCOM=y | ||||
| CONFIG_HW_RANDOM=y | ||||
| CONFIG_HW_RANDOM_MSM=y | ||||
| CONFIG_HZ_FIXED=0 | ||||
| CONFIG_I2C=y | ||||
| CONFIG_I2C_BOARDINFO=y | ||||
| CONFIG_I2C_CHARDEV=y | ||||
| CONFIG_I2C_HELPER_AUTO=y | ||||
| CONFIG_I2C_QUP=y | ||||
| CONFIG_INITRAMFS_SOURCE="" | ||||
| CONFIG_IOMMU_HELPER=y | ||||
| # CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set | ||||
| # CONFIG_IOMMU_IO_PGTABLE_LPAE is not set | ||||
| CONFIG_IOMMU_SUPPORT=y | ||||
| # CONFIG_IPQ_GCC_4019 is not set | ||||
| CONFIG_IPQ_GCC_806X=y | ||||
| # CONFIG_IPQ_GCC_8074 is not set | ||||
| # CONFIG_IPQ_LCC_806X is not set | ||||
| CONFIG_IRQCHIP=y | ||||
| CONFIG_IRQ_DOMAIN=y | ||||
| CONFIG_IRQ_DOMAIN_HIERARCHY=y | ||||
| CONFIG_IRQ_FORCED_THREADING=y | ||||
| CONFIG_IRQ_WORK=y | ||||
| CONFIG_KPSS_XCC=y | ||||
| CONFIG_KRAITCC=y | ||||
| CONFIG_KRAIT_CLOCKS=y | ||||
| CONFIG_KRAIT_L2_ACCESSORS=y | ||||
| CONFIG_LIBFDT=y | ||||
| CONFIG_LOCKUP_DETECTOR=y | ||||
| CONFIG_LOCK_SPIN_ON_OWNER=y | ||||
| CONFIG_LZO_COMPRESS=y | ||||
| CONFIG_LZO_DECOMPRESS=y | ||||
| CONFIG_MDIO_BITBANG=y | ||||
| CONFIG_MDIO_BOARDINFO=y | ||||
| CONFIG_MDIO_GPIO=y | ||||
| # CONFIG_MDM_GCC_9615 is not set | ||||
| # CONFIG_MDM_LCC_9615 is not set | ||||
| # CONFIG_MFD_MAX77620 is not set | ||||
| CONFIG_MFD_QCOM_RPM=y | ||||
| # CONFIG_MFD_SPMI_PMIC is not set | ||||
| CONFIG_MFD_SYSCON=y | ||||
| CONFIG_MIGHT_HAVE_CACHE_L2X0=y | ||||
| CONFIG_MIGHT_HAVE_PCI=y | ||||
| CONFIG_MMC=y | ||||
| CONFIG_MMC_ARMMMCI=y | ||||
| CONFIG_MMC_BLOCK=y | ||||
| CONFIG_MMC_BLOCK_MINORS=16 | ||||
| CONFIG_MMC_QCOM_DML=y | ||||
| CONFIG_MMC_SDHCI=y | ||||
| CONFIG_MMC_SDHCI_MSM=y | ||||
| # CONFIG_MMC_SDHCI_PCI is not set | ||||
| CONFIG_MMC_SDHCI_PLTFM=y | ||||
| # CONFIG_MMC_TIFM_SD is not set | ||||
| CONFIG_MODULES_USE_ELF_REL=y | ||||
| CONFIG_MSM_BUS_SCALING=y | ||||
| CONFIG_MSM_GCC_8660=y | ||||
| # CONFIG_MSM_GCC_8916 is not set | ||||
| CONFIG_MSM_GCC_8960=y | ||||
| CONFIG_MSM_GCC_8974=y | ||||
| # CONFIG_MSM_GCC_8994 is not set | ||||
| # CONFIG_MSM_GCC_8996 is not set | ||||
| # CONFIG_MSM_IOMMU is not set | ||||
| # CONFIG_MSM_LCC_8960 is not set | ||||
| CONFIG_MSM_MMCC_8960=y | ||||
| CONFIG_MSM_MMCC_8974=y | ||||
| # CONFIG_MSM_MMCC_8996 is not set | ||||
| CONFIG_MTD_CMDLINE_PARTS=y | ||||
| CONFIG_MTD_M25P80=y | ||||
| CONFIG_MTD_NAND=y | ||||
| CONFIG_MTD_NAND_ECC=y | ||||
| CONFIG_MTD_NAND_QCOM=y | ||||
| # CONFIG_MTD_PHYSMAP_OF_VERSATILE is not set | ||||
| CONFIG_MTD_QCOM_SMEM_PARTS=y | ||||
| CONFIG_MTD_SPI_NOR=y | ||||
| CONFIG_MTD_SPLIT_FIRMWARE=y | ||||
| CONFIG_MTD_SPLIT_FIT_FW=y | ||||
| CONFIG_MTD_UBI=y | ||||
| CONFIG_MTD_UBI_BEB_LIMIT=20 | ||||
| CONFIG_MTD_UBI_BLOCK=y | ||||
| # CONFIG_MTD_UBI_FASTMAP is not set | ||||
| # CONFIG_MTD_UBI_GLUEBI is not set | ||||
| CONFIG_MTD_UBI_WL_THRESHOLD=4096 | ||||
| CONFIG_MULTI_IRQ_HANDLER=y | ||||
| CONFIG_MUTEX_SPIN_ON_OWNER=y | ||||
| CONFIG_NEED_DMA_MAP_STATE=y | ||||
| CONFIG_NEON=y | ||||
| CONFIG_NET_DSA=y | ||||
| CONFIG_NET_DSA_HWMON=y | ||||
| CONFIG_NET_DSA_QCA8K=y | ||||
| CONFIG_NET_DSA_TAG_QCA=y | ||||
| CONFIG_NET_FLOW_LIMIT=y | ||||
| CONFIG_NET_PTP_CLASSIFY=y | ||||
| CONFIG_NET_SWITCHDEV=y | ||||
| CONFIG_NLS=y | ||||
| CONFIG_NO_BOOTMEM=y | ||||
| CONFIG_NO_HZ=y | ||||
| CONFIG_NO_HZ_COMMON=y | ||||
| CONFIG_NO_HZ_IDLE=y | ||||
| CONFIG_NR_CPUS=4 | ||||
| CONFIG_NVMEM=y | ||||
| CONFIG_OF=y | ||||
| CONFIG_OF_ADDRESS=y | ||||
| CONFIG_OF_ADDRESS_PCI=y | ||||
| CONFIG_OF_EARLY_FLATTREE=y | ||||
| CONFIG_OF_FLATTREE=y | ||||
| CONFIG_OF_GPIO=y | ||||
| CONFIG_OF_IRQ=y | ||||
| CONFIG_OF_MDIO=y | ||||
| CONFIG_OF_NET=y | ||||
| CONFIG_OF_PCI=y | ||||
| CONFIG_OF_PCI_IRQ=y | ||||
| CONFIG_OF_RESERVED_MEM=y | ||||
| CONFIG_OLD_SIGACTION=y | ||||
| CONFIG_OLD_SIGSUSPEND3=y | ||||
| CONFIG_PADATA=y | ||||
| CONFIG_PAGE_OFFSET=0xC0000000 | ||||
| CONFIG_PCI=y | ||||
| CONFIG_PCIEAER=y | ||||
| CONFIG_PCIEPORTBUS=y | ||||
| CONFIG_PCIE_DW=y | ||||
| CONFIG_PCIE_QCOM=y | ||||
| CONFIG_PCI_DEBUG=y | ||||
| CONFIG_PCI_DISABLE_COMMON_QUIRKS=y | ||||
| CONFIG_PCI_DOMAINS=y | ||||
| CONFIG_PCI_DOMAINS_GENERIC=y | ||||
| CONFIG_PCI_MSI=y | ||||
| CONFIG_PCI_MSI_IRQ_DOMAIN=y | ||||
| CONFIG_PERF_USE_VMALLOC=y | ||||
| CONFIG_PGTABLE_LEVELS=2 | ||||
| CONFIG_PHYLIB=y | ||||
| # CONFIG_PHY_QCOM_APQ8064_SATA is not set | ||||
| CONFIG_PHY_QCOM_IPQ806X_SATA=y | ||||
| # CONFIG_PHY_QCOM_QMP is not set | ||||
| # CONFIG_PHY_QCOM_QUSB2 is not set | ||||
| # CONFIG_PHY_QCOM_UFS is not set | ||||
| CONFIG_PINCTRL=y | ||||
| CONFIG_PINCTRL_APQ8064=y | ||||
| # CONFIG_PINCTRL_APQ8084 is not set | ||||
| # CONFIG_PINCTRL_IPQ4019 is not set | ||||
| CONFIG_PINCTRL_IPQ8064=y | ||||
| # CONFIG_PINCTRL_IPQ8074 is not set | ||||
| # CONFIG_PINCTRL_MDM9615 is not set | ||||
| CONFIG_PINCTRL_MSM=y | ||||
| # CONFIG_PINCTRL_MSM8660 is not set | ||||
| # CONFIG_PINCTRL_MSM8916 is not set | ||||
| # CONFIG_PINCTRL_MSM8960 is not set | ||||
| # CONFIG_PINCTRL_MSM8994 is not set | ||||
| # CONFIG_PINCTRL_MSM8996 is not set | ||||
| CONFIG_PINCTRL_MSM8X74=y | ||||
| # CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set | ||||
| # CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set | ||||
| # CONFIG_PL330_DMA is not set | ||||
| CONFIG_PM_OPP=y | ||||
| CONFIG_POWER_RESET=y | ||||
| # CONFIG_POWER_RESET_BRCMKONA is not set | ||||
| CONFIG_POWER_RESET_MSM=y | ||||
| CONFIG_POWER_SUPPLY=y | ||||
| CONFIG_PPS=y | ||||
| CONFIG_PRINTK_TIME=y | ||||
| CONFIG_PTP_1588_CLOCK=y | ||||
| CONFIG_QCOM_ADM=y | ||||
| CONFIG_QCOM_BAM_DMA=y | ||||
| CONFIG_QCOM_CLK_RPM=y | ||||
| # CONFIG_QCOM_EBI2 is not set | ||||
| CONFIG_QCOM_GDSC=y | ||||
| CONFIG_QCOM_GSBI=y | ||||
| CONFIG_QCOM_HFPLL=y | ||||
| # CONFIG_QCOM_IOMMU is not set | ||||
| CONFIG_QCOM_PM=y | ||||
| # CONFIG_QCOM_Q6V5_PIL is not set | ||||
| CONFIG_QCOM_QFPROM=y | ||||
| CONFIG_QCOM_RPMCC=y | ||||
| CONFIG_QCOM_SCM=y | ||||
| CONFIG_QCOM_SCM_32=y | ||||
| # CONFIG_QCOM_SMD is not set | ||||
| CONFIG_QCOM_SMEM=y | ||||
| # CONFIG_QCOM_SMP2P is not set | ||||
| # CONFIG_QCOM_SMSM is not set | ||||
| CONFIG_QCOM_TCSR=y | ||||
| CONFIG_QCOM_TSENS=y | ||||
| # CONFIG_QCOM_WCNSS_PIL is not set | ||||
| CONFIG_QCOM_WDT=y | ||||
| # CONFIG_QRTR is not set | ||||
| CONFIG_RAS=y | ||||
| CONFIG_RATIONAL=y | ||||
| CONFIG_RCU_CPU_STALL_TIMEOUT=21 | ||||
| CONFIG_RCU_STALL_COMMON=y | ||||
| CONFIG_REGMAP=y | ||||
| CONFIG_REGMAP_I2C=y | ||||
| CONFIG_REGMAP_MMIO=y | ||||
| CONFIG_REGMAP_SPI=y | ||||
| CONFIG_REGULATOR=y | ||||
| CONFIG_REGULATOR_FIXED_VOLTAGE=y | ||||
| CONFIG_REGULATOR_QCOM_RPM=y | ||||
| # CONFIG_REGULATOR_QCOM_SPMI is not set | ||||
| CONFIG_RESET_CONTROLLER=y | ||||
| CONFIG_RFS_ACCEL=y | ||||
| # CONFIG_RPMSG_QCOM_SMD is not set | ||||
| CONFIG_RPS=y | ||||
| CONFIG_RTC_CLASS=y | ||||
| # CONFIG_RTC_DRV_CMOS is not set | ||||
| CONFIG_RTC_I2C_AND_SPI=y | ||||
| CONFIG_RWSEM_SPIN_ON_OWNER=y | ||||
| CONFIG_RWSEM_XCHGADD_ALGORITHM=y | ||||
| # CONFIG_SCHED_INFO is not set | ||||
| # CONFIG_SCSI_DMA is not set | ||||
| CONFIG_SERIAL_8250_FSL=y | ||||
| # CONFIG_SERIAL_AMBA_PL011 is not set | ||||
| CONFIG_SERIAL_MSM=y | ||||
| CONFIG_SERIAL_MSM_CONSOLE=y | ||||
| CONFIG_SMP=y | ||||
| CONFIG_SMP_ON_UP=y | ||||
| CONFIG_SPARSE_IRQ=y | ||||
| CONFIG_SPI=y | ||||
| # CONFIG_SPI_CADENCE_QUADSPI is not set | ||||
| CONFIG_SPI_MASTER=y | ||||
| CONFIG_SPI_QUP=y | ||||
| CONFIG_SPMI=y | ||||
| CONFIG_SPMI_MSM_PMIC_ARB=y | ||||
| CONFIG_SRCU=y | ||||
| CONFIG_STMMAC_ETH=y | ||||
| CONFIG_STMMAC_PLATFORM=y | ||||
| CONFIG_SWCONFIG=y | ||||
| CONFIG_SWCONFIG_LEDS=y | ||||
| CONFIG_SWIOTLB=y | ||||
| CONFIG_SWPHY=y | ||||
| CONFIG_SWP_EMULATE=y | ||||
| CONFIG_SYS_SUPPORTS_APM_EMULATION=y | ||||
| CONFIG_THERMAL=y | ||||
| CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y | ||||
| CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 | ||||
| CONFIG_THERMAL_GOV_STEP_WISE=y | ||||
| CONFIG_THERMAL_HWMON=y | ||||
| CONFIG_THERMAL_OF=y | ||||
| # CONFIG_THUMB2_KERNEL is not set | ||||
| CONFIG_TICK_CPU_ACCOUNTING=y | ||||
| CONFIG_TREE_RCU=y | ||||
| CONFIG_UBIFS_FS=y | ||||
| CONFIG_UBIFS_FS_ADVANCED_COMPR=y | ||||
| CONFIG_UBIFS_FS_LZO=y | ||||
| CONFIG_UBIFS_FS_ZLIB=y | ||||
| CONFIG_UEVENT_HELPER_PATH="" | ||||
| CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" | ||||
| CONFIG_USB=y | ||||
| CONFIG_USB_COMMON=y | ||||
| # CONFIG_USB_EHCI_HCD is not set | ||||
| # CONFIG_USB_QCOM_8X16_PHY is not set | ||||
| CONFIG_USB_SUPPORT=y | ||||
| # CONFIG_USB_UHCI_HCD is not set | ||||
| CONFIG_USE_OF=y | ||||
| CONFIG_VDSO=y | ||||
| CONFIG_VECTORS_BASE=0xffff0000 | ||||
| CONFIG_VFP=y | ||||
| CONFIG_VFPv3=y | ||||
| CONFIG_WATCHDOG_CORE=y | ||||
| CONFIG_XPS=y | ||||
| CONFIG_XZ_DEC_ARM=y | ||||
| CONFIG_XZ_DEC_BCJ=y | ||||
| CONFIG_ZBOOT_ROM_BSS=0 | ||||
| CONFIG_ZBOOT_ROM_TEXT=0 | ||||
| CONFIG_ZLIB_DEFLATE=y | ||||
| CONFIG_ZLIB_INFLATE=y | ||||
| @@ -0,0 +1,250 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| / { | ||||
| 	model = "Qualcomm IPQ8064/AP148"; | ||||
| 	compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0x1e000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				pins = "gpio12", "gpio13"; | ||||
| 				function = "gsbi4"; | ||||
| 				bias-disable; | ||||
| 			}; | ||||
|  | ||||
| 			spi_pins: spi_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio18", "gpio19", "gpio21"; | ||||
| 					function = "gsbi5"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-none; | ||||
| 				}; | ||||
| 			}; | ||||
| 			nand_pins: nand_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio34", "gpio35", "gpio36", | ||||
| 					       "gpio37", "gpio38", "gpio39", | ||||
| 					       "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					function = "nand"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 				pullups { | ||||
| 					pins = "gpio39"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 				hold { | ||||
| 					pins = "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					bias-bus-hold; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
|  | ||||
| 			/* | ||||
| 			* The i2c device on gsbi4 should not be enabled. | ||||
| 			* On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			* RPM usage. Turning this on in kernel manifests as | ||||
| 			* i2c failure for the RPM. | ||||
| 			*/ | ||||
| 		}; | ||||
|  | ||||
| 		gsbi5: gsbi@1a200000 { | ||||
| 			qcom,mode = <GSBI_PROT_SPI>; | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			spi4: spi@1a280000 { | ||||
| 				status = "ok"; | ||||
| 				spi-max-frequency = <50000000>; | ||||
|  | ||||
| 				pinctrl-0 = <&spi_pins>; | ||||
| 				pinctrl-names = "default"; | ||||
|  | ||||
| 				cs-gpios = <&qcom_pinmux 20 0>; | ||||
|  | ||||
| 				flash: m25p80@0 { | ||||
| 					compatible = "s25fl256s1"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
| 					spi-max-frequency = <50000000>; | ||||
| 					reg = <0>; | ||||
|  | ||||
| 					partitions { | ||||
| 						compatible = "qcom,smem"; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		sata-phy@1b400000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		sata@29000000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		nand@1ac00000 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&nand_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			cs0 { | ||||
| 				reg = <0>; | ||||
| 				compatible = "qcom,nandcs"; | ||||
|  | ||||
| 				nand-ecc-strength = <4>; | ||||
| 				nand-bus-width = <8>; | ||||
| 				nand-ecc-step-size = <512>; | ||||
|  | ||||
| 				partitions { | ||||
| 					compatible = "qcom,smem"; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1,500 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
|  | ||||
| / { | ||||
| 	model = "TP-Link Archer C2600"; | ||||
| 	compatible = "tplink,c2600", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0x1e000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
|  | ||||
| 		led-boot = &power; | ||||
| 		led-failsafe = &general; | ||||
| 		led-running = &power; | ||||
| 		led-upgrade = &general; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio16", "gpio54", "gpio65"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			led_pins: led_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio6", "gpio7", "gpio8", "gpio9", "gpio26", "gpio33", | ||||
| 					       "gpio53", "gpio66"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			spi_pins: spi_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio18", "gpio19", "gpio21"; | ||||
| 					function = "gsbi5"; | ||||
| 					bias-pull-down; | ||||
| 				}; | ||||
|  | ||||
| 				data { | ||||
| 					pins = "gpio18", "gpio19"; | ||||
| 					drive-strength = <10>; | ||||
| 				}; | ||||
|  | ||||
| 				cs { | ||||
| 					pins = "gpio20"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
|  | ||||
| 				clk { | ||||
| 					pins = "gpio21"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb0_pwr_en_pin: usb0_pwr_en_pin { | ||||
| 				mux { | ||||
| 					pins = "gpio25"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-pull-up; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb1_pwr_en_pin: usb1_pwr_en_pin { | ||||
| 				mux { | ||||
| 					pins = "gpio23"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-pull-up; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		gsbi5: gsbi@1a200000 { | ||||
| 			qcom,mode = <GSBI_PROT_SPI>; | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			spi5: spi@1a280000 { | ||||
| 				status = "ok"; | ||||
|  | ||||
| 				pinctrl-0 = <&spi_pins>; | ||||
| 				pinctrl-names = "default"; | ||||
|  | ||||
| 				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; | ||||
|  | ||||
| 				flash: m25p80@0 { | ||||
| 					compatible = "jedec,spi-nor"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
| 					spi-max-frequency = <50000000>; | ||||
| 					reg = <0>; | ||||
|  | ||||
| 					SBL1@0 { | ||||
| 						label = "SBL1"; | ||||
| 						reg = <0x0 0x20000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					MIBIB@20000 { | ||||
| 						label = "MIBIB"; | ||||
| 						reg = <0x20000 0x20000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL2@40000 { | ||||
| 						label = "SBL2"; | ||||
| 						reg = <0x40000 0x20000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL3@60000 { | ||||
| 						label = "SBL3"; | ||||
| 						reg = <0x60000 0x30000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					DDRCONFIG@90000 { | ||||
| 						label = "DDRCONFIG"; | ||||
| 						reg = <0x90000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SSD@a0000 { | ||||
| 						label = "SSD"; | ||||
| 						reg = <0xa0000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					TZ@b0000 { | ||||
| 						label = "TZ"; | ||||
| 						reg = <0xb0000 0x30000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					RPM@e0000 { | ||||
| 						label = "RPM"; | ||||
| 						reg = <0xe0000 0x20000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					fs-uboot@100000 { | ||||
| 						label = "fs-uboot"; | ||||
| 						reg = <0x100000 0x70000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					uboot-env@170000 { | ||||
| 						label = "uboot-env"; | ||||
| 						reg = <0x170000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					radio@1b0000 { | ||||
| 						label = "radio"; | ||||
| 						reg = <0x1b0000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					os-image@1f0000 { | ||||
| 						label = "os-image"; | ||||
| 						reg = <0x1f0000 0x200000>; | ||||
| 					}; | ||||
|  | ||||
| 					rootfs@3f0000 { | ||||
| 						label = "rootfs"; | ||||
| 						reg = <0x3f0000 0x1b00000>; | ||||
| 					}; | ||||
|  | ||||
| 					defaultmac: default-mac@1ef0000 { | ||||
| 						label = "default-mac"; | ||||
| 						reg = <0x1ef0000 0x00200>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					pin@1ef0200 { | ||||
| 						label = "pin"; | ||||
| 						reg = <0x1ef0200 0x00200>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					product-info@1ef0400 { | ||||
| 						label = "product-info"; | ||||
| 						reg = <0x1ef0400 0x0fc00>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					partition-table@1f00000 { | ||||
| 						label = "partition-table"; | ||||
| 						reg = <0x1f00000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					soft-version@1f10000 { | ||||
| 						label = "soft-version"; | ||||
| 						reg = <0x1f10000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					support-list@1f20000 { | ||||
| 						label = "support-list"; | ||||
| 						reg = <0x1f20000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					profile@1f30000 { | ||||
| 						label = "profile"; | ||||
| 						reg = <0x1f30000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					default-config@1f40000 { | ||||
| 						label = "default-config"; | ||||
| 						reg = <0x1f40000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					user-config@1f50000 { | ||||
| 						label = "user-config"; | ||||
| 						reg = <0x1f50000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					qos-db@1f90000 { | ||||
| 						label = "qos-db"; | ||||
| 						reg = <0x1f90000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					usb-config@1fd0000 { | ||||
| 						label = "usb-config"; | ||||
| 						reg = <0x1fd0000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					log@1fe0000 { | ||||
| 						label = "log"; | ||||
| 						reg = <0x1fe0000 0x20000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb0_pwr_en_pin>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb1_pwr_en_pin>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			mtd-mac-address = <&defaultmac 0x8>; | ||||
| 			mtd-mac-address-increment = <1>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
|  | ||||
| 			mtd-mac-address = <&defaultmac 0x8>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 49 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 		}; | ||||
|  | ||||
| 		ledswitch { | ||||
| 			label = "ledswitch"; | ||||
| 			gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_LIGHTS_TOGGLE>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		lan { | ||||
| 			label = "c2600:white:lan"; | ||||
| 			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb4 { | ||||
| 			label = "c2600:white:usb_4"; | ||||
| 			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb2 { | ||||
| 			label = "c2600:white:usb_2"; | ||||
| 			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "c2600:white:wps"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_amber { | ||||
| 			label = "c2600:amber:wan"; | ||||
| 			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_LOW>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_white { | ||||
| 			label = "c2600:white:wan"; | ||||
| 			gpios = <&qcom_pinmux 33 GPIO_ACTIVE_LOW>; | ||||
| 		}; | ||||
|  | ||||
| 		power: power { | ||||
| 			label = "c2600:white:power"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		general: general { | ||||
| 			label = "c2600:white:general"; | ||||
| 			gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1,419 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
|  | ||||
| / { | ||||
| 	model = "Netgear Nighthawk X4 D7800"; | ||||
| 	compatible = "netgear,d7800", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0xe000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
|  | ||||
| 		led-boot = &power_white; | ||||
| 		led-failsafe = &power_amber; | ||||
| 		led-running = &power_white; | ||||
| 		led-upgrade = &power_amber; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		bootargs = "rootfstype=squashfs noinitrd"; | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio6", "gpio54", "gpio65"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			led_pins: led_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", | ||||
| 						"gpio24","gpio26", "gpio53", "gpio64"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			nand_pins: nand_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio34", "gpio35", "gpio36", | ||||
| 					       "gpio37", "gpio38", "gpio39", | ||||
| 					       "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					function = "nand"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 				pullups { | ||||
| 					pins = "gpio39"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 				hold { | ||||
| 					pins = "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					bias-bus-hold; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb0_pwr_en_pins: usb0_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio15"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb1_pwr_en_pins: usb1_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio16", "gpio68"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		sata-phy@1b400000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		sata@29000000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb0_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb1_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 			reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&pcie0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&pcie1_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		nand@1ac00000 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&nand_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <1>; | ||||
|  | ||||
| 			cs0 { | ||||
| 				reg = <0>; | ||||
| 				compatible = "qcom,nandcs"; | ||||
|  | ||||
| 				nand-ecc-strength = <4>; | ||||
| 				nand-bus-width = <8>; | ||||
| 				nand-ecc-step-size = <512>; | ||||
|  | ||||
| 				partitions { | ||||
| 					compatible = "fixed-partitions"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
|  | ||||
| 					qcadata@0 { | ||||
| 						label = "qcadata"; | ||||
| 						reg = <0x0000000 0x0c80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL@c80000 { | ||||
| 						label = "APPSBL"; | ||||
| 						reg = <0x0c80000 0x0500000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBLENV@1180000 { | ||||
| 						label = "APPSBLENV"; | ||||
| 						reg = <0x1180000 0x0080000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					art: art@1200000 { | ||||
| 						label = "art"; | ||||
| 						reg = <0x1200000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					artbak: art@1340000 { | ||||
| 						label = "artbak"; | ||||
| 						reg = <0x1340000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					kernel@1480000 { | ||||
| 						label = "kernel"; | ||||
| 						reg = <0x1480000 0x0200000>; | ||||
| 					}; | ||||
|  | ||||
| 					ubi@1680000 { | ||||
| 						label = "ubi"; | ||||
| 						reg = <0x1680000 0x1E00000>; | ||||
| 					}; | ||||
|  | ||||
| 					netgear@3480000 { | ||||
| 						label = "netgear"; | ||||
| 						reg = <0x3480000 0x4480000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					reserve@7900000 { | ||||
| 						label = "reserve"; | ||||
| 						reg = <0x7900000 0x0700000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					firmware@1480000 { | ||||
| 						label = "firmware"; | ||||
| 						reg = <0x1480000 0x2000000>; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			phy-handle = <&phy4>; | ||||
| 			qcom,id = <1>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			mtd-mac-address = <&art 6>; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
|  | ||||
| 			mtd-mac-address = <&art 0>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		usb1 { | ||||
| 			label = "d7800:white:usb1"; | ||||
| 			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb2 { | ||||
| 			label = "d7800:white:usb2"; | ||||
| 			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power_amber: power_amber { | ||||
| 			label = "d7800:amber:power"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_white { | ||||
| 			label = "d7800:white:wan"; | ||||
| 			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_amber { | ||||
| 			label = "d7800:amber:wan"; | ||||
| 			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "d7800:white:wps"; | ||||
| 			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		esata { | ||||
| 			label = "d7800:white:esata"; | ||||
| 			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power_white: power_white { | ||||
| 			label = "d7800:white:power"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "d7800:white:wifi"; | ||||
| 			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1,236 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| / { | ||||
| 	model = "Qualcomm IPQ8064/DB149"; | ||||
| 	compatible = "qcom,ipq8064-db149", "qcom,ipq8064"; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	alias { | ||||
| 		serial0 = &uart2; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				pins = "gpio12", "gpio13"; | ||||
| 				function = "gsbi4"; | ||||
| 				bias-disable; | ||||
| 			}; | ||||
|  | ||||
| 			spi_pins: spi_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio18", "gpio19", "gpio21"; | ||||
| 					function = "gsbi5"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-none; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii0_pins: rgmii0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio2", "gpio66"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi2: gsbi@12480000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			uart2: serial@12490000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi5: gsbi@1a200000 { | ||||
| 			qcom,mode = <GSBI_PROT_SPI>; | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			spi4: spi@1a280000 { | ||||
| 				status = "ok"; | ||||
| 				spi-max-frequency = <50000000>; | ||||
|  | ||||
| 				pinctrl-0 = <&spi_pins>; | ||||
| 				pinctrl-names = "default"; | ||||
|  | ||||
| 				cs-gpios = <&qcom_pinmux 20 0>; | ||||
|  | ||||
| 				flash: m25p80@0 { | ||||
| 					compatible = "s25fl256s1"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
| 					spi-max-frequency = <50000000>; | ||||
| 					reg = <0>; | ||||
| 					m25p,fast-read; | ||||
|  | ||||
| 					partition@0 { | ||||
| 						label = "lowlevel_init"; | ||||
| 						reg = <0x0 0x1b0000>; | ||||
| 					}; | ||||
|  | ||||
| 					partition@1 { | ||||
| 						label = "u-boot"; | ||||
| 						reg = <0x1b0000 0x80000>; | ||||
| 					}; | ||||
|  | ||||
| 					partition@2 { | ||||
| 						label = "u-boot-env"; | ||||
| 						reg = <0x230000 0x40000>; | ||||
| 					}; | ||||
|  | ||||
| 					partition@3 { | ||||
| 						label = "caldata"; | ||||
| 						reg = <0x270000 0x40000>; | ||||
| 					}; | ||||
|  | ||||
| 					partition@4 { | ||||
| 						label = "firmware"; | ||||
| 						reg = <0x2b0000 0x1d50000>; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		sata-phy@1b400000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		sata@29000000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie2: pci@1b900000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; | ||||
|  | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 				>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
|  | ||||
| 			phy6: ethernet-phy@6 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <6>; | ||||
| 			}; | ||||
|  | ||||
| 			phy7: ethernet-phy@7 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <7>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac0: ethernet@37000000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <0>; | ||||
| 			phy-handle = <&phy4>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <1>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
| 			phy-handle = <&phy6>; | ||||
| 		}; | ||||
|  | ||||
| 		gmac3: ethernet@37600000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <3>; | ||||
| 			phy-handle = <&phy7>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
| @@ -0,0 +1,406 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
|  | ||||
| / { | ||||
| 	model = "Linksys EA8500 WiFi Router"; | ||||
| 	compatible = "linksys,ea8500", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0x1e000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
|  | ||||
| 		led-boot = &power; | ||||
| 		led-failsafe = &power; | ||||
| 		led-running = &power; | ||||
| 		led-upgrade = &power; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		bootargs = "console=ttyMSM0,115200n8"; | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 		append-rootblock = "ubi.mtd=";  /* append to bootargs adding the root deviceblock nbr from bootloader */ | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio65", "gpio67", "gpio68"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			led_pins: led_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio6", "gpio53", "gpio54"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			nand_pins: nand_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio34", "gpio35", "gpio36", | ||||
| 					       "gpio37", "gpio38", "gpio39", | ||||
| 					       "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					function = "nand"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 				pullups { | ||||
| 					pins = "gpio39"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 				hold { | ||||
| 					pins = "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					bias-bus-hold; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		sata-phy@1b400000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		sata@29000000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie2: pci@1b900000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		nand@1ac00000 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&nand_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			cs0 { | ||||
| 				reg = <0>; | ||||
| 				compatible = "qcom,nandcs"; | ||||
|  | ||||
| 				nand-ecc-strength = <4>; | ||||
| 				nand-bus-width = <8>; | ||||
| 				nand-ecc-step-size = <512>; | ||||
|  | ||||
| 				partitions { | ||||
| 					compatible = "fixed-partitions"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
|  | ||||
| 					SBL1@0 { | ||||
| 						label = "SBL1"; | ||||
| 						reg = <0x0000000 0x0040000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					MIBIB@40000 { | ||||
| 						label = "MIBIB"; | ||||
| 						reg = <0x0040000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL2@180000 { | ||||
| 						label = "SBL2"; | ||||
| 						reg = <0x0180000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL3@2c0000 { | ||||
| 						label = "SBL3"; | ||||
| 						reg = <0x02c0000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					DDRCONFIG@540000 { | ||||
| 						label = "DDRCONFIG"; | ||||
| 						reg = <0x0540000 0x0120000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SSD@660000 { | ||||
| 						label = "SSD"; | ||||
| 						reg = <0x0660000 0x0120000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					TZ@780000 { | ||||
| 						label = "TZ"; | ||||
| 						reg = <0x0780000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					RPM@a00000 { | ||||
| 						label = "RPM"; | ||||
| 						reg = <0x0a00000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					art: art@c80000 { | ||||
| 						label = "art"; | ||||
| 						reg = <0x0c80000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL@dc0000 { | ||||
| 						label = "APPSBL"; | ||||
| 						reg = <0x0dc0000 0x0100000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					u_env@ec0000 { | ||||
| 						label = "u_env"; | ||||
| 						reg = <0x0ec0000 0x0040000>; | ||||
| 					}; | ||||
|  | ||||
| 					s_env@f00000 { | ||||
| 						label = "s_env"; | ||||
| 						reg = <0x0f00000 0x0040000>; | ||||
| 					}; | ||||
|  | ||||
| 					devinfo@f40000 { | ||||
| 						label = "devinfo"; | ||||
| 						reg = <0x0f40000 0x0040000>; | ||||
| 					}; | ||||
|  | ||||
| 					linux@f80000 { | ||||
| 						label = "kernel1"; | ||||
| 						reg = <0x0f80000 0x2800000>;  /* 3 MB spill to rootfs*/ | ||||
| 					}; | ||||
|  | ||||
| 					rootfs@1280000 { | ||||
| 						label = "rootfs1"; | ||||
| 						reg = <0x1280000 0x2500000>; | ||||
| 					}; | ||||
|  | ||||
| 					linux2@3780000 { | ||||
| 						label = "kernel2"; | ||||
| 						reg = <0x3780000 0x2800000>; | ||||
| 					}; | ||||
|  | ||||
| 					rootfs2@3a80000 { | ||||
| 						label = "rootfs2"; | ||||
| 						reg = <0x3a80000 0x2500000>; | ||||
| 					}; | ||||
|  | ||||
| 					syscfg@5f80000 { | ||||
| 						label = "syscfg"; | ||||
| 						reg = <0x5f80000 0x2080000>; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
| 			qcom,phy_mdio_addr = <4>; | ||||
| 			qcom,poll_required = <1>; | ||||
| 			qcom,rgmii_delay = <0>; | ||||
| 			qcom,emulation = <0>; | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
| 		//lan | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
| 			qcom,phy_mdio_addr = <0>;	/* none */ | ||||
| 			qcom,poll_required = <0>;	/* no polling */ | ||||
| 			qcom,rgmii_delay = <0>; | ||||
| 			qcom,emulation = <0>; | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		adm_dma: dma@18300000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART >; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "ea8500:green:wps"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power: power { | ||||
| 			label = "ea8500:white:power"; | ||||
| 			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "ea8500:green:wifi"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
| @@ -0,0 +1,394 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
| #include <dt-bindings/soc/qcom,tcsr.h> | ||||
|  | ||||
| / { | ||||
| 	model = "Netgear Nighthawk X4 R7500"; | ||||
| 	compatible = "netgear,r7500", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0xe000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
|  | ||||
| 		led-boot = &power_white; | ||||
| 		led-failsafe = &power_amber; | ||||
| 		led-running = &power_white; | ||||
| 		led-upgrade = &power_amber; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		bootargs = "rootfstype=squashfs noinitrd"; | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio6", "gpio54", "gpio65"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			led_pins: led_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", | ||||
| 						"gpio24","gpio26", "gpio53", "gpio64"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			nand_pins: nand_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio34", "gpio35", "gpio36", | ||||
| 					       "gpio37", "gpio38", "gpio39", | ||||
| 					       "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					function = "nand"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 				pullups { | ||||
| 					pins = "gpio39"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 				hold { | ||||
| 					pins = "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					bias-bus-hold; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		sata-phy@1b400000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		sata@29000000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			clocks = <&gcc USB30_0_UTMI_CLK>; | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			clocks = <&gcc USB30_0_MASTER_CLK>; | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			clocks = <&gcc USB30_1_UTMI_CLK>; | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			clocks = <&gcc USB30_1_MASTER_CLK>; | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			clocks = <&gcc USB30_1_MASTER_CLK>; | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			clocks = <&gcc USB30_0_MASTER_CLK>; | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		nand@1ac00000 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&nand_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			cs0 { | ||||
| 				reg = <0>; | ||||
| 				compatible = "qcom,nandcs"; | ||||
|  | ||||
| 				nand-ecc-strength = <4>; | ||||
| 				nand-bus-width = <8>; | ||||
| 				nand-ecc-step-size = <512>; | ||||
|  | ||||
| 				partitions { | ||||
| 					compatible = "fixed-partitions"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
|  | ||||
| 					qcadata@0 { | ||||
| 						label = "qcadata"; | ||||
| 						reg = <0x0000000 0x0c80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL@c80000 { | ||||
| 						label = "APPSBL"; | ||||
| 						reg = <0x0c80000 0x0500000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBLENV@1180000 { | ||||
| 						label = "APPSBLENV"; | ||||
| 						reg = <0x1180000 0x0080000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					art: art@1200000 { | ||||
| 						label = "art"; | ||||
| 						reg = <0x1200000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					kernel@1340000 { | ||||
| 						label = "kernel"; | ||||
| 						reg = <0x1340000 0x0200000>; | ||||
| 					}; | ||||
|  | ||||
| 					ubi@1540000 { | ||||
| 						label = "ubi"; | ||||
| 						reg = <0x1540000 0x1800000>; | ||||
| 					}; | ||||
|  | ||||
| 					netgear@2d40000 { | ||||
| 						label = "netgear"; | ||||
| 						reg = <0x2d40000 0x0c00000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					reserve@3940000 { | ||||
| 						label = "reserve"; | ||||
| 						reg = <0x3940000 0x46c0000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					firmware@1340000 { | ||||
| 						label = "firmware"; | ||||
| 						reg = <0x1340000 0x1a00000>; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			mtd-mac-address = <&art 6>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
|  | ||||
| 			mtd-mac-address = <&art 0>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		usb1 { | ||||
| 			label = "r7500:white:usb1"; | ||||
| 			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb2 { | ||||
| 			label = "r7500:white:usb2"; | ||||
| 			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power_amber: power_amber { | ||||
| 			label = "r7500:amber:power"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_white { | ||||
| 			label = "r7500:white:wan"; | ||||
| 			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_amber { | ||||
| 			label = "r7500:amber:wan"; | ||||
| 			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "r7500:white:wps"; | ||||
| 			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		esata { | ||||
| 			label = "r7500:white:esata"; | ||||
| 			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power_white: power_white { | ||||
| 			label = "r7500:white:power"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "r7500:white:wifi"; | ||||
| 			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &tcsr { | ||||
| 	qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>; | ||||
| 	compatible = "qcom,tcsr"; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1,425 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
|  | ||||
| / { | ||||
| 	model = "Netgear Nighthawk X4 R7500v2"; | ||||
| 	compatible = "netgear,r7500v2", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0x1e000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
|  | ||||
| 		rsvd@5fe00000 { | ||||
| 			reg = <0x5fe00000 0x200000>; | ||||
| 			reusable; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
|  | ||||
| 		led-boot = &power; | ||||
| 		led-failsafe = &power; | ||||
| 		led-running = &power; | ||||
| 		led-upgrade = &power; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		bootargs = "rootfstype=squashfs noinitrd"; | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio6", "gpio54", "gpio65"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			led_pins: led_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", | ||||
| 						"gpio24","gpio26", "gpio53", "gpio64"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			nand_pins: nand_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio34", "gpio35", "gpio36", | ||||
| 					       "gpio37", "gpio38", "gpio39", | ||||
| 					       "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					function = "nand"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 				pullups { | ||||
| 					pins = "gpio39"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 				hold { | ||||
| 					pins = "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					bias-bus-hold; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb0_pwr_en_pins: usb0_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio15"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb1_pwr_en_pins: usb1_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio16", "gpio68"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		sata-phy@1b400000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		sata@29000000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb0_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb1_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 			reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; | ||||
| 			pinctrl-0 = <&pcie0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; | ||||
| 			pinctrl-0 = <&pcie1_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		nand@1ac00000 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&nand_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			cs0 { | ||||
| 				reg = <0>; | ||||
| 				compatible = "qcom,nandcs"; | ||||
|  | ||||
| 				nand-ecc-strength = <4>; | ||||
| 				nand-bus-width = <8>; | ||||
| 				nand-ecc-step-size = <512>; | ||||
|  | ||||
| 				partitions { | ||||
| 					compatible = "fixed-partitions"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
|  | ||||
| 					qcadata@0 { | ||||
| 						label = "qcadata"; | ||||
| 						reg = <0x0000000 0x0c80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL@c80000 { | ||||
| 						label = "APPSBL"; | ||||
| 						reg = <0x0c80000 0x0500000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBLENV@1180000 { | ||||
| 						label = "APPSBLENV"; | ||||
| 						reg = <0x1180000 0x0080000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					art: art@1200000 { | ||||
| 						label = "art"; | ||||
| 						reg = <0x1200000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					artbak: art@1340000 { | ||||
| 						label = "artbak"; | ||||
| 						reg = <0x1340000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					kernel@1480000 { | ||||
| 						label = "kernel"; | ||||
| 						reg = <0x1480000 0x0200000>; | ||||
| 					}; | ||||
|  | ||||
| 					ubi@1680000 { | ||||
| 						label = "ubi"; | ||||
| 						reg = <0x1680000 0x1E00000>; | ||||
| 					}; | ||||
|  | ||||
| 					netgear@3480000 { | ||||
| 						label = "netgear"; | ||||
| 						reg = <0x3480000 0x4480000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					reserve@7900000 { | ||||
| 						label = "reserve"; | ||||
| 						reg = <0x7900000 0x0700000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					firmware@1480000 { | ||||
| 						label = "firmware"; | ||||
| 						reg = <0x1480000 0x2000000>; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0xaa545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			mtd-mac-address = <&art 6>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
|  | ||||
| 			mtd-mac-address = <&art 0>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		usb1 { | ||||
| 			label = "r7500v2:amber:usb1"; | ||||
| 			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb3 { | ||||
| 			label = "r7500v2:amber:usb3"; | ||||
| 			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		status { | ||||
| 			label = "r7500v2:amber:status"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		internet { | ||||
| 			label = "r7500v2:white:internet"; | ||||
| 			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan { | ||||
| 			label = "r7500v2:white:wan"; | ||||
| 			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "r7500v2:white:wps"; | ||||
| 			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		esata { | ||||
| 			label = "r7500v2:white:esata"; | ||||
| 			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power: power { | ||||
| 			label = "r7500v2:white:power"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "r7500v2:white:wifi"; | ||||
| 			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1,424 @@ | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
|  | ||||
| / { | ||||
| 	model = "TP-Link Archer VR2600v"; | ||||
| 	compatible = "tplink,vr2600v", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0x1e000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
|  | ||||
| 		led-boot = &power; | ||||
| 		led-failsafe = &general; | ||||
| 		led-running = &power; | ||||
| 		led-upgrade = &general; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			led_pins: led_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio7", "gpio8", "gpio9", "gpio16", "gpio17", | ||||
| 						"gpio26", "gpio53", "gpio56", "gpio66"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio54", "gpio64", "gpio65", "gpio67", "gpio68"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			spi_pins: spi_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio18", "gpio19", "gpio21"; | ||||
| 					function = "gsbi5"; | ||||
| 					bias-pull-down; | ||||
| 				}; | ||||
|  | ||||
| 				data { | ||||
| 					pins = "gpio18", "gpio19"; | ||||
| 					drive-strength = <10>; | ||||
| 				}; | ||||
|  | ||||
| 				cs { | ||||
| 					pins = "gpio20"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
|  | ||||
| 				clk { | ||||
| 					pins = "gpio21"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		gsbi5: gsbi@1a200000 { | ||||
| 			qcom,mode = <GSBI_PROT_SPI>; | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			spi4: spi@1a280000 { | ||||
| 				status = "ok"; | ||||
|  | ||||
| 				pinctrl-0 = <&spi_pins>; | ||||
| 				pinctrl-names = "default"; | ||||
|  | ||||
| 				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; | ||||
|  | ||||
| 				flash: W25Q128@0 { | ||||
| 					compatible = "jedec,spi-nor"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
| 					spi-max-frequency = <50000000>; | ||||
| 					reg = <0>; | ||||
|  | ||||
| 					SBL1@0 { | ||||
| 						label = "SBL1"; | ||||
| 						reg = <0x0 0x20000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					MIBIB@20000 { | ||||
| 						label = "MIBIB"; | ||||
| 						reg = <0x20000 0x20000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL2@40000 { | ||||
| 						label = "SBL2"; | ||||
| 						reg = <0x40000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL3@80000 { | ||||
| 						label = "SBL3"; | ||||
| 						reg = <0x80000 0x80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					DDRCONFIG@100000 { | ||||
| 						label = "DDRCONFIG"; | ||||
| 						reg = <0x100000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SSD@110000 { | ||||
| 						label = "SSD"; | ||||
| 						reg = <0x110000 0x10000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					TZ@120000 { | ||||
| 						label = "TZ"; | ||||
| 						reg = <0x120000 0x80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					RPM@1a0000 { | ||||
| 						label = "RPM"; | ||||
| 						reg = <0x1a0000 0x80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL@220000 { | ||||
| 						label = "APPSBL"; | ||||
| 						reg = <0x220000 0x80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBLENV@2a0000 { | ||||
| 						label = "APPSBLENV"; | ||||
| 						reg = <0x2a0000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					OLDART@2e0000 { | ||||
| 						label = "OLDART"; | ||||
| 						reg = <0x2e0000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					kernel@320000 { | ||||
| 						label = "kernel"; | ||||
| 						reg = <0x320000 0x200000>; | ||||
| 					}; | ||||
|  | ||||
| 					rootfs@520000 { | ||||
| 						label = "rootfs"; | ||||
| 						reg = <0x520000 0xa60000>; | ||||
| 					}; | ||||
|  | ||||
| 					defaultmac: default-mac@0xfaf100 { | ||||
| 						label = "default-mac"; | ||||
| 						reg = <0xfaf100 0x00200>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					ART@fc0000 { | ||||
| 						label = "ART"; | ||||
| 						reg = <0xfc0000 0x40000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			mtd-mac-address = <&defaultmac 0>; | ||||
| 			mtd-mac-address-increment = <1>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
|  | ||||
| 			mtd-mac-address = <&defaultmac 0>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 		}; | ||||
|  | ||||
| 		dect { | ||||
| 			label = "dect"; | ||||
| 			gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_PHONE>; | ||||
| 		}; | ||||
|  | ||||
| 		ledswitch { | ||||
| 			label = "ledswitch"; | ||||
| 			gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_LIGHTS_TOGGLE>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		dsl { | ||||
| 			label = "vr2600v:white:dsl"; | ||||
| 			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb { | ||||
| 			label = "vr2600v:white:usb"; | ||||
| 			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		lan { | ||||
| 			label = "vr2600v:white:lan"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wlan2g { | ||||
| 			label = "vr2600v:white:wlan2g"; | ||||
| 			gpios = <&qcom_pinmux 16 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wlan5g { | ||||
| 			label = "vr2600v:white:wlan5g"; | ||||
| 			gpios = <&qcom_pinmux 17 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power: power { | ||||
| 			label = "vr2600v:white:power"; | ||||
| 			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		phone { | ||||
| 			label = "vr2600v:white:phone"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan { | ||||
| 			label = "vr2600v:white:wan"; | ||||
| 			gpios = <&qcom_pinmux 56 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		general: general { | ||||
| 			label = "vr2600v:white:general"; | ||||
| 			gpios = <&qcom_pinmux 66 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1,570 @@ | ||||
| /* | ||||
|  *  BSD LICENSE | ||||
|  * | ||||
|  *  Copyright (C) 2017 Christian Mehlis <christian@m3hlis.de> | ||||
|  *  Copyright (C) 2018 Mathias Kresin <dev@kresin.me> | ||||
|  *  All rights reserved. | ||||
|  * | ||||
|  *  Redistribution and use in source and binary forms, with or without | ||||
|  *  modification, are permitted provided that the following conditions | ||||
|  *  are met: | ||||
|  * | ||||
|  *    1. Redistributions of source code must retain the above copyright | ||||
|  *       notice, this list of conditions and the following disclaimer. | ||||
|  *    2. Redistributions in binary form must reproduce the above copyright | ||||
|  *       notice, this list of conditions and the following disclaimer in the | ||||
|  *       documentation and/or other materials provided with the distribution. | ||||
|  *    3. Neither the names of the copyright holders nor the names of any | ||||
|  *       contributors may be used to endorse or promote products derived | ||||
|  *       from this software without specific prior written permission. | ||||
|  * | ||||
|  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
|  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
|  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
|  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
|  *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
|  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
|  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
|  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
|  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
|  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
|  | ||||
| #include "qcom-ipq8064-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
| #include <dt-bindings/soc/qcom,tcsr.h> | ||||
|  | ||||
| / { | ||||
| 	compatible = "compex,wpq864", "qcom,ipq8064"; | ||||
| 	model = "Compex WPQ864"; | ||||
|  | ||||
| 	aliases { | ||||
| 		mdio-gpio0 = &mdio0; | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		ethernet0 = &gmac1; | ||||
| 		ethernet1 = &gmac0; | ||||
|  | ||||
| 		led-boot = &led_pass; | ||||
| 		led-failsafe = &led_fail; | ||||
| 		led-running = &led_pass; | ||||
| 		led-upgrade = &led_pass; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		rpm@108000  { | ||||
| 			pinctrl-0 = <&rpm_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		nand@1ac00000 { | ||||
| 			status = "okay"; | ||||
|  | ||||
| 			pinctrl-0 = <&nand_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			mt29f2g08abbeah4@0 { | ||||
| 				compatible = "qcom,nandcs"; | ||||
|  | ||||
| 				reg = <0>; | ||||
|  | ||||
| 				nand-ecc-strength = <4>; | ||||
| 				nand-bus-width = <8>; | ||||
| 				nand-ecc-step-size = <512>; | ||||
|  | ||||
| 				partitions { | ||||
| 					compatible = "fixed-partitions"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
|  | ||||
| 					SBL1@0 { | ||||
| 						label = "SBL1"; | ||||
| 						reg = <0x0000000 0x0040000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					MIBIB@40000 { | ||||
| 						label = "MIBIB"; | ||||
| 						reg = <0x0040000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL2@180000 { | ||||
| 						label = "SBL2"; | ||||
| 						reg = <0x0180000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL3@2c0000 { | ||||
| 						label = "SBL3"; | ||||
| 						reg = <0x02c0000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					DDRCONFIG@540000 { | ||||
| 						label = "DDRCONFIG"; | ||||
| 						reg = <0x0540000 0x0120000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SSD@660000 { | ||||
| 						label = "SSD"; | ||||
| 						reg = <0x0660000 0x0120000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					TZ@780000 { | ||||
| 						label = "TZ"; | ||||
| 						reg = <0x0780000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					RPM@a00000 { | ||||
| 						label = "RPM"; | ||||
| 						reg = <0x0a00000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL@c80000 { | ||||
| 						label = "APPSBL"; | ||||
| 						reg = <0x0c80000 0x0500000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBLENV@1180000 { | ||||
| 						label = "APPSBLENV"; | ||||
| 						reg = <0x1180000 0x0080000>; | ||||
| 					}; | ||||
|  | ||||
| 					ART@1200000 { | ||||
| 						label = "ART"; | ||||
| 						reg = <0x1200000 0x0140000>; | ||||
| 					}; | ||||
|  | ||||
| 					ubi@1340000 { | ||||
| 						label = "ubi"; | ||||
| 						reg = <0x1340000 0x4000000>; | ||||
| 					}; | ||||
|  | ||||
| 					BOOTCONFIG@1340000 { | ||||
| 						label = "BOOTCONFIG"; | ||||
| 						reg = <0x5340000 0x0060000>; | ||||
| 					}; | ||||
|  | ||||
| 					SBL2-1@53a0000- { | ||||
| 						label = "SBL2_1"; | ||||
| 						reg = <0x53a0000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SBL3-1@54e0000 { | ||||
| 						label = "SBL3_1"; | ||||
| 						reg = <0x54e0000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					DDRCONFIG-1@5760000 { | ||||
| 						label = "DDRCONFIG_1"; | ||||
| 						reg = <0x5760000 0x0120000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					SSD-1@5880000 { | ||||
| 						label = "SSD_1"; | ||||
| 						reg = <0x5880000 0x0120000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					TZ-1@59a0000 { | ||||
| 						label = "TZ_1"; | ||||
| 						reg = <0x59a0000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					RPM-1@5c20000 { | ||||
| 						label = "RPM_1"; | ||||
| 						reg = <0x5c20000 0x0280000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					BOOTCONFIG1@5ea0000 { | ||||
| 						label = "BOOTCONFIG1"; | ||||
| 						reg = <0x5ea0000 0x0060000>; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL-1@5f00000 { | ||||
| 						label = "APPSBL_1"; | ||||
| 						reg = <0x5f00000 0x0500000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					ubi-1@6400000 { | ||||
| 						label = "ubi_1"; | ||||
| 						reg = <0x6400000 0x4000000>; | ||||
| 					}; | ||||
|  | ||||
| 					unused@a400000 { | ||||
| 						label = "unused"; | ||||
| 						reg = <0xa400000 0x5c00000>; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	mdio0: mdio { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <0>; | ||||
|  | ||||
| 		compatible = "virtual,mdio-gpio"; | ||||
|  | ||||
| 		pinctrl-0 = <&mdio0_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
|  | ||||
| 		ethernet-phy@0 { | ||||
| 			device_type = "ethernet-phy"; | ||||
| 			reg = <0>; | ||||
| 			qca,ar8327-initvals = < | ||||
| 				0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 				0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 				0x0000c 0x80        /* PAD6_MODE */ | ||||
| 				0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 				0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 				0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 				0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 				>; | ||||
| 		}; | ||||
|  | ||||
| 		ethernet-phy@4 { | ||||
| 			device_type = "ethernet-phy"; | ||||
| 			reg = <4>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
|  | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		rss4 { | ||||
| 			label = "wpq864:green:rss4"; | ||||
| 			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		rss3 { | ||||
| 			label = "wpq864:green:rss3"; | ||||
| 			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		rss2 { | ||||
| 			label = "wpq864:orange:rss2"; | ||||
| 			gpios = <&qcom_pinmux 25 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		rss1 { | ||||
| 			label = "wpq864:red:rss1"; | ||||
| 			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		led_pass: pass { | ||||
| 			label = "wpq864:green:pass"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		led_fail: fail { | ||||
| 			label = "wpq864:green:fail"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb { | ||||
| 			label = "wpq864:green:usb"; | ||||
| 			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb-pcie { | ||||
| 			label = "wpq864:green:usb-pcie"; | ||||
| 			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
|  | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	beeper { | ||||
| 		compatible = "gpio-beeper"; | ||||
|  | ||||
| 		pinctrl-0 = <&beeper_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		gpios = <&qcom_pinmux 55 GPIO_ACTIVE_HIGH>; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "okay"; | ||||
| }; | ||||
|  | ||||
| &gmac1 { | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	pinctrl-0 = <&rgmii2_pins>; | ||||
| 	pinctrl-names = "default"; | ||||
|  | ||||
| 	phy-mode = "rgmii"; | ||||
| 	qcom,id = <1>; | ||||
|  | ||||
| 	fixed-link { | ||||
| 		speed = <1000>; | ||||
| 		full-duplex; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &gmac2 { | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	phy-mode = "sgmii"; | ||||
| 	qcom,id = <2>; | ||||
|  | ||||
| 	fixed-link { | ||||
| 		speed = <1000>; | ||||
| 		full-duplex; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &gsbi4 { | ||||
| 	status = "okay"; | ||||
| 	qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| }; | ||||
|  | ||||
| &gsbi4_serial { | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	pinctrl-0 = <&uart0_pins>; | ||||
| 	pinctrl-names = "default"; | ||||
| }; | ||||
|  | ||||
| &gsbi5 { | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	qcom,mode = <GSBI_PROT_SPI>; | ||||
|  | ||||
| 	spi@1a280000 { | ||||
| 		status = "okay"; | ||||
|  | ||||
| 		pinctrl-0 = <&spi_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; | ||||
|  | ||||
| 		s25fl256s1@0 { | ||||
| 			compatible = "jedec,spi-nor"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <1>; | ||||
| 			reg = <0>; | ||||
| 			spi-max-frequency = <50000000>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &hs_phy_0 {		/* USB3 port 0 HS phy */ | ||||
| 	status = "okay"; | ||||
| }; | ||||
|  | ||||
| &hs_phy_1 {		/* USB3 port 1 HS phy */ | ||||
| 	status = "okay"; | ||||
| }; | ||||
|  | ||||
| &ss_phy_0 {		/* USB3 port 0 SS phy */ | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	rx_eq = <2>; | ||||
| 	tx_deamp_3_5db = <32>; | ||||
| 	mpll = <160>; | ||||
| }; | ||||
|  | ||||
| &ss_phy_1 {		/* USB3 port 1 SS phy */ | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	rx_eq = <2>; | ||||
| 	tx_deamp_3_5db = <32>; | ||||
| 	mpll = <160>; | ||||
| }; | ||||
|  | ||||
| &pcie0 { | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	/delete-property/ pinctrl-0; | ||||
| 	/delete-property/ pinctrl-names; | ||||
| 	/delete-property/ perst-gpios; | ||||
| }; | ||||
|  | ||||
| &pcie1 { | ||||
| 	status = "okay"; | ||||
| }; | ||||
|  | ||||
| &pcie2 { | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	/delete-property/ pinctrl-0; | ||||
| 	/delete-property/ pinctrl-names; | ||||
| 	/delete-property/ perst-gpios; | ||||
| }; | ||||
|  | ||||
| &qcom_pinmux { | ||||
| 	pinctrl-names = "default"; | ||||
| 	pinctrl-0 = <&state_default>; | ||||
|  | ||||
| 	state_default: pinctrl0 { | ||||
| 		pcie0_pcie2_perst { | ||||
| 			pins = "gpio3"; | ||||
| 			function = "gpio"; | ||||
| 			drive-strength = <2>; | ||||
| 			bias-disable; | ||||
| 			output-high; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	led_pins: led_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio7", "gpio8", "gpio9",  "gpio22", | ||||
| 			       "gpio23", "gpio24", "gpio25", "gpio53"; | ||||
| 			function = "gpio"; | ||||
| 			drive-strength = <2>; | ||||
| 			bias-pull-up; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	button_pins: button_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio54"; | ||||
| 			function = "gpio"; | ||||
| 			drive-strength = <2>; | ||||
| 			bias-pull-up; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	beeper_pins: beeper_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio55"; | ||||
| 			function = "gpio"; | ||||
| 			drive-strength = <2>; | ||||
| 			bias-pull-up; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	rpm_pins: rpm_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio12", "gpio13"; | ||||
| 			function = "gsbi4"; | ||||
| 			drive-strength = <10>; | ||||
| 			bias-disable; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	uart0_pins: uart0_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio10", "gpio11"; | ||||
| 			function = "gsbi4"; | ||||
| 			drive-strength = <10>; | ||||
| 			bias-disable; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	spi_pins: spi_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio18", "gpio19"; | ||||
| 			function = "gsbi5"; | ||||
| 			drive-strength = <10>; | ||||
| 			bias-pull-down; | ||||
| 		}; | ||||
|  | ||||
| 		clk { | ||||
| 			pins = "gpio21"; | ||||
| 			function = "gsbi5"; | ||||
| 			drive-strength = <12>; | ||||
| 			bias-pull-down; | ||||
| 		}; | ||||
|  | ||||
| 		cs { | ||||
| 			pins = "gpio20"; | ||||
| 			function = "gpio"; | ||||
| 			drive-strength = <10>; | ||||
| 			bias-pull-up; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	nand_pins: nand_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio34", "gpio35", "gpio36", "gpio37", | ||||
| 			       "gpio38", "gpio39", "gpio40", "gpio41", | ||||
| 			       "gpio42", "gpio43", "gpio44", "gpio45", | ||||
| 			       "gpio46", "gpio47"; | ||||
| 			function = "nand"; | ||||
| 			drive-strength = <10>; | ||||
| 			bias-disable; | ||||
| 		}; | ||||
|  | ||||
| 		pullups { | ||||
| 			pins = "gpio39"; | ||||
| 			bias-pull-up; | ||||
| 		}; | ||||
|  | ||||
| 		hold { | ||||
| 			pins = "gpio40", "gpio41", "gpio42", "gpio43", | ||||
| 			       "gpio44", "gpio45", "gpio46", "gpio47"; | ||||
| 			bias-bus-hold; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	mdio0_pins: mdio0_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio0", "gpio1"; | ||||
| 			function = "gpio"; | ||||
| 			drive-strength = <8>; | ||||
| 			bias-disable; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	rgmii2_pins: rgmii2_pins { | ||||
| 		mux { | ||||
| 			pins = "gpio27", "gpio28", "gpio29", "gpio30", | ||||
| 			       "gpio31", "gpio32", "gpio51", "gpio52", | ||||
| 			       "gpio59", "gpio60", "gpio61", "gpio62"; | ||||
| 			function = "rgmii2"; | ||||
| 			drive-strength = <8>; | ||||
| 			bias-disable; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &usb3_0 { | ||||
| 	status = "okay"; | ||||
| }; | ||||
|  | ||||
|  | ||||
| &usb3_1 { | ||||
| 	status = "okay"; | ||||
| }; | ||||
|  | ||||
| &tcsr { | ||||
| 	qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>; | ||||
| }; | ||||
							
								
								
									
										1333
									
								
								target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064.dtsi
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1333
									
								
								target/linux/ipq806x/files-4.14/arch/arm/boot/dts/qcom-ipq8064.dtsi
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,393 @@ | ||||
| #include "qcom-ipq8065-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
|  | ||||
| / { | ||||
| 	model = "ZyXEL NBG6817"; | ||||
| 	compatible = "zyxel,nbg6817", "qcom,ipq8065"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0x1e000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
| 		sdcc1 = &sdcc1; | ||||
|  | ||||
| 		led-boot = &power; | ||||
| 		led-failsafe = &power; | ||||
| 		led-running = &power; | ||||
| 		led-upgrade = &power; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		bootargs = "rootfstype=squashfs,ext4 rootwait noinitrd"; | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 		append-rootblock = "root=/dev/mmcblk0p"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio53", "gpio54", "gpio65"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			led_pins: led_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio9", "gpio26", "gpio33", "gpio64"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-down; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
|  | ||||
| 				clk { | ||||
| 					pins = "gpio1"; | ||||
| 					input-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
|  | ||||
| 				tx { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ; | ||||
| 					input-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			spi_pins: spi_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio18", "gpio19", "gpio21"; | ||||
| 					function = "gsbi5"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-none; | ||||
| 				}; | ||||
|  | ||||
| 				cs { | ||||
| 					pins = "gpio20"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb0_pwr_en_pins: usb0_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio16", "gpio17"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
|  | ||||
| 				pwr { | ||||
| 					pins = "gpio17"; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
|  | ||||
| 				ovc { | ||||
| 					pins = "gpio16"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb1_pwr_en_pins: usb1_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio14", "gpio15"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
|  | ||||
| 				pwr { | ||||
| 					pins = "gpio14"; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
|  | ||||
| 				ovc { | ||||
| 					pins = "gpio15"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		gsbi5: gsbi@1a200000 { | ||||
| 			qcom,mode = <GSBI_PROT_SPI>; | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			spi4: spi@1a280000 { | ||||
| 				status = "ok"; | ||||
|  | ||||
| 				pinctrl-0 = <&spi_pins>; | ||||
| 				pinctrl-names = "default"; | ||||
|  | ||||
| 				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; | ||||
|  | ||||
| 				flash: m25p80@0 { | ||||
| 					compatible = "jedec,spi-nor"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
| 					spi-max-frequency = <51200000>; | ||||
| 					reg = <0>; | ||||
|  | ||||
| 					partitions { | ||||
| 						compatible = "qcom,smem"; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb0_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb1_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 			reset-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; | ||||
| 			pinctrl-0 = <&pcie0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			reset-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; | ||||
| 			pinctrl-0 = <&pcie1_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0xaa545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					0x00970 0x1e864443  /* QM_PORT0_CTRL0 */ | ||||
| 					0x00974 0x000001c6  /* QM_PORT0_CTRL1 */ | ||||
| 					0x00978 0x19008643  /* QM_PORT1_CTRL0 */ | ||||
| 					0x0097c 0x000001c6  /* QM_PORT1_CTRL1 */ | ||||
| 					0x00980 0x19008643  /* QM_PORT2_CTRL0 */ | ||||
| 					0x00984 0x000001c6  /* QM_PORT2_CTRL1 */ | ||||
| 					0x00988 0x19008643  /* QM_PORT3_CTRL0 */ | ||||
| 					0x0098c 0x000001c6  /* QM_PORT3_CTRL1 */ | ||||
| 					0x00990 0x19008643  /* QM_PORT4_CTRL0 */ | ||||
| 					0x00994 0x000001c6  /* QM_PORT4_CTRL1 */ | ||||
| 					0x00998 0x1e864443  /* QM_PORT5_CTRL0 */ | ||||
| 					0x0099c 0x000001c6  /* QM_PORT5_CTRL1 */ | ||||
| 					0x009a0 0x1e864443  /* QM_PORT6_CTRL0 */ | ||||
| 					0x009a4 0x000001c6  /* QM_PORT6_CTRL1 */ | ||||
| 					>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
| 			qcom,phy_mdio_addr = <4>; | ||||
| 			qcom,poll_required = <0>; | ||||
| 			qcom,rgmii_delay = <1>; | ||||
| 			qcom,phy_mii_type = <0>; | ||||
| 			qcom,emulation = <0>; | ||||
| 			qcom,irq = <255>; | ||||
| 			mdiobus = <&mdio0>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
| 			qcom,phy_mdio_addr = <0>;	/* none */ | ||||
| 			qcom,poll_required = <0>;	/* no polling */ | ||||
| 			qcom,rgmii_delay = <0>; | ||||
| 			qcom,phy_mii_type = <1>; | ||||
| 			qcom,emulation = <0>; | ||||
| 			qcom,irq = <258>; | ||||
| 			mdiobus = <&mdio0>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		amba { | ||||
| 			sdcc1: sdcc@12400000 { | ||||
| 				status = "okay"; | ||||
| 			}; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 			linux,input-type = <EV_SW>; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		internet { | ||||
| 			label = "nbg6817:white:internet"; | ||||
| 			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		power: power { | ||||
| 			label = "nbg6817:white:power"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		wifi2g { | ||||
| 			label = "nbg6817:amber:wifi2g"; | ||||
| 			gpios = <&qcom_pinmux 33 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		/* wifi2g amber from the manual is missing */ | ||||
|  | ||||
| 		wifi5g { | ||||
| 			label = "nbg6817:amber:wifi5g"; | ||||
| 			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		/* wifi5g amber from the manual is missing */ | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1,575 @@ | ||||
| #include "qcom-ipq8065-v1.0.dtsi" | ||||
|  | ||||
| #include <dt-bindings/input/input.h> | ||||
|  | ||||
| / { | ||||
| 	model = "Netgear Nighthawk X4S R7800"; | ||||
| 	compatible = "netgear,r7800", "qcom,ipq8065", "qcom,ipq8064"; | ||||
|  | ||||
| 	memory@0 { | ||||
| 		reg = <0x42000000 0x1e000000>; | ||||
| 		device_type = "memory"; | ||||
| 	}; | ||||
|  | ||||
| 	reserved-memory { | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <1>; | ||||
| 		ranges; | ||||
| 		rsvd@41200000 { | ||||
| 			reg = <0x41200000 0x300000>; | ||||
| 			no-map; | ||||
| 		}; | ||||
|  | ||||
| 		rsvd@5fe00000 { | ||||
| 			reg = <0x5fe00000 0x200000>; | ||||
| 			reusable; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	aliases { | ||||
| 		serial0 = &gsbi4_serial; | ||||
| 		mdio-gpio0 = &mdio0; | ||||
|  | ||||
| 		led-boot = &power_white; | ||||
| 		led-failsafe = &power_amber; | ||||
| 		led-running = &power_white; | ||||
| 		led-upgrade = &power_amber; | ||||
| 	}; | ||||
|  | ||||
| 	chosen { | ||||
| 		linux,stdout-path = "serial0:115200n8"; | ||||
| 	}; | ||||
|  | ||||
| 	soc { | ||||
| 		pinmux@800000 { | ||||
| 			button_pins: button_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio6", "gpio54", "gpio65"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <2>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			i2c4_pins: i2c4_pinmux { | ||||
| 				mux { | ||||
| 					pins = "gpio12", "gpio13"; | ||||
| 					function = "gsbi4"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			led_pins: led_pins { | ||||
| 				pins = "gpio7", "gpio8", "gpio9", "gpio22", "gpio23", | ||||
| 					"gpio24","gpio26", "gpio53", "gpio64"; | ||||
| 				function = "gpio"; | ||||
| 				drive-strength = <2>; | ||||
| 				bias-pull-down; | ||||
| 			}; | ||||
|  | ||||
| 			nand_pins: nand_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio34", "gpio35", "gpio36", | ||||
| 					       "gpio37", "gpio38", "gpio39", | ||||
| 					       "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					function = "nand"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
| 				pullups { | ||||
| 					pins = "gpio39"; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
| 				hold { | ||||
| 					pins = "gpio40", "gpio41", "gpio42", | ||||
| 					       "gpio43", "gpio44", "gpio45", | ||||
| 					       "gpio46", "gpio47"; | ||||
| 					bias-bus-hold; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			mdio0_pins: mdio0_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio0", "gpio1"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
|  | ||||
| 				clk { | ||||
| 					pins = "gpio1"; | ||||
| 					input-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			rgmii2_pins: rgmii2_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", | ||||
| 					       "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; | ||||
| 					function = "rgmii2"; | ||||
| 					drive-strength = <8>; | ||||
| 					bias-disable; | ||||
| 				}; | ||||
|  | ||||
| 				tx { | ||||
| 					pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32" ; | ||||
| 					input-disable; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			spi_pins: spi_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio18", "gpio19", "gpio21"; | ||||
| 					function = "gsbi5"; | ||||
| 					bias-pull-down; | ||||
| 				}; | ||||
|  | ||||
| 				data { | ||||
| 					pins = "gpio18", "gpio19"; | ||||
| 					drive-strength = <10>; | ||||
| 				}; | ||||
|  | ||||
| 				cs { | ||||
| 					pins = "gpio20"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
|  | ||||
| 				clk { | ||||
| 					pins = "gpio21"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			spi6_pins: spi6_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio55", "gpio56", "gpio58"; | ||||
| 					function = "gsbi6"; | ||||
| 					bias-pull-down; | ||||
| 				}; | ||||
|  | ||||
| 				mosi { | ||||
| 					pins = "gpio55"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
|  | ||||
| 				miso { | ||||
| 					pins = "gpio56"; | ||||
| 					drive-strength = <14>; | ||||
| 				}; | ||||
|  | ||||
| 				cs { | ||||
| 					pins = "gpio57"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-pull-up; | ||||
| 				}; | ||||
|  | ||||
| 				clk { | ||||
| 					pins = "gpio58"; | ||||
| 					drive-strength = <12>; | ||||
| 				}; | ||||
|  | ||||
| 				reset { | ||||
| 					pins = "gpio33"; | ||||
| 					drive-strength = <10>; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb0_pwr_en_pins: usb0_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio15"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
|  | ||||
| 			usb1_pwr_en_pins: usb1_pwr_en_pins { | ||||
| 				mux { | ||||
| 					pins = "gpio16", "gpio68"; | ||||
| 					function = "gpio"; | ||||
| 					drive-strength = <12>; | ||||
| 					bias-pull-down; | ||||
| 					output-high; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi@16300000 { | ||||
| 			qcom,mode = <GSBI_PROT_I2C_UART>; | ||||
| 			status = "ok"; | ||||
| 			serial@16340000 { | ||||
| 				status = "ok"; | ||||
| 			}; | ||||
| 			/* | ||||
| 			 * The i2c device on gsbi4 should not be enabled. | ||||
| 			 * On ipq806x designs gsbi4 i2c is meant for exclusive | ||||
| 			 * RPM usage. Turning this on in kernel manifests as | ||||
| 			 * i2c failure for the RPM. | ||||
| 			 */ | ||||
| 		}; | ||||
|  | ||||
| 		gsbi5: gsbi@1a200000 { | ||||
| 			qcom,mode = <GSBI_PROT_SPI>; | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			spi5: spi@1a280000 { | ||||
| 				status = "ok"; | ||||
|  | ||||
| 				pinctrl-0 = <&spi_pins>; | ||||
| 				pinctrl-names = "default"; | ||||
|  | ||||
| 				cs-gpios = <&qcom_pinmux 20 GPIO_ACTIVE_HIGH>; | ||||
|  | ||||
| 				flash: m25p80@0 { | ||||
| 					compatible = "jedec,spi-nor"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
| 					spi-max-frequency = <50000000>; | ||||
| 					reg = <0>; | ||||
|  | ||||
| 					partitions { | ||||
| 						compatible = "qcom,smem"; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gsbi6: gsbi@16500000 { | ||||
| 			qcom,mode = <GSBI_PROT_SPI>; | ||||
| 			status = "ok"; | ||||
| 			spi6: spi@16580000 { | ||||
| 				status = "ok"; | ||||
|  | ||||
| 				pinctrl-0 = <&spi6_pins>; | ||||
| 				pinctrl-names = "default"; | ||||
|  | ||||
| 				cs-gpios = <&qcom_pinmux 57 GPIO_ACTIVE_HIGH>; | ||||
|  | ||||
| 				spi-nor@0 { | ||||
| 					compatible = "jedec,spi-nor"; | ||||
| 					reg = <0>; | ||||
| 					spi-max-frequency = <6000000>; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		sata-phy@1b400000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		sata@29000000 { | ||||
| 			ports-implemented = <0x1>; | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8800 {		/* USB3 port 1 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@100f8830 {		/* USB3 port 1 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8800 {		/* USB3 port 0 HS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		phy@110f8830 {		/* USB3 port 0 SS phy */ | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@0 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb0_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		usb30@1 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&usb1_pwr_en_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie0: pci@1b500000 { | ||||
| 			status = "ok"; | ||||
| 		}; | ||||
|  | ||||
| 		pcie1: pci@1b700000 { | ||||
| 			status = "ok"; | ||||
| 			force_gen1 = <1>; | ||||
| 		}; | ||||
|  | ||||
| 		nand@1ac00000 { | ||||
| 			status = "ok"; | ||||
|  | ||||
| 			pinctrl-0 = <&nand_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			cs0 { | ||||
| 				reg = <0>; | ||||
| 				compatible = "qcom,nandcs"; | ||||
|  | ||||
| 				nand-ecc-strength = <4>; | ||||
| 				nand-bus-width = <8>; | ||||
| 				nand-ecc-step-size = <512>; | ||||
|  | ||||
| 				partitions { | ||||
| 					compatible = "fixed-partitions"; | ||||
| 					#address-cells = <1>; | ||||
| 					#size-cells = <1>; | ||||
|  | ||||
| 					qcadata@0 { | ||||
| 						label = "qcadata"; | ||||
| 						reg = <0x0000000 0x0c80000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBL@c80000 { | ||||
| 						label = "APPSBL"; | ||||
| 						reg = <0x0c80000 0x0500000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					APPSBLENV@1180000 { | ||||
| 						label = "APPSBLENV"; | ||||
| 						reg = <0x1180000 0x0080000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					art: art@1200000 { | ||||
| 						label = "art"; | ||||
| 						reg = <0x1200000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					artbak: art@1340000 { | ||||
| 						label = "artbak"; | ||||
| 						reg = <0x1340000 0x0140000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					kernel@1480000 { | ||||
| 						label = "kernel"; | ||||
| 						reg = <0x1480000 0x0200000>; | ||||
| 					}; | ||||
|  | ||||
| 					ubi@1680000 { | ||||
| 						label = "ubi"; | ||||
| 						reg = <0x1680000 0x1E00000>; | ||||
| 					}; | ||||
|  | ||||
| 					netgear@3480000 { | ||||
| 						label = "netgear"; | ||||
| 						reg = <0x3480000 0x4480000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					reserve@7900000 { | ||||
| 						label = "reserve"; | ||||
| 						reg = <0x7900000 0x0700000>; | ||||
| 						read-only; | ||||
| 					}; | ||||
|  | ||||
| 					firmware@1480000 { | ||||
| 						label = "firmware"; | ||||
| 						reg = <0x1480000 0x2000000>; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		mdio0: mdio { | ||||
| 			compatible = "virtual,mdio-gpio"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 			gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH &qcom_pinmux 0 GPIO_ACTIVE_HIGH>; | ||||
| 			pinctrl-0 = <&mdio0_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
|  | ||||
| 			phy0: ethernet-phy@0 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <0>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x00004 0x7600000   /* PAD0_MODE */ | ||||
| 					0x00008 0x1000000   /* PAD5_MODE */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					0x000e4 0xaa545     /* MAC_POWER_SEL */ | ||||
| 					0x000e0 0xc74164de  /* SGMII_CTRL */ | ||||
| 					0x0007c 0x4e        /* PORT0_STATUS */ | ||||
| 					0x00094 0x4e        /* PORT6_STATUS */ | ||||
| 					0x00970 0x1e864443  /* QM_PORT0_CTRL0 */ | ||||
| 					0x00974 0x000001c6  /* QM_PORT0_CTRL1 */ | ||||
| 					0x00978 0x19008643  /* QM_PORT1_CTRL0 */ | ||||
| 					0x0097c 0x000001c6  /* QM_PORT1_CTRL1 */ | ||||
| 					0x00980 0x19008643  /* QM_PORT2_CTRL0 */ | ||||
| 					0x00984 0x000001c6  /* QM_PORT2_CTRL1 */ | ||||
| 					0x00988 0x19008643  /* QM_PORT3_CTRL0 */ | ||||
| 					0x0098c 0x000001c6  /* QM_PORT3_CTRL1 */ | ||||
| 					0x00990 0x19008643  /* QM_PORT4_CTRL0 */ | ||||
| 					0x00994 0x000001c6  /* QM_PORT4_CTRL1 */ | ||||
| 					0x00998 0x1e864443  /* QM_PORT5_CTRL0 */ | ||||
| 					0x0099c 0x000001c6  /* QM_PORT5_CTRL1 */ | ||||
| 					0x009a0 0x1e864443  /* QM_PORT6_CTRL0 */ | ||||
| 					0x009a4 0x000001c6  /* QM_PORT6_CTRL1 */ | ||||
| 					>; | ||||
| 				qca,ar8327-vlans = < | ||||
| 					0x1	0x5e	    /* VLAN1 Ports 1/2/3/4/6 */ | ||||
| 					0x2	0x21	    /* VLAN2 Ports 0/5 */ | ||||
| 				>; | ||||
| 			}; | ||||
|  | ||||
| 			phy4: ethernet-phy@4 { | ||||
| 				device_type = "ethernet-phy"; | ||||
| 				reg = <4>; | ||||
| 				qca,ar8327-initvals = < | ||||
| 					0x000e4 0x6a545     /* MAC_POWER_SEL */ | ||||
| 					0x0000c 0x80        /* PAD6_MODE */ | ||||
| 					>; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac1: ethernet@37200000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "rgmii"; | ||||
| 			qcom,id = <1>; | ||||
| 			qcom,phy_mdio_addr = <4>; | ||||
| 			qcom,poll_required = <0>; | ||||
| 			qcom,rgmii_delay = <1>; | ||||
| 			qcom,phy_mii_type = <0>; | ||||
| 			qcom,emulation = <0>; | ||||
| 			qcom,irq = <255>; | ||||
| 			mdiobus = <&mdio0>; | ||||
|  | ||||
| 			pinctrl-0 = <&rgmii2_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
|  | ||||
| 			mtd-mac-address = <&art 6>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		gmac2: ethernet@37400000 { | ||||
| 			status = "ok"; | ||||
| 			phy-mode = "sgmii"; | ||||
| 			qcom,id = <2>; | ||||
| 			qcom,phy_mdio_addr = <0>;	/* none */ | ||||
| 			qcom,poll_required = <0>;	/* no polling */ | ||||
| 			qcom,rgmii_delay = <0>; | ||||
| 			qcom,phy_mii_type = <1>; | ||||
| 			qcom,emulation = <0>; | ||||
| 			qcom,irq = <258>; | ||||
| 			mdiobus = <&mdio0>; | ||||
|  | ||||
| 			mtd-mac-address = <&art 0>; | ||||
|  | ||||
| 			fixed-link { | ||||
| 				speed = <1000>; | ||||
| 				full-duplex; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		rpm@108000 { | ||||
| 			pinctrl-0 = <&i2c4_pins>; | ||||
| 			pinctrl-names = "default"; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-keys { | ||||
| 		compatible = "gpio-keys"; | ||||
| 		pinctrl-0 = <&button_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "wifi"; | ||||
| 			gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RFKILL>; | ||||
| 			debounce-interval = <60>; | ||||
| 			wakeup-source; | ||||
| 		}; | ||||
|  | ||||
| 		reset { | ||||
| 			label = "reset"; | ||||
| 			gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_RESTART>; | ||||
| 			debounce-interval = <60>; | ||||
| 			wakeup-source; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "wps"; | ||||
| 			gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; | ||||
| 			linux,code = <KEY_WPS_BUTTON>; | ||||
| 			debounce-interval = <60>; | ||||
| 			wakeup-source; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	gpio-leds { | ||||
| 		compatible = "gpio-leds"; | ||||
| 		pinctrl-0 = <&led_pins>; | ||||
| 		pinctrl-names = "default"; | ||||
|  | ||||
| 		power_white: power_white { | ||||
| 			label = "r7800:white:power"; | ||||
| 			gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; | ||||
| 			default-state = "keep"; | ||||
| 		}; | ||||
|  | ||||
| 		power_amber: power_amber { | ||||
| 			label = "r7800:amber:power"; | ||||
| 			gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_white { | ||||
| 			label = "r7800:white:wan"; | ||||
| 			gpios = <&qcom_pinmux 22 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wan_amber { | ||||
| 			label = "r7800:amber:wan"; | ||||
| 			gpios = <&qcom_pinmux 23 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb1 { | ||||
| 			label = "r7800:white:usb1"; | ||||
| 			gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		usb2 { | ||||
| 			label = "r7800:white:usb2"; | ||||
| 			gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		esata { | ||||
| 			label = "r7800:white:esata"; | ||||
| 			gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wifi { | ||||
| 			label = "r7800:white:wifi"; | ||||
| 			gpios = <&qcom_pinmux 64 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
|  | ||||
| 		wps { | ||||
| 			label = "r7800:white:wps"; | ||||
| 			gpios = <&qcom_pinmux 24 GPIO_ACTIVE_HIGH>; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| &adm_dma { | ||||
| 	status = "ok"; | ||||
| }; | ||||
| @@ -0,0 +1 @@ | ||||
| #include "qcom-ipq8065.dtsi" | ||||
| @@ -0,0 +1,164 @@ | ||||
| #include "qcom-ipq8064.dtsi" | ||||
|  | ||||
| / { | ||||
| 	model = "Qualcomm IPQ8065"; | ||||
| 	compatible = "qcom,ipq8065", "qcom,ipq8064"; | ||||
|  | ||||
| 	qcom,pvs { | ||||
| 		qcom,pvs-format-a; | ||||
| 		qcom,speed0-pvs0-bin-v0 = | ||||
| 			< 1725000000 1262500 >, | ||||
| 			< 1400000000 1175000 >, | ||||
| 			< 1000000000 1100000 >, | ||||
| 			 < 800000000 1050000 >, | ||||
| 			 < 600000000 1000000 >, | ||||
| 			 < 384000000 975000 >; | ||||
| 		qcom,speed0-pvs1-bin-v0 = | ||||
| 			< 1725000000 1225000 >, | ||||
| 			< 1400000000 1150000 >, | ||||
| 			< 1000000000 1075000 >, | ||||
| 			 < 800000000 1025000 >, | ||||
| 			 < 600000000 975000 >, | ||||
| 			 < 384000000 950000 >; | ||||
| 		qcom,speed0-pvs2-bin-v0 = | ||||
| 			< 1725000000 1200000 >, | ||||
| 			< 1400000000 1125000 >, | ||||
| 			< 1000000000 1050000 >, | ||||
| 			 < 800000000 1000000 >, | ||||
| 			 < 600000000 950000 >, | ||||
| 			 < 384000000 925000 >; | ||||
| 		qcom,speed0-pvs3-bin-v0 = | ||||
| 			< 1725000000 1175000 >, | ||||
| 			< 1400000000 1100000 >, | ||||
| 			< 1000000000 1025000 >, | ||||
| 			 < 800000000 975000 >, | ||||
| 			 < 600000000 925000 >, | ||||
| 			 < 384000000 900000 >; | ||||
| 		qcom,speed0-pvs4-bin-v0 = | ||||
| 			< 1725000000 1150000 >, | ||||
| 			< 1400000000 1075000 >, | ||||
| 			< 1000000000 1000000 >, | ||||
| 			 < 800000000 950000 >, | ||||
| 			 < 600000000 900000 >, | ||||
| 			 < 384000000 875000 >; | ||||
| 		qcom,speed0-pvs5-bin-v0 = | ||||
| 			< 1725000000 1100000 >, | ||||
| 			< 1400000000 1025000 >, | ||||
| 			< 1000000000 950000 >, | ||||
| 			 < 800000000 900000 >, | ||||
| 			 < 600000000 850000 >, | ||||
| 			 < 384000000 825000 >; | ||||
| 		qcom,speed0-pvs6-bin-v0 = | ||||
| 			< 1725000000 1050000 >, | ||||
| 			< 1400000000 975000 >, | ||||
| 			< 1000000000 900000 >, | ||||
| 			 < 800000000 850000 >, | ||||
| 			 < 600000000 800000 >, | ||||
| 			 < 384000000 775000 >; | ||||
| 	}; | ||||
|  | ||||
| 	soc: soc { | ||||
|  | ||||
| 		rpm@108000 { | ||||
|  | ||||
| 			regulators { | ||||
|  | ||||
| 				smb208_s2a: s2a { | ||||
| 					regulator-min-microvolt = <775000>; | ||||
| 					regulator-max-microvolt = <1275000>; | ||||
| 				}; | ||||
|  | ||||
| 				smb208_s2b: s2b { | ||||
| 					regulator-min-microvolt = <775000>; | ||||
| 					regulator-max-microvolt = <1275000>; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
|  | ||||
| 		ss_phy_0: phy@110f8830 { | ||||
| 			rx_eq = <2>; | ||||
| 			tx_deamp_3_5db = <32>; | ||||
| 			mpll = <0xa0>; | ||||
| 		}; | ||||
| 		ss_phy_1: phy@100f8830 { | ||||
| 			rx_eq = <2>; | ||||
| 			tx_deamp_3_5db = <32>; | ||||
| 			mpll = <0xa0>; | ||||
| 		}; | ||||
|  | ||||
| 		/* Temporary fixed regulator */ | ||||
| 		vsdcc_fixed: vsdcc-regulator { | ||||
| 			compatible = "regulator-fixed"; | ||||
| 			regulator-name = "SDCC Power"; | ||||
| 			regulator-min-microvolt = <3300000>; | ||||
| 			regulator-max-microvolt = <3300000>; | ||||
| 			regulator-always-on; | ||||
| 		}; | ||||
|  | ||||
| 		sdcc1bam:dma@12402000 { | ||||
| 			compatible = "qcom,bam-v1.3.0"; | ||||
| 			reg = <0x12402000 0x8000>; | ||||
| 			interrupts = <0 98 0>; | ||||
| 			clocks = <&gcc SDC1_H_CLK>; | ||||
| 			clock-names = "bam_clk"; | ||||
| 			#dma-cells = <1>; | ||||
| 			qcom,ee = <0>; | ||||
|                 }; | ||||
|  | ||||
| 		sdcc3bam:dma@12182000 { | ||||
| 			compatible = "qcom,bam-v1.3.0"; | ||||
| 			reg = <0x12182000 0x8000>; | ||||
| 			interrupts = <0 96 0>; | ||||
| 			clocks = <&gcc SDC3_H_CLK>; | ||||
| 			clock-names = "bam_clk"; | ||||
| 			#dma-cells = <1>; | ||||
| 			qcom,ee = <0>; | ||||
| 		}; | ||||
|  | ||||
| 		amba { | ||||
| 			compatible = "arm,amba-bus"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <1>; | ||||
| 			ranges; | ||||
| 			sdcc1: sdcc@12400000 { | ||||
| 				status          = "disabled"; | ||||
| 				compatible      = "arm,pl18x", "arm,primecell"; | ||||
| 				arm,primecell-periphid = <0x00051180>; | ||||
| 				reg             = <0x12400000 0x2000>; | ||||
| 				interrupts      = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; | ||||
| 				interrupt-names = "cmd_irq"; | ||||
| 				clocks          = <&gcc SDC1_CLK>, <&gcc SDC1_H_CLK>; | ||||
| 				clock-names     = "mclk", "apb_pclk"; | ||||
| 				bus-width       = <8>; | ||||
| 				max-frequency   = <96000000>; | ||||
| 				non-removable; | ||||
| 				cap-sd-highspeed; | ||||
| 				cap-mmc-highspeed; | ||||
| 				vmmc-supply = <&vsdcc_fixed>; | ||||
| 				dmas = <&sdcc1bam 2>, <&sdcc1bam 1>; | ||||
| 				dma-names = "tx", "rx"; | ||||
| 			}; | ||||
|  | ||||
| 			sdcc3: sdcc@12180000 { | ||||
| 				compatible      = "arm,pl18x", "arm,primecell"; | ||||
| 				arm,primecell-periphid = <0x00051180>; | ||||
| 				status          = "disabled"; | ||||
| 				reg             = <0x12180000 0x2000>; | ||||
| 				interrupts      = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>; | ||||
| 				interrupt-names = "cmd_irq"; | ||||
| 				clocks          = <&gcc SDC3_CLK>, <&gcc SDC3_H_CLK>; | ||||
| 				clock-names     = "mclk", "apb_pclk"; | ||||
| 				bus-width       = <8>; | ||||
| 				cap-sd-highspeed; | ||||
| 				cap-mmc-highspeed; | ||||
| 				max-frequency   = <192000000>; | ||||
| 				#mmc-ddr-1_8v; | ||||
| 				sd-uhs-sdr104; | ||||
| 				sd-uhs-ddr50; | ||||
| 				vqmmc-supply = <&vsdcc_fixed>; | ||||
| 				dmas = <&sdcc3bam 2>, <&sdcc3bam 1>; | ||||
| 				dma-names = "tx", "rx"; | ||||
| 			}; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
| @@ -0,0 +1,71 @@ | ||||
| From 28d0ed88f536dd639adf1b0c7c08e04be3c8f294 Mon Sep 17 00:00:00 2001 | ||||
| From: Thomas Pedersen <twp@codeaurora.org> | ||||
| Date: Mon, 16 May 2016 17:58:50 -0700 | ||||
| Subject: [PATCH 01/69] dtbindings: qcom_adm: Fix channel specifiers | ||||
|  | ||||
| Original patch from Andy Gross. | ||||
|  | ||||
| This patch removes the crci information from the dma | ||||
| channel property.  At least one client device requires | ||||
| using more than one CRCI value for a channel.  This does | ||||
| not match the current binding and the crci information | ||||
| needs to be removed. | ||||
|  | ||||
| Instead, the client device will provide this information | ||||
| via other means. | ||||
|  | ||||
| Signed-off-by: Andy Gross <agross@codeaurora.org> | ||||
| Signed-off-by: Thomas Pedersen <twp@codeaurora.org> | ||||
| --- | ||||
|  Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++---------- | ||||
|  1 file changed, 6 insertions(+), 10 deletions(-) | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/dma/qcom_adm.txt | ||||
| +++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt | ||||
| @@ -4,8 +4,7 @@ Required properties: | ||||
|  - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 | ||||
|  - reg: Address range for DMA registers | ||||
|  - interrupts: Should contain one interrupt shared by all channels | ||||
| -- #dma-cells: must be <2>.  First cell denotes the channel number.  Second cell | ||||
| -  denotes CRCI (client rate control interface) flow control assignment. | ||||
| +- #dma-cells: must be <1>.  First cell denotes the channel number. | ||||
|  - clocks: Should contain the core clock and interface clock. | ||||
|  - clock-names: Must contain "core" for the core clock and "iface" for the | ||||
|    interface clock. | ||||
| @@ -22,7 +21,7 @@ Example: | ||||
| 			compatible = "qcom,adm"; | ||||
| 			reg = <0x18300000 0x100000>; | ||||
| 			interrupts = <0 170 0>; | ||||
| -			#dma-cells = <2>; | ||||
| +			#dma-cells = <1>; | ||||
|  | ||||
| 			clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; | ||||
| 			clock-names = "core", "iface"; | ||||
| @@ -35,15 +34,12 @@ Example: | ||||
| 			qcom,ee = <0>; | ||||
| 		}; | ||||
|  | ||||
| -DMA clients must use the format descripted in the dma.txt file, using a three | ||||
| +DMA clients must use the format descripted in the dma.txt file, using a two | ||||
|  cell specifier for each channel. | ||||
|  | ||||
| -Each dmas request consists of 3 cells: | ||||
| +Each dmas request consists of two cells: | ||||
|   1. phandle pointing to the DMA controller | ||||
|   2. channel number | ||||
| - 3. CRCI assignment, if applicable.  If no CRCI flow control is required, use 0. | ||||
| -    The CRCI is used for flow control.  It identifies the peripheral device that | ||||
| -    is the source/destination for the transferred data. | ||||
|  | ||||
|  Example: | ||||
|  | ||||
| @@ -55,7 +51,7 @@ Example: | ||||
|  | ||||
| 		cs-gpios = <&qcom_pinmux 20 0>; | ||||
|  | ||||
| -		dmas = <&adm_dma 6 9>, | ||||
| -			<&adm_dma 5 10>; | ||||
| +		dmas = <&adm_dma 6>, | ||||
| +			<&adm_dma 5>; | ||||
| 		dma-names = "rx", "tx"; | ||||
| 	}; | ||||
| @@ -0,0 +1,966 @@ | ||||
| From 563fa24db4e529c5a3311928d73a8a90531ee527 Mon Sep 17 00:00:00 2001 | ||||
| From: Thomas Pedersen <twp@codeaurora.org> | ||||
| Date: Mon, 16 May 2016 17:58:51 -0700 | ||||
| Subject: [PATCH 02/69] dmaengine: Add ADM driver | ||||
|  | ||||
| Original patch by Andy Gross. | ||||
|  | ||||
| Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA | ||||
| controller found in the MSM8x60 and IPQ/APQ8064 platforms. | ||||
|  | ||||
| The ADM supports both memory to memory transactions and memory | ||||
| to/from peripheral device transactions.  The controller also provides flow | ||||
| control capabilities for transactions to/from peripheral devices. | ||||
|  | ||||
| The initial release of this driver supports slave transfers to/from peripherals | ||||
| and also incorporates CRCI (client rate control interface) flow control. | ||||
|  | ||||
| Signed-off-by: Andy Gross <agross@codeaurora.org> | ||||
| Signed-off-by: Thomas Pedersen <twp@codeaurora.org> | ||||
| --- | ||||
|  drivers/dma/qcom/Kconfig    |  10 + | ||||
|  drivers/dma/qcom/Makefile   |   1 + | ||||
|  drivers/dma/qcom/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++ | ||||
|  3 files changed, 911 insertions(+) | ||||
|  create mode 100644 drivers/dma/qcom/qcom_adm.c | ||||
|  | ||||
| --- a/drivers/dma/qcom/Kconfig | ||||
| +++ b/drivers/dma/qcom/Kconfig | ||||
| @@ -27,3 +27,13 @@ config QCOM_HIDMA | ||||
| 	  (user to kernel, kernel to kernel, etc.).  It only supports | ||||
| 	  memcpy interface. The core is not intended for general | ||||
| 	  purpose slave DMA. | ||||
| + | ||||
| +config QCOM_ADM | ||||
| +	tristate "Qualcomm ADM support" | ||||
| +	depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) | ||||
| +	select DMA_ENGINE | ||||
| +	select DMA_VIRTUAL_CHANNELS | ||||
| +	---help--- | ||||
| +	  Enable support for the Qualcomm ADM DMA controller.  This controller | ||||
| +	  provides DMA capabilities for both general purpose and on-chip | ||||
| +	  peripheral devices. | ||||
| --- a/drivers/dma/qcom/Makefile | ||||
| +++ b/drivers/dma/qcom/Makefile | ||||
| @@ -4,3 +4,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg | ||||
|  hdma_mgmt-objs	 := hidma_mgmt.o hidma_mgmt_sys.o | ||||
|  obj-$(CONFIG_QCOM_HIDMA) +=  hdma.o | ||||
|  hdma-objs        := hidma_ll.o hidma.o hidma_dbg.o | ||||
| +obj-$(CONFIG_QCOM_ADM) += qcom_adm.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/dma/qcom/qcom_adm.c | ||||
| @@ -0,0 +1,914 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + * | ||||
| + */ | ||||
| + | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/slab.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/interrupt.h> | ||||
| +#include <linux/dma-mapping.h> | ||||
| +#include <linux/scatterlist.h> | ||||
| +#include <linux/device.h> | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/of_address.h> | ||||
| +#include <linux/of_irq.h> | ||||
| +#include <linux/of_dma.h> | ||||
| +#include <linux/reset.h> | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/dmaengine.h> | ||||
| + | ||||
| +#include "../dmaengine.h" | ||||
| +#include "../virt-dma.h" | ||||
| + | ||||
| +/* ADM registers - calculated from channel number and security domain */ | ||||
| +#define ADM_CHAN_MULTI			0x4 | ||||
| +#define ADM_CI_MULTI			0x4 | ||||
| +#define ADM_CRCI_MULTI			0x4 | ||||
| +#define ADM_EE_MULTI			0x800 | ||||
| +#define ADM_CHAN_OFFS(chan)		(ADM_CHAN_MULTI * chan) | ||||
| +#define ADM_EE_OFFS(ee)			(ADM_EE_MULTI * ee) | ||||
| +#define ADM_CHAN_EE_OFFS(chan, ee)	(ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee)) | ||||
| +#define ADM_CHAN_OFFS(chan)		(ADM_CHAN_MULTI * chan) | ||||
| +#define ADM_CI_OFFS(ci)			(ADM_CHAN_OFF(ci)) | ||||
| +#define ADM_CH_CMD_PTR(chan, ee)	(ADM_CHAN_EE_OFFS(chan, ee)) | ||||
| +#define ADM_CH_RSLT(chan, ee)		(0x40 + ADM_CHAN_EE_OFFS(chan, ee)) | ||||
| +#define ADM_CH_FLUSH_STATE0(chan, ee)	(0x80 + ADM_CHAN_EE_OFFS(chan, ee)) | ||||
| +#define ADM_CH_STATUS_SD(chan, ee)	(0x200 + ADM_CHAN_EE_OFFS(chan, ee)) | ||||
| +#define ADM_CH_CONF(chan)		(0x240 + ADM_CHAN_OFFS(chan)) | ||||
| +#define ADM_CH_RSLT_CONF(chan, ee)	(0x300 + ADM_CHAN_EE_OFFS(chan, ee)) | ||||
| +#define ADM_SEC_DOMAIN_IRQ_STATUS(ee)	(0x380 + ADM_EE_OFFS(ee)) | ||||
| +#define ADM_CI_CONF(ci)			(0x390 + ci * ADM_CI_MULTI) | ||||
| +#define ADM_GP_CTL			0x3d8 | ||||
| +#define ADM_CRCI_CTL(crci, ee)		(0x400 + crci * ADM_CRCI_MULTI + \ | ||||
| +						ADM_EE_OFFS(ee)) | ||||
| + | ||||
| +/* channel status */ | ||||
| +#define ADM_CH_STATUS_VALID	BIT(1) | ||||
| + | ||||
| +/* channel result */ | ||||
| +#define ADM_CH_RSLT_VALID	BIT(31) | ||||
| +#define ADM_CH_RSLT_ERR		BIT(3) | ||||
| +#define ADM_CH_RSLT_FLUSH	BIT(2) | ||||
| +#define ADM_CH_RSLT_TPD		BIT(1) | ||||
| + | ||||
| +/* channel conf */ | ||||
| +#define ADM_CH_CONF_SHADOW_EN		BIT(12) | ||||
| +#define ADM_CH_CONF_MPU_DISABLE		BIT(11) | ||||
| +#define ADM_CH_CONF_PERM_MPU_CONF	BIT(9) | ||||
| +#define ADM_CH_CONF_FORCE_RSLT_EN	BIT(7) | ||||
| +#define ADM_CH_CONF_SEC_DOMAIN(ee)	(((ee & 0x3) << 4) | ((ee & 0x4) << 11)) | ||||
| + | ||||
| +/* channel result conf */ | ||||
| +#define ADM_CH_RSLT_CONF_FLUSH_EN	BIT(1) | ||||
| +#define ADM_CH_RSLT_CONF_IRQ_EN		BIT(0) | ||||
| + | ||||
| +/* CRCI CTL */ | ||||
| +#define ADM_CRCI_CTL_MUX_SEL	BIT(18) | ||||
| +#define ADM_CRCI_CTL_RST	BIT(17) | ||||
| + | ||||
| +/* CI configuration */ | ||||
| +#define ADM_CI_RANGE_END(x)	(x << 24) | ||||
| +#define ADM_CI_RANGE_START(x)	(x << 16) | ||||
| +#define ADM_CI_BURST_4_WORDS	BIT(2) | ||||
| +#define ADM_CI_BURST_8_WORDS	BIT(3) | ||||
| + | ||||
| +/* GP CTL */ | ||||
| +#define ADM_GP_CTL_LP_EN	BIT(12) | ||||
| +#define ADM_GP_CTL_LP_CNT(x)	(x << 8) | ||||
| + | ||||
| +/* Command pointer list entry */ | ||||
| +#define ADM_CPLE_LP		BIT(31) | ||||
| +#define ADM_CPLE_CMD_PTR_LIST	BIT(29) | ||||
| + | ||||
| +/* Command list entry */ | ||||
| +#define ADM_CMD_LC		BIT(31) | ||||
| +#define ADM_CMD_DST_CRCI(n)	(((n) & 0xf) << 7) | ||||
| +#define ADM_CMD_SRC_CRCI(n)	(((n) & 0xf) << 3) | ||||
| + | ||||
| +#define ADM_CMD_TYPE_SINGLE	0x0 | ||||
| +#define ADM_CMD_TYPE_BOX	0x3 | ||||
| + | ||||
| +#define ADM_CRCI_MUX_SEL	BIT(4) | ||||
| +#define ADM_DESC_ALIGN		8 | ||||
| +#define ADM_MAX_XFER		(SZ_64K-1) | ||||
| +#define ADM_MAX_ROWS		(SZ_64K-1) | ||||
| +#define ADM_MAX_CHANNELS	16 | ||||
| + | ||||
| +struct adm_desc_hw_box { | ||||
| +	u32 cmd; | ||||
| +	u32 src_addr; | ||||
| +	u32 dst_addr; | ||||
| +	u32 row_len; | ||||
| +	u32 num_rows; | ||||
| +	u32 row_offset; | ||||
| +}; | ||||
| + | ||||
| +struct adm_desc_hw_single { | ||||
| +	u32 cmd; | ||||
| +	u32 src_addr; | ||||
| +	u32 dst_addr; | ||||
| +	u32 len; | ||||
| +}; | ||||
| + | ||||
| +struct adm_async_desc { | ||||
| +	struct virt_dma_desc vd; | ||||
| +	struct adm_device *adev; | ||||
| + | ||||
| +	size_t length; | ||||
| +	enum dma_transfer_direction dir; | ||||
| +	dma_addr_t dma_addr; | ||||
| +	size_t dma_len; | ||||
| + | ||||
| +	void *cpl; | ||||
| +	dma_addr_t cp_addr; | ||||
| +	u32 crci; | ||||
| +	u32 mux; | ||||
| +	u32 blk_size; | ||||
| +}; | ||||
| + | ||||
| +struct adm_chan { | ||||
| +	struct virt_dma_chan vc; | ||||
| +	struct adm_device *adev; | ||||
| + | ||||
| +	/* parsed from DT */ | ||||
| +	u32 id;			/* channel id */ | ||||
| + | ||||
| +	struct adm_async_desc *curr_txd; | ||||
| +	struct dma_slave_config slave; | ||||
| +	struct list_head node; | ||||
| + | ||||
| +	int error; | ||||
| +	int initialized; | ||||
| +}; | ||||
| + | ||||
| +static inline struct adm_chan *to_adm_chan(struct dma_chan *common) | ||||
| +{ | ||||
| +	return container_of(common, struct adm_chan, vc.chan); | ||||
| +} | ||||
| + | ||||
| +struct adm_device { | ||||
| +	void __iomem *regs; | ||||
| +	struct device *dev; | ||||
| +	struct dma_device common; | ||||
| +	struct device_dma_parameters dma_parms; | ||||
| +	struct adm_chan *channels; | ||||
| + | ||||
| +	u32 ee; | ||||
| + | ||||
| +	struct clk *core_clk; | ||||
| +	struct clk *iface_clk; | ||||
| + | ||||
| +	struct reset_control *clk_reset; | ||||
| +	struct reset_control *c0_reset; | ||||
| +	struct reset_control *c1_reset; | ||||
| +	struct reset_control *c2_reset; | ||||
| +	int irq; | ||||
| +}; | ||||
| + | ||||
| +/** | ||||
| + * adm_free_chan - Frees dma resources associated with the specific channel | ||||
| + * | ||||
| + * Free all allocated descriptors associated with this channel | ||||
| + * | ||||
| + */ | ||||
| +static void adm_free_chan(struct dma_chan *chan) | ||||
| +{ | ||||
| +	/* free all queued descriptors */ | ||||
| +	vchan_free_chan_resources(to_virt_chan(chan)); | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_get_blksize - Get block size from burst value | ||||
| + * | ||||
| + */ | ||||
| +static int adm_get_blksize(unsigned int burst) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	switch (burst) { | ||||
| +	case 16: | ||||
| +	case 32: | ||||
| +	case 64: | ||||
| +	case 128: | ||||
| +		ret = ffs(burst>>4) - 1; | ||||
| +		break; | ||||
| +	case 192: | ||||
| +		ret = 4; | ||||
| +		break; | ||||
| +	case 256: | ||||
| +		ret = 5; | ||||
| +		break; | ||||
| +	default: | ||||
| +		ret = -EINVAL; | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_process_fc_descriptors - Process descriptors for flow controlled xfers | ||||
| + * | ||||
| + * @achan: ADM channel | ||||
| + * @desc: Descriptor memory pointer | ||||
| + * @sg: Scatterlist entry | ||||
| + * @crci: CRCI value | ||||
| + * @burst: Burst size of transaction | ||||
| + * @direction: DMA transfer direction | ||||
| + */ | ||||
| +static void *adm_process_fc_descriptors(struct adm_chan *achan, | ||||
| +	void *desc, struct scatterlist *sg, u32 crci, u32 burst, | ||||
| +	enum dma_transfer_direction direction) | ||||
| +{ | ||||
| +	struct adm_desc_hw_box *box_desc = NULL; | ||||
| +	struct adm_desc_hw_single *single_desc; | ||||
| +	u32 remainder = sg_dma_len(sg); | ||||
| +	u32 rows, row_offset, crci_cmd; | ||||
| +	u32 mem_addr = sg_dma_address(sg); | ||||
| +	u32 *incr_addr = &mem_addr; | ||||
| +	u32 *src, *dst; | ||||
| + | ||||
| +	if (direction == DMA_DEV_TO_MEM) { | ||||
| +		crci_cmd = ADM_CMD_SRC_CRCI(crci); | ||||
| +		row_offset = burst; | ||||
| +		src = &achan->slave.src_addr; | ||||
| +		dst = &mem_addr; | ||||
| +	} else { | ||||
| +		crci_cmd = ADM_CMD_DST_CRCI(crci); | ||||
| +		row_offset = burst << 16; | ||||
| +		src = &mem_addr; | ||||
| +		dst = &achan->slave.dst_addr; | ||||
| +	} | ||||
| + | ||||
| +	while (remainder >= burst) { | ||||
| +		box_desc = desc; | ||||
| +		box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd; | ||||
| +		box_desc->row_offset = row_offset; | ||||
| +		box_desc->src_addr = *src; | ||||
| +		box_desc->dst_addr = *dst; | ||||
| + | ||||
| +		rows = remainder / burst; | ||||
| +		rows = min_t(u32, rows, ADM_MAX_ROWS); | ||||
| +		box_desc->num_rows = rows << 16 | rows; | ||||
| +		box_desc->row_len = burst << 16 | burst; | ||||
| + | ||||
| +		*incr_addr += burst * rows; | ||||
| +		remainder -= burst * rows; | ||||
| +		desc += sizeof(*box_desc); | ||||
| +	} | ||||
| + | ||||
| +	/* if leftover bytes, do one single descriptor */ | ||||
| +	if (remainder) { | ||||
| +		single_desc = desc; | ||||
| +		single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd; | ||||
| +		single_desc->len = remainder; | ||||
| +		single_desc->src_addr = *src; | ||||
| +		single_desc->dst_addr = *dst; | ||||
| +		desc += sizeof(*single_desc); | ||||
| + | ||||
| +		if (sg_is_last(sg)) | ||||
| +			single_desc->cmd |= ADM_CMD_LC; | ||||
| +	} else { | ||||
| +		if (box_desc && sg_is_last(sg)) | ||||
| +			box_desc->cmd |= ADM_CMD_LC; | ||||
| +	} | ||||
| + | ||||
| +	return desc; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers | ||||
| + * | ||||
| + * @achan: ADM channel | ||||
| + * @desc: Descriptor memory pointer | ||||
| + * @sg: Scatterlist entry | ||||
| + * @direction: DMA transfer direction | ||||
| + */ | ||||
| +static void *adm_process_non_fc_descriptors(struct adm_chan *achan, | ||||
| +	void *desc, struct scatterlist *sg, | ||||
| +	enum dma_transfer_direction direction) | ||||
| +{ | ||||
| +	struct adm_desc_hw_single *single_desc; | ||||
| +	u32 remainder = sg_dma_len(sg); | ||||
| +	u32 mem_addr = sg_dma_address(sg); | ||||
| +	u32 *incr_addr = &mem_addr; | ||||
| +	u32 *src, *dst; | ||||
| + | ||||
| +	if (direction == DMA_DEV_TO_MEM) { | ||||
| +		src = &achan->slave.src_addr; | ||||
| +		dst = &mem_addr; | ||||
| +	} else { | ||||
| +		src = &mem_addr; | ||||
| +		dst = &achan->slave.dst_addr; | ||||
| +	} | ||||
| + | ||||
| +	do { | ||||
| +		single_desc = desc; | ||||
| +		single_desc->cmd = ADM_CMD_TYPE_SINGLE; | ||||
| +		single_desc->src_addr = *src; | ||||
| +		single_desc->dst_addr = *dst; | ||||
| +		single_desc->len = (remainder > ADM_MAX_XFER) ? | ||||
| +				ADM_MAX_XFER : remainder; | ||||
| + | ||||
| +		remainder -= single_desc->len; | ||||
| +		*incr_addr += single_desc->len; | ||||
| +		desc += sizeof(*single_desc); | ||||
| +	} while (remainder); | ||||
| + | ||||
| +	/* set last command if this is the end of the whole transaction */ | ||||
| +	if (sg_is_last(sg)) | ||||
| +		single_desc->cmd |= ADM_CMD_LC; | ||||
| + | ||||
| +	return desc; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_prep_slave_sg - Prep slave sg transaction | ||||
| + * | ||||
| + * @chan: dma channel | ||||
| + * @sgl: scatter gather list | ||||
| + * @sg_len: length of sg | ||||
| + * @direction: DMA transfer direction | ||||
| + * @flags: DMA flags | ||||
| + * @context: transfer context (unused) | ||||
| + */ | ||||
| +static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, | ||||
| +	struct scatterlist *sgl, unsigned int sg_len, | ||||
| +	enum dma_transfer_direction direction, unsigned long flags, | ||||
| +	void *context) | ||||
| +{ | ||||
| +	struct adm_chan *achan = to_adm_chan(chan); | ||||
| +	struct adm_device *adev = achan->adev; | ||||
| +	struct adm_async_desc *async_desc; | ||||
| +	struct scatterlist *sg; | ||||
| +	dma_addr_t cple_addr; | ||||
| +	u32 i, burst; | ||||
| +	u32 single_count = 0, box_count = 0, crci = 0; | ||||
| +	void *desc; | ||||
| +	u32 *cple; | ||||
| +	int blk_size = 0; | ||||
| + | ||||
| +	if (!is_slave_direction(direction)) { | ||||
| +		dev_err(adev->dev, "invalid dma direction\n"); | ||||
| +		return NULL; | ||||
| +	} | ||||
| + | ||||
| +	/* | ||||
| +	 * get burst value from slave configuration | ||||
| +	 */ | ||||
| +	burst = (direction == DMA_MEM_TO_DEV) ? | ||||
| +		achan->slave.dst_maxburst : | ||||
| +		achan->slave.src_maxburst; | ||||
| + | ||||
| +	/* if using flow control, validate burst and crci values */ | ||||
| +	if (achan->slave.device_fc) { | ||||
| + | ||||
| +		blk_size = adm_get_blksize(burst); | ||||
| +		if (blk_size < 0) { | ||||
| +			dev_err(adev->dev, "invalid burst value: %d\n", | ||||
| +				burst); | ||||
| +			return ERR_PTR(-EINVAL); | ||||
| +		} | ||||
| + | ||||
| +		crci = achan->slave.slave_id & 0xf; | ||||
| +		if (!crci || achan->slave.slave_id > 0x1f) { | ||||
| +			dev_err(adev->dev, "invalid crci value\n"); | ||||
| +			return ERR_PTR(-EINVAL); | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	/* iterate through sgs and compute allocation size of structures */ | ||||
| +	for_each_sg(sgl, sg, sg_len, i) { | ||||
| +		if (achan->slave.device_fc) { | ||||
| +			box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst, | ||||
| +						  ADM_MAX_ROWS); | ||||
| +			if (sg_dma_len(sg) % burst) | ||||
| +				single_count++; | ||||
| +		} else { | ||||
| +			single_count += DIV_ROUND_UP(sg_dma_len(sg), | ||||
| +						     ADM_MAX_XFER); | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	async_desc = kzalloc(sizeof(*async_desc), GFP_ATOMIC); | ||||
| +	if (!async_desc) | ||||
| +		return ERR_PTR(-ENOMEM); | ||||
| + | ||||
| +	if (crci) | ||||
| +		async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? | ||||
| +					ADM_CRCI_CTL_MUX_SEL : 0; | ||||
| +	async_desc->crci = crci; | ||||
| +	async_desc->blk_size = blk_size; | ||||
| +	async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + | ||||
| +				box_count * sizeof(struct adm_desc_hw_box) + | ||||
| +				sizeof(*cple) + 2 * ADM_DESC_ALIGN; | ||||
| + | ||||
| +	async_desc->cpl = kzalloc(async_desc->dma_len, GFP_ATOMIC); | ||||
| +	if (!async_desc->cpl) | ||||
| +		goto free; | ||||
| + | ||||
| +	async_desc->adev = adev; | ||||
| + | ||||
| +	/* both command list entry and descriptors must be 8 byte aligned */ | ||||
| +	cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); | ||||
| +	desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN); | ||||
| + | ||||
| +	for_each_sg(sgl, sg, sg_len, i) { | ||||
| +		async_desc->length += sg_dma_len(sg); | ||||
| + | ||||
| +		if (achan->slave.device_fc) | ||||
| +			desc = adm_process_fc_descriptors(achan, desc, sg, crci, | ||||
| +							burst, direction); | ||||
| +		else | ||||
| +			desc = adm_process_non_fc_descriptors(achan, desc, sg, | ||||
| +							   direction); | ||||
| +	} | ||||
| + | ||||
| +	async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl, | ||||
| +					      async_desc->dma_len, | ||||
| +					      DMA_TO_DEVICE); | ||||
| +	if (dma_mapping_error(adev->dev, async_desc->dma_addr)) | ||||
| +		goto free; | ||||
| + | ||||
| +	cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl); | ||||
| + | ||||
| +	/* init cmd list */ | ||||
| +	dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple), | ||||
| +				DMA_TO_DEVICE); | ||||
| +	*cple = ADM_CPLE_LP; | ||||
| +	*cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3; | ||||
| +	dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple), | ||||
| +				   DMA_TO_DEVICE); | ||||
| + | ||||
| +	return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); | ||||
| + | ||||
| +free: | ||||
| +	kfree(async_desc); | ||||
| +	return ERR_PTR(-ENOMEM); | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_terminate_all - terminate all transactions on a channel | ||||
| + * @achan: adm dma channel | ||||
| + * | ||||
| + * Dequeues and frees all transactions, aborts current transaction | ||||
| + * No callbacks are done | ||||
| + * | ||||
| + */ | ||||
| +static int adm_terminate_all(struct dma_chan *chan) | ||||
| +{ | ||||
| +	struct adm_chan *achan = to_adm_chan(chan); | ||||
| +	struct adm_device *adev = achan->adev; | ||||
| +	unsigned long flags; | ||||
| +	LIST_HEAD(head); | ||||
| + | ||||
| +	spin_lock_irqsave(&achan->vc.lock, flags); | ||||
| +	vchan_get_all_descriptors(&achan->vc, &head); | ||||
| + | ||||
| +	/* send flush command to terminate current transaction */ | ||||
| +	writel_relaxed(0x0, | ||||
| +		adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); | ||||
| + | ||||
| +	spin_unlock_irqrestore(&achan->vc.lock, flags); | ||||
| + | ||||
| +	vchan_dma_desc_free_list(&achan->vc, &head); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) | ||||
| +{ | ||||
| +	struct adm_chan *achan = to_adm_chan(chan); | ||||
| +	unsigned long flag; | ||||
| + | ||||
| +	spin_lock_irqsave(&achan->vc.lock, flag); | ||||
| +	memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); | ||||
| +	spin_unlock_irqrestore(&achan->vc.lock, flag); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_start_dma - start next transaction | ||||
| + * @achan - ADM dma channel | ||||
| + */ | ||||
| +static void adm_start_dma(struct adm_chan *achan) | ||||
| +{ | ||||
| +	struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); | ||||
| +	struct adm_device *adev = achan->adev; | ||||
| +	struct adm_async_desc *async_desc; | ||||
| + | ||||
| +	lockdep_assert_held(&achan->vc.lock); | ||||
| + | ||||
| +	if (!vd) | ||||
| +		return; | ||||
| + | ||||
| +	list_del(&vd->node); | ||||
| + | ||||
| +	/* write next command list out to the CMD FIFO */ | ||||
| +	async_desc = container_of(vd, struct adm_async_desc, vd); | ||||
| +	achan->curr_txd = async_desc; | ||||
| + | ||||
| +	/* reset channel error */ | ||||
| +	achan->error = 0; | ||||
| + | ||||
| +	if (!achan->initialized) { | ||||
| +		/* enable interrupts */ | ||||
| +		writel(ADM_CH_CONF_SHADOW_EN | | ||||
| +		       ADM_CH_CONF_PERM_MPU_CONF | | ||||
| +		       ADM_CH_CONF_MPU_DISABLE | | ||||
| +		       ADM_CH_CONF_SEC_DOMAIN(adev->ee), | ||||
| +		       adev->regs + ADM_CH_CONF(achan->id)); | ||||
| + | ||||
| +		writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN, | ||||
| +			adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); | ||||
| + | ||||
| +		achan->initialized = 1; | ||||
| +	} | ||||
| + | ||||
| +	/* set the crci block size if this transaction requires CRCI */ | ||||
| +	if (async_desc->crci) { | ||||
| +		writel(async_desc->mux | async_desc->blk_size, | ||||
| +			adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); | ||||
| +	} | ||||
| + | ||||
| +	/* make sure IRQ enable doesn't get reordered */ | ||||
| +	wmb(); | ||||
| + | ||||
| +	/* write next command list out to the CMD FIFO */ | ||||
| +	writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, | ||||
| +		adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_dma_irq - irq handler for ADM controller | ||||
| + * @irq: IRQ of interrupt | ||||
| + * @data: callback data | ||||
| + * | ||||
| + * IRQ handler for the bam controller | ||||
| + */ | ||||
| +static irqreturn_t adm_dma_irq(int irq, void *data) | ||||
| +{ | ||||
| +	struct adm_device *adev = data; | ||||
| +	u32 srcs, i; | ||||
| +	struct adm_async_desc *async_desc; | ||||
| +	unsigned long flags; | ||||
| + | ||||
| +	srcs = readl_relaxed(adev->regs + | ||||
| +			ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee)); | ||||
| + | ||||
| +	for (i = 0; i < ADM_MAX_CHANNELS; i++) { | ||||
| +		struct adm_chan *achan = &adev->channels[i]; | ||||
| +		u32 status, result; | ||||
| + | ||||
| +		if (srcs & BIT(i)) { | ||||
| +			status = readl_relaxed(adev->regs + | ||||
| +				ADM_CH_STATUS_SD(i, adev->ee)); | ||||
| + | ||||
| +			/* if no result present, skip */ | ||||
| +			if (!(status & ADM_CH_STATUS_VALID)) | ||||
| +				continue; | ||||
| + | ||||
| +			result = readl_relaxed(adev->regs + | ||||
| +				ADM_CH_RSLT(i, adev->ee)); | ||||
| + | ||||
| +			/* no valid results, skip */ | ||||
| +			if (!(result & ADM_CH_RSLT_VALID)) | ||||
| +				continue; | ||||
| + | ||||
| +			/* flag error if transaction was flushed or failed */ | ||||
| +			if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH)) | ||||
| +				achan->error = 1; | ||||
| + | ||||
| +			spin_lock_irqsave(&achan->vc.lock, flags); | ||||
| +			async_desc = achan->curr_txd; | ||||
| + | ||||
| +			achan->curr_txd = NULL; | ||||
| + | ||||
| +			if (async_desc) { | ||||
| +				vchan_cookie_complete(&async_desc->vd); | ||||
| + | ||||
| +				/* kick off next DMA */ | ||||
| +				adm_start_dma(achan); | ||||
| +			} | ||||
| + | ||||
| +			spin_unlock_irqrestore(&achan->vc.lock, flags); | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	return IRQ_HANDLED; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_tx_status - returns status of transaction | ||||
| + * @chan: dma channel | ||||
| + * @cookie: transaction cookie | ||||
| + * @txstate: DMA transaction state | ||||
| + * | ||||
| + * Return status of dma transaction | ||||
| + */ | ||||
| +static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, | ||||
| +	struct dma_tx_state *txstate) | ||||
| +{ | ||||
| +	struct adm_chan *achan = to_adm_chan(chan); | ||||
| +	struct virt_dma_desc *vd; | ||||
| +	enum dma_status ret; | ||||
| +	unsigned long flags; | ||||
| +	size_t residue = 0; | ||||
| + | ||||
| +	ret = dma_cookie_status(chan, cookie, txstate); | ||||
| +	if (ret == DMA_COMPLETE || !txstate) | ||||
| +		return ret; | ||||
| + | ||||
| +	spin_lock_irqsave(&achan->vc.lock, flags); | ||||
| + | ||||
| +	vd = vchan_find_desc(&achan->vc, cookie); | ||||
| +	if (vd) | ||||
| +		residue = container_of(vd, struct adm_async_desc, vd)->length; | ||||
| + | ||||
| +	spin_unlock_irqrestore(&achan->vc.lock, flags); | ||||
| + | ||||
| +	/* | ||||
| +	 * residue is either the full length if it is in the issued list, or 0 | ||||
| +	 * if it is in progress.  We have no reliable way of determining | ||||
| +	 * anything inbetween | ||||
| +	*/ | ||||
| +	dma_set_residue(txstate, residue); | ||||
| + | ||||
| +	if (achan->error) | ||||
| +		return DMA_ERROR; | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_issue_pending - starts pending transactions | ||||
| + * @chan: dma channel | ||||
| + * | ||||
| + * Issues all pending transactions and starts DMA | ||||
| + */ | ||||
| +static void adm_issue_pending(struct dma_chan *chan) | ||||
| +{ | ||||
| +	struct adm_chan *achan = to_adm_chan(chan); | ||||
| +	unsigned long flags; | ||||
| + | ||||
| +	spin_lock_irqsave(&achan->vc.lock, flags); | ||||
| + | ||||
| +	if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) | ||||
| +		adm_start_dma(achan); | ||||
| +	spin_unlock_irqrestore(&achan->vc.lock, flags); | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * adm_dma_free_desc - free descriptor memory | ||||
| + * @vd: virtual descriptor | ||||
| + * | ||||
| + */ | ||||
| +static void adm_dma_free_desc(struct virt_dma_desc *vd) | ||||
| +{ | ||||
| +	struct adm_async_desc *async_desc = container_of(vd, | ||||
| +			struct adm_async_desc, vd); | ||||
| + | ||||
| +	dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr, | ||||
| +			 async_desc->dma_len, DMA_TO_DEVICE); | ||||
| +	kfree(async_desc->cpl); | ||||
| +	kfree(async_desc); | ||||
| +} | ||||
| + | ||||
| +static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, | ||||
| +	u32 index) | ||||
| +{ | ||||
| +	achan->id = index; | ||||
| +	achan->adev = adev; | ||||
| + | ||||
| +	vchan_init(&achan->vc, &adev->common); | ||||
| +	achan->vc.desc_free = adm_dma_free_desc; | ||||
| +} | ||||
| + | ||||
| +static int adm_dma_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct adm_device *adev; | ||||
| +	struct resource *iores; | ||||
| +	int ret; | ||||
| +	u32 i; | ||||
| + | ||||
| +	adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); | ||||
| +	if (!adev) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	adev->dev = &pdev->dev; | ||||
| + | ||||
| +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| +	adev->regs = devm_ioremap_resource(&pdev->dev, iores); | ||||
| +	if (IS_ERR(adev->regs)) | ||||
| +		return PTR_ERR(adev->regs); | ||||
| + | ||||
| +	adev->irq = platform_get_irq(pdev, 0); | ||||
| +	if (adev->irq < 0) | ||||
| +		return adev->irq; | ||||
| + | ||||
| +	ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); | ||||
| +	if (ret) { | ||||
| +		dev_err(adev->dev, "Execution environment unspecified\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	adev->core_clk = devm_clk_get(adev->dev, "core"); | ||||
| +	if (IS_ERR(adev->core_clk)) | ||||
| +		return PTR_ERR(adev->core_clk); | ||||
| + | ||||
| +	ret = clk_prepare_enable(adev->core_clk); | ||||
| +	if (ret) { | ||||
| +		dev_err(adev->dev, "failed to prepare/enable core clock\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	adev->iface_clk = devm_clk_get(adev->dev, "iface"); | ||||
| +	if (IS_ERR(adev->iface_clk)) { | ||||
| +		ret = PTR_ERR(adev->iface_clk); | ||||
| +		goto err_disable_core_clk; | ||||
| +	} | ||||
| + | ||||
| +	ret = clk_prepare_enable(adev->iface_clk); | ||||
| +	if (ret) { | ||||
| +		dev_err(adev->dev, "failed to prepare/enable iface clock\n"); | ||||
| +		goto err_disable_core_clk; | ||||
| +	} | ||||
| + | ||||
| +	adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk"); | ||||
| +	if (IS_ERR(adev->clk_reset)) { | ||||
| +		dev_err(adev->dev, "failed to get ADM0 reset\n"); | ||||
| +		ret = PTR_ERR(adev->clk_reset); | ||||
| +		goto err_disable_clks; | ||||
| +	} | ||||
| + | ||||
| +	adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0"); | ||||
| +	if (IS_ERR(adev->c0_reset)) { | ||||
| +		dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); | ||||
| +		ret = PTR_ERR(adev->c0_reset); | ||||
| +		goto err_disable_clks; | ||||
| +	} | ||||
| + | ||||
| +	adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1"); | ||||
| +	if (IS_ERR(adev->c1_reset)) { | ||||
| +		dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); | ||||
| +		ret = PTR_ERR(adev->c1_reset); | ||||
| +		goto err_disable_clks; | ||||
| +	} | ||||
| + | ||||
| +	adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2"); | ||||
| +	if (IS_ERR(adev->c2_reset)) { | ||||
| +		dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); | ||||
| +		ret = PTR_ERR(adev->c2_reset); | ||||
| +		goto err_disable_clks; | ||||
| +	} | ||||
| + | ||||
| +	reset_control_assert(adev->clk_reset); | ||||
| +	reset_control_assert(adev->c0_reset); | ||||
| +	reset_control_assert(adev->c1_reset); | ||||
| +	reset_control_assert(adev->c2_reset); | ||||
| + | ||||
| +	reset_control_deassert(adev->clk_reset); | ||||
| +	reset_control_deassert(adev->c0_reset); | ||||
| +	reset_control_deassert(adev->c1_reset); | ||||
| +	reset_control_deassert(adev->c2_reset); | ||||
| + | ||||
| +	adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS, | ||||
| +				sizeof(*adev->channels), GFP_KERNEL); | ||||
| + | ||||
| +	if (!adev->channels) { | ||||
| +		ret = -ENOMEM; | ||||
| +		goto err_disable_clks; | ||||
| +	} | ||||
| + | ||||
| +	/* allocate and initialize channels */ | ||||
| +	INIT_LIST_HEAD(&adev->common.channels); | ||||
| + | ||||
| +	for (i = 0; i < ADM_MAX_CHANNELS; i++) | ||||
| +		adm_channel_init(adev, &adev->channels[i], i); | ||||
| + | ||||
| +	/* reset CRCIs */ | ||||
| +	for (i = 0; i < 16; i++) | ||||
| +		writel(ADM_CRCI_CTL_RST, adev->regs + | ||||
| +			ADM_CRCI_CTL(i, adev->ee)); | ||||
| + | ||||
| +	/* configure client interfaces */ | ||||
| +	writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) | | ||||
| +		ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); | ||||
| +	writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) | | ||||
| +		ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); | ||||
| +	writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) | | ||||
| +		ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); | ||||
| +	writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf), | ||||
| +		adev->regs + ADM_GP_CTL); | ||||
| + | ||||
| +	ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, | ||||
| +			0, "adm_dma", adev); | ||||
| +	if (ret) | ||||
| +		goto err_disable_clks; | ||||
| + | ||||
| +	platform_set_drvdata(pdev, adev); | ||||
| + | ||||
| +	adev->common.dev = adev->dev; | ||||
| +	adev->common.dev->dma_parms = &adev->dma_parms; | ||||
| + | ||||
| +	/* set capabilities */ | ||||
| +	dma_cap_zero(adev->common.cap_mask); | ||||
| +	dma_cap_set(DMA_SLAVE, adev->common.cap_mask); | ||||
| +	dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); | ||||
| + | ||||
| +	/* initialize dmaengine apis */ | ||||
| +	adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV); | ||||
| +	adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; | ||||
| +	adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||||
| +	adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||||
| +	adev->common.device_free_chan_resources = adm_free_chan; | ||||
| +	adev->common.device_prep_slave_sg = adm_prep_slave_sg; | ||||
| +	adev->common.device_issue_pending = adm_issue_pending; | ||||
| +	adev->common.device_tx_status = adm_tx_status; | ||||
| +	adev->common.device_terminate_all = adm_terminate_all; | ||||
| +	adev->common.device_config = adm_slave_config; | ||||
| + | ||||
| +	ret = dma_async_device_register(&adev->common); | ||||
| +	if (ret) { | ||||
| +		dev_err(adev->dev, "failed to register dma async device\n"); | ||||
| +		goto err_disable_clks; | ||||
| +	} | ||||
| + | ||||
| +	ret = of_dma_controller_register(pdev->dev.of_node, | ||||
| +					 of_dma_xlate_by_chan_id, | ||||
| +					 &adev->common); | ||||
| +	if (ret) | ||||
| +		goto err_unregister_dma; | ||||
| + | ||||
| +	return 0; | ||||
| + | ||||
| +err_unregister_dma: | ||||
| +	dma_async_device_unregister(&adev->common); | ||||
| +err_disable_clks: | ||||
| +	clk_disable_unprepare(adev->iface_clk); | ||||
| +err_disable_core_clk: | ||||
| +	clk_disable_unprepare(adev->core_clk); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static int adm_dma_remove(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct adm_device *adev = platform_get_drvdata(pdev); | ||||
| +	struct adm_chan *achan; | ||||
| +	u32 i; | ||||
| + | ||||
| +	of_dma_controller_free(pdev->dev.of_node); | ||||
| +	dma_async_device_unregister(&adev->common); | ||||
| + | ||||
| +	for (i = 0; i < ADM_MAX_CHANNELS; i++) { | ||||
| +		achan = &adev->channels[i]; | ||||
| + | ||||
| +		/* mask IRQs for this channel/EE pair */ | ||||
| +		writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); | ||||
| + | ||||
| +		adm_terminate_all(&adev->channels[i].vc.chan); | ||||
| +	} | ||||
| + | ||||
| +	devm_free_irq(adev->dev, adev->irq, adev); | ||||
| + | ||||
| +	clk_disable_unprepare(adev->core_clk); | ||||
| +	clk_disable_unprepare(adev->iface_clk); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static const struct of_device_id adm_of_match[] = { | ||||
| +	{ .compatible = "qcom,adm", }, | ||||
| +	{} | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, adm_of_match); | ||||
| + | ||||
| +static struct platform_driver adm_dma_driver = { | ||||
| +	.probe = adm_dma_probe, | ||||
| +	.remove = adm_dma_remove, | ||||
| +	.driver = { | ||||
| +		.name = "adm-dma-engine", | ||||
| +		.of_match_table = adm_of_match, | ||||
| +	}, | ||||
| +}; | ||||
| + | ||||
| +module_platform_driver(adm_dma_driver); | ||||
| + | ||||
| +MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); | ||||
| +MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| @@ -0,0 +1,40 @@ | ||||
| From 0c974b87829e007dc4fae94e20d488204e20e662 Mon Sep 17 00:00:00 2001 | ||||
| From: John Crispin <john@phrozen.org> | ||||
| Date: Thu, 9 Mar 2017 08:16:10 +0100 | ||||
| Subject: [PATCH 30/69] clk: Disable i2c device on gsbi4 | ||||
|  | ||||
| This patch was not annotated and comes from the v4.4 tree. | ||||
|  | ||||
| Signed-off-by: John Crispin <john@phrozen.org> | ||||
| --- | ||||
|  drivers/clk/qcom/gcc-ipq806x.c | 5 +++-- | ||||
|  1 file changed, 3 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/clk/qcom/gcc-ipq806x.c | ||||
| +++ b/drivers/clk/qcom/gcc-ipq806x.c | ||||
| @@ -294,7 +294,7 @@ static struct clk_rcg gsbi1_uart_src = { | ||||
| 			.parent_names = gcc_pxo_pll8, | ||||
| 			.num_parents = 2, | ||||
| 			.ops = &clk_rcg_ops, | ||||
| -			.flags = CLK_SET_PARENT_GATE, | ||||
| +			.flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED, | ||||
| 		}, | ||||
| 	}, | ||||
|  }; | ||||
| @@ -312,7 +312,7 @@ static struct clk_branch gsbi1_uart_clk | ||||
| 			}, | ||||
| 			.num_parents = 1, | ||||
| 			.ops = &clk_branch_ops, | ||||
| -			.flags = CLK_SET_RATE_PARENT, | ||||
| +			.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, | ||||
| 		}, | ||||
| 	}, | ||||
|  }; | ||||
| @@ -890,6 +890,7 @@ static struct clk_branch gsbi1_h_clk = { | ||||
| 		.hw.init = &(struct clk_init_data){ | ||||
| 			.name = "gsbi1_h_clk", | ||||
| 			.ops = &clk_branch_ops, | ||||
| +			.flags = CLK_IGNORE_UNUSED, | ||||
| 		}, | ||||
| 	}, | ||||
|  }; | ||||
| @@ -0,0 +1,282 @@ | ||||
| From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001 | ||||
| From: Mathieu Olivari <mathieu@codeaurora.org> | ||||
| Date: Thu, 9 Mar 2017 08:18:08 +0100 | ||||
| Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms | ||||
|  | ||||
| On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is | ||||
| used to store partition layout. This new parser can now be used to read | ||||
| SMEM and use it to register an MTD layout according to its content. | ||||
|  | ||||
| Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> | ||||
| Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org> | ||||
| --- | ||||
|  drivers/mtd/Kconfig          |   7 ++ | ||||
|  drivers/mtd/Makefile         |   1 + | ||||
|  drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++ | ||||
|  3 files changed, 236 insertions(+) | ||||
|  create mode 100644 drivers/mtd/qcom_smem_part.c | ||||
|  | ||||
| --- a/drivers/mtd/Kconfig | ||||
| +++ b/drivers/mtd/Kconfig | ||||
| @@ -194,6 +194,13 @@ config MTD_MYLOADER_PARTS | ||||
| 	  You will still need the parsing functions to be called by the driver | ||||
| 	  for your particular device. It won't happen automatically. | ||||
|  | ||||
| +config MTD_QCOM_SMEM_PARTS | ||||
| +	tristate "QCOM SMEM partitioning support" | ||||
| +	depends on QCOM_SMEM | ||||
| +	help | ||||
| +	  This provides partitions parser for QCOM devices using SMEM | ||||
| +	  such as IPQ806x. | ||||
| + | ||||
|  comment "User Modules And Translation Layers" | ||||
|  | ||||
|  # | ||||
| --- /dev/null | ||||
| +++ b/drivers/mtd/qcom_smem_part.c | ||||
| @@ -0,0 +1,235 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2015, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/device.h> | ||||
| +#include <linux/slab.h> | ||||
| + | ||||
| +#include <linux/mtd/mtd.h> | ||||
| +#include <linux/mtd/partitions.h> | ||||
| +#include <linux/spi/spi.h> | ||||
| +#include <linux/module.h> | ||||
| + | ||||
| +#include <linux/soc/qcom/smem.h> | ||||
| + | ||||
| +/* Processor/host identifier for the application processor */ | ||||
| +#define SMEM_HOST_APPS			0 | ||||
| + | ||||
| +/* SMEM items index */ | ||||
| +#define SMEM_AARM_PARTITION_TABLE	9 | ||||
| +#define SMEM_BOOT_FLASH_TYPE		421 | ||||
| +#define SMEM_BOOT_FLASH_BLOCK_SIZE	424 | ||||
| + | ||||
| +/* SMEM Flash types */ | ||||
| +#define SMEM_FLASH_NAND			2 | ||||
| +#define SMEM_FLASH_SPI			6 | ||||
| + | ||||
| +#define SMEM_PART_NAME_SZ		16 | ||||
| +#define SMEM_PARTS_MAX			32 | ||||
| + | ||||
| +struct smem_partition { | ||||
| +	char name[SMEM_PART_NAME_SZ]; | ||||
| +	__le32 start; | ||||
| +	__le32 size; | ||||
| +	__le32 attr; | ||||
| +}; | ||||
| + | ||||
| +struct smem_partition_table { | ||||
| +	u8 magic[8]; | ||||
| +	__le32 version; | ||||
| +	__le32 len; | ||||
| +	struct smem_partition parts[SMEM_PARTS_MAX]; | ||||
| +}; | ||||
| + | ||||
| +/* SMEM Magic values in partition table */ | ||||
| +static const u8 SMEM_PTABLE_MAGIC[] = { | ||||
| +	0xaa, 0x73, 0xee, 0x55, | ||||
| +	0xdb, 0xbd, 0x5e, 0xe3, | ||||
| +}; | ||||
| + | ||||
| +static int qcom_smem_get_flash_blksz(u64 **smem_blksz) | ||||
| +{ | ||||
| +	size_t size; | ||||
| + | ||||
| +	*smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, | ||||
| +			    	    &size); | ||||
| + | ||||
| +	if (IS_ERR(*smem_blksz)) { | ||||
| +		pr_err("Unable to read flash blksz from SMEM\n"); | ||||
| +		return -ENOENT; | ||||
| +	} | ||||
| + | ||||
| +	if (size != sizeof(**smem_blksz)) { | ||||
| +		pr_err("Invalid flash blksz size in SMEM\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qcom_smem_get_flash_type(u64 **smem_flash_type) | ||||
| +{ | ||||
| +	size_t size; | ||||
| + | ||||
| +	*smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, | ||||
| +			    		&size); | ||||
| + | ||||
| +	if (IS_ERR(*smem_flash_type)) { | ||||
| +		pr_err("Unable to read flash type from SMEM\n"); | ||||
| +		return -ENOENT; | ||||
| +	} | ||||
| + | ||||
| +	if (size != sizeof(**smem_flash_type)) { | ||||
| +		pr_err("Invalid flash type size in SMEM\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts) | ||||
| +{ | ||||
| +	size_t size; | ||||
| + | ||||
| +	*pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, | ||||
| +				&size); | ||||
| + | ||||
| +	if (IS_ERR(*pparts)) { | ||||
| +		pr_err("Unable to read partition table from SMEM\n"); | ||||
| +		return -ENOENT; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int of_dev_node_match(struct device *dev, void *data) | ||||
| +{ | ||||
| +	return dev->of_node == data; | ||||
| +} | ||||
| + | ||||
| +static bool is_spi_device(struct device_node *np) | ||||
| +{ | ||||
| +	struct device *dev; | ||||
| + | ||||
| +	dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match); | ||||
| +	if (!dev) | ||||
| +		return false; | ||||
| + | ||||
| +	put_device(dev); | ||||
| +	return true; | ||||
| +} | ||||
| + | ||||
| +static int parse_qcom_smem_partitions(struct mtd_info *master, | ||||
| +				      const struct mtd_partition **pparts, | ||||
| +				      struct mtd_part_parser_data *data) | ||||
| +{ | ||||
| +	struct smem_partition_table *smem_parts; | ||||
| +	u64 *smem_flash_type, *smem_blksz; | ||||
| +	struct mtd_partition *mtd_parts; | ||||
| +	struct device_node *of_node = master->dev.of_node; | ||||
| +	int i, ret; | ||||
| + | ||||
| +	/* | ||||
| +	 * SMEM will only store the partition table of the boot device. | ||||
| +	 * If this is not the boot device, do not return any partition. | ||||
| +	 */ | ||||
| +	ret = qcom_smem_get_flash_type(&smem_flash_type); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master)) | ||||
| +	    || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node))) | ||||
| +		return 0; | ||||
| + | ||||
| +	/* | ||||
| +	 * Just for sanity purpose, make sure the block size in SMEM matches the | ||||
| +	 * block size of the MTD device | ||||
| +	 */ | ||||
| +	ret = qcom_smem_get_flash_blksz(&smem_blksz); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	if (*smem_blksz != master->erasesize) { | ||||
| +		pr_err("SMEM block size differs from MTD block size\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	/* Get partition pointer from SMEM */ | ||||
| +	ret = qcom_smem_get_flash_partitions(&smem_parts); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic, | ||||
| +		   sizeof(SMEM_PTABLE_MAGIC))) { | ||||
| +		pr_err("SMEM partition magic invalid\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	/* Allocate and populate the mtd structures */ | ||||
| +	mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts), | ||||
| +			    GFP_KERNEL); | ||||
| +	if (!mtd_parts) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	for (i = 0; i < smem_parts->len; i++) { | ||||
| +		struct smem_partition *s_part = &smem_parts->parts[i]; | ||||
| +		struct mtd_partition *m_part = &mtd_parts[i]; | ||||
| + | ||||
| +		m_part->name = s_part->name; | ||||
| +		m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); | ||||
| +		m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); | ||||
| + | ||||
| +		/* | ||||
| +		 * The last SMEM partition may have its size marked as | ||||
| +		 * something like 0xffffffff, which means "until the end of the | ||||
| +		 * flash device". In this case, truncate it. | ||||
| +		 */ | ||||
| +		if (m_part->offset + m_part->size > master->size) | ||||
| +			m_part->size = master->size - m_part->offset; | ||||
| +	} | ||||
| + | ||||
| +	*pparts = mtd_parts; | ||||
| + | ||||
| +	return smem_parts->len; | ||||
| +} | ||||
| + | ||||
| +static const struct of_device_id qcom_smem_of_match_table[] = { | ||||
| +	{ .compatible = "qcom,smem" }, | ||||
| +	{}, | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, qcom_smem_of_match_table); | ||||
| + | ||||
| +static struct mtd_part_parser qcom_smem_parser = { | ||||
| +	.owner = THIS_MODULE, | ||||
| +	.parse_fn = parse_qcom_smem_partitions, | ||||
| +	.name = "qcom-smem", | ||||
| +	.of_match_table = qcom_smem_of_match_table, | ||||
| +}; | ||||
| + | ||||
| +static int __init qcom_smem_parser_init(void) | ||||
| +{ | ||||
| +	register_mtd_parser(&qcom_smem_parser); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static void __exit qcom_smem_parser_exit(void) | ||||
| +{ | ||||
| +	deregister_mtd_parser(&qcom_smem_parser); | ||||
| +} | ||||
| + | ||||
| +module_init(qcom_smem_parser_init); | ||||
| +module_exit(qcom_smem_parser_exit); | ||||
| + | ||||
| +MODULE_LICENSE("GPL"); | ||||
| +MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>"); | ||||
| +MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); | ||||
| --- a/drivers/mtd/Makefile | ||||
| +++ b/drivers/mtd/Makefile | ||||
| @@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_AR7_PARTS)	+= ar7part.o | ||||
|  obj-$(CONFIG_MTD_BCM63XX_PARTS)	+= bcm63xxpart.o | ||||
|  obj-$(CONFIG_MTD_BCM47XX_PARTS)	+= bcm47xxpart.o | ||||
|  obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o | ||||
| +obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o | ||||
|  obj-y				+= parsers/ | ||||
|  | ||||
|  # 'Users' - code which presents functionality to userspace. | ||||
| @@ -0,0 +1,616 @@ | ||||
| From b9004f4fd23e4c614d71c972f3a9311665480e29 Mon Sep 17 00:00:00 2001 | ||||
| From: Andy Gross <agross@codeaurora.org> | ||||
| Date: Thu, 9 Mar 2017 08:19:18 +0100 | ||||
| Subject: [PATCH 32/69] phy: add qcom dwc3 phy | ||||
|  | ||||
| Signed-off-by: Andy Gross <agross@codeaurora.org> | ||||
| --- | ||||
|  drivers/phy/Kconfig         |  12 + | ||||
|  drivers/phy/Makefile        |   1 + | ||||
|  drivers/phy/phy-qcom-dwc3.c | 575 ++++++++++++++++++++++++++++++++++++++++++++ | ||||
|  3 files changed, 588 insertions(+) | ||||
|  create mode 100644 drivers/phy/phy-qcom-dwc3.c | ||||
|  | ||||
| --- a/drivers/phy/qualcomm/Kconfig | ||||
| +++ b/drivers/phy/qualcomm/Kconfig | ||||
| @@ -56,3 +56,15 @@ | ||||
| 	select GENERIC_PHY | ||||
| 	help | ||||
| 	  Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. | ||||
| + | ||||
| +config PHY_QCOM_DWC3 | ||||
| +	tristate "QCOM DWC3 USB PHY support" | ||||
| +	depends on ARCH_QCOM | ||||
| +	depends on HAS_IOMEM | ||||
| +	depends on OF | ||||
| +	select GENERIC_PHY | ||||
| +	help | ||||
| +	  This option enables support for the Synopsis PHYs present inside the | ||||
| +	  Qualcomm USB3.0 DWC3 controller.  This driver supports both HS and SS | ||||
| +	  PHY controllers. | ||||
| + | ||||
| --- a/drivers/phy/qualcomm/Makefile | ||||
| +++ b/drivers/phy/qualcomm/Makefile | ||||
| @@ -8,3 +8,4 @@ | ||||
|  obj-$(CONFIG_PHY_QCOM_UFS)		+= phy-qcom-ufs-qmp-20nm.o | ||||
|  obj-$(CONFIG_PHY_QCOM_USB_HS) 		+= phy-qcom-usb-hs.o | ||||
|  obj-$(CONFIG_PHY_QCOM_USB_HSIC) 	+= phy-qcom-usb-hsic.o | ||||
| +obj-$(CONFIG_PHY_QCOM_DWC3)		+= phy-qcom-dwc3.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/phy/qualcomm/phy-qcom-dwc3.c | ||||
| @@ -0,0 +1,575 @@ | ||||
| +/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| +* 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. | ||||
| +*/ | ||||
| + | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/err.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/phy/phy.h> | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/delay.h> | ||||
| + | ||||
| +/** | ||||
| + *  USB QSCRATCH Hardware registers | ||||
| + */ | ||||
| +#define QSCRATCH_GENERAL_CFG		(0x08) | ||||
| +#define HSUSB_PHY_CTRL_REG		(0x10) | ||||
| + | ||||
| +/* PHY_CTRL_REG */ | ||||
| +#define HSUSB_CTRL_DMSEHV_CLAMP			BIT(24) | ||||
| +#define HSUSB_CTRL_USB2_SUSPEND			BIT(23) | ||||
| +#define HSUSB_CTRL_UTMI_CLK_EN			BIT(21) | ||||
| +#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID		BIT(20) | ||||
| +#define HSUSB_CTRL_USE_CLKCORE			BIT(18) | ||||
| +#define HSUSB_CTRL_DPSEHV_CLAMP			BIT(17) | ||||
| +#define HSUSB_CTRL_COMMONONN			BIT(11) | ||||
| +#define HSUSB_CTRL_ID_HV_CLAMP			BIT(9) | ||||
| +#define HSUSB_CTRL_OTGSESSVLD_CLAMP		BIT(8) | ||||
| +#define HSUSB_CTRL_CLAMP_EN			BIT(7) | ||||
| +#define HSUSB_CTRL_RETENABLEN			BIT(1) | ||||
| +#define HSUSB_CTRL_POR				BIT(0) | ||||
| + | ||||
| +/* QSCRATCH_GENERAL_CFG */ | ||||
| +#define HSUSB_GCFG_XHCI_REV		BIT(2) | ||||
| + | ||||
| +/** | ||||
| + *  USB QSCRATCH Hardware registers | ||||
| + */ | ||||
| +#define SSUSB_PHY_CTRL_REG		(0x00) | ||||
| +#define SSUSB_PHY_PARAM_CTRL_1		(0x04) | ||||
| +#define SSUSB_PHY_PARAM_CTRL_2		(0x08) | ||||
| +#define CR_PROTOCOL_DATA_IN_REG		(0x0c) | ||||
| +#define CR_PROTOCOL_DATA_OUT_REG	(0x10) | ||||
| +#define CR_PROTOCOL_CAP_ADDR_REG	(0x14) | ||||
| +#define CR_PROTOCOL_CAP_DATA_REG	(0x18) | ||||
| +#define CR_PROTOCOL_READ_REG		(0x1c) | ||||
| +#define CR_PROTOCOL_WRITE_REG		(0x20) | ||||
| + | ||||
| +/* PHY_CTRL_REG */ | ||||
| +#define SSUSB_CTRL_REF_USE_PAD		BIT(28) | ||||
| +#define SSUSB_CTRL_TEST_POWERDOWN	BIT(27) | ||||
| +#define SSUSB_CTRL_LANE0_PWR_PRESENT	BIT(24) | ||||
| +#define SSUSB_CTRL_SS_PHY_EN		BIT(8) | ||||
| +#define SSUSB_CTRL_SS_PHY_RESET		BIT(7) | ||||
| + | ||||
| +/* SSPHY control registers */ | ||||
| +#define SSPHY_CTRL_RX_OVRD_IN_HI(lane)	(0x1006 + 0x100 * lane) | ||||
| +#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane)	(0x1002 + 0x100 * lane) | ||||
| + | ||||
| +/* SSPHY SoC version specific values */ | ||||
| +#define SSPHY_RX_EQ_VALUE		4	/* Override value for rx_eq */ | ||||
| +#define SSPHY_TX_DEEMPH_3_5DB		23	/* Override value for transmit | ||||
| +						   preemphasis */ | ||||
| +#define SSPHY_MPLL_VALUE		0	/* Override value for mpll */ | ||||
| + | ||||
| +/* QSCRATCH PHY_PARAM_CTRL1 fields */ | ||||
| +#define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK	0x07f00000u | ||||
| +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK	0x000fc000u | ||||
| +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK	0x00003f00u | ||||
| +#define PHY_PARAM_CTRL1_LOS_BIAS_MASK		0x000000f8u | ||||
| + | ||||
| +#define PHY_PARAM_CTRL1_MASK				\ | ||||
| +		(PHY_PARAM_CTRL1_TX_FULL_SWING_MASK |	\ | ||||
| +		 PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK |	\ | ||||
| +		 PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK |	\ | ||||
| +		 PHY_PARAM_CTRL1_LOS_BIAS_MASK) | ||||
| + | ||||
| +#define PHY_PARAM_CTRL1_TX_FULL_SWING(x)	\ | ||||
| +		(((x) << 20) & PHY_PARAM_CTRL1_TX_FULL_SWING_MASK) | ||||
| +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x)	\ | ||||
| +		(((x) << 14) & PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK) | ||||
| +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x)	\ | ||||
| +		(((x) <<  8) & PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK) | ||||
| +#define PHY_PARAM_CTRL1_LOS_BIAS(x)	\ | ||||
| +		(((x) <<  3) & PHY_PARAM_CTRL1_LOS_BIAS_MASK) | ||||
| + | ||||
| +/* RX OVRD IN HI bits */ | ||||
| +#define RX_OVRD_IN_HI_RX_RESET_OVRD		BIT(13) | ||||
| +#define RX_OVRD_IN_HI_RX_RX_RESET		BIT(12) | ||||
| +#define RX_OVRD_IN_HI_RX_EQ_OVRD		BIT(11) | ||||
| +#define RX_OVRD_IN_HI_RX_EQ_MASK		0x0700 | ||||
| +#define RX_OVRD_IN_HI_RX_EQ_SHIFT		8 | ||||
| +#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD		BIT(7) | ||||
| +#define RX_OVRD_IN_HI_RX_EQ_EN			BIT(6) | ||||
| +#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD	BIT(5) | ||||
| +#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK	0x0018 | ||||
| +#define RX_OVRD_IN_HI_RX_RATE_OVRD		BIT(2) | ||||
| +#define RX_OVRD_IN_HI_RX_RATE_MASK		0x0003 | ||||
| + | ||||
| +/* TX OVRD DRV LO register bits */ | ||||
| +#define TX_OVRD_DRV_LO_AMPLITUDE_MASK	0x007F | ||||
| +#define TX_OVRD_DRV_LO_PREEMPH_MASK	0x3F80 | ||||
| +#define TX_OVRD_DRV_LO_PREEMPH_SHIFT	7 | ||||
| +#define TX_OVRD_DRV_LO_EN		BIT(14) | ||||
| + | ||||
| +/* SS CAP register bits */ | ||||
| +#define SS_CR_CAP_ADDR_REG		BIT(0) | ||||
| +#define SS_CR_CAP_DATA_REG		BIT(0) | ||||
| +#define SS_CR_READ_REG			BIT(0) | ||||
| +#define SS_CR_WRITE_REG			BIT(0) | ||||
| + | ||||
| +struct qcom_dwc3_usb_phy { | ||||
| +	void __iomem		*base; | ||||
| +	struct device		*dev; | ||||
| +	struct clk		*xo_clk; | ||||
| +	struct clk		*ref_clk; | ||||
| +	u32			rx_eq; | ||||
| +	u32			tx_deamp_3_5db; | ||||
| +	u32			mpll; | ||||
| +}; | ||||
| + | ||||
| +struct qcom_dwc3_phy_drvdata { | ||||
| +	struct phy_ops	ops; | ||||
| +	u32		clk_rate; | ||||
| +}; | ||||
| + | ||||
| +/** | ||||
| + * Write register and read back masked value to confirm it is written | ||||
| + * | ||||
| + * @base - QCOM DWC3 PHY base virtual address. | ||||
| + * @offset - register offset. | ||||
| + * @mask - register bitmask specifying what should be updated | ||||
| + * @val - value to write. | ||||
| + */ | ||||
| +static inline void qcom_dwc3_phy_write_readback( | ||||
| +	struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset, | ||||
| +	const u32 mask, u32 val) | ||||
| +{ | ||||
| +	u32 write_val, tmp = readl(phy_dwc3->base + offset); | ||||
| + | ||||
| +	tmp &= ~mask;		/* retain other bits */ | ||||
| +	write_val = tmp | val; | ||||
| + | ||||
| +	writel(write_val, phy_dwc3->base + offset); | ||||
| + | ||||
| +	/* Read back to see if val was written */ | ||||
| +	tmp = readl(phy_dwc3->base + offset); | ||||
| +	tmp &= mask;		/* clear other bits */ | ||||
| + | ||||
| +	if (tmp != val) | ||||
| +		dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", | ||||
| +			val, offset); | ||||
| +} | ||||
| + | ||||
| +static int wait_for_latch(void __iomem *addr) | ||||
| +{ | ||||
| +	u32 retry = 10; | ||||
| + | ||||
| +	while (true) { | ||||
| +		if (!readl(addr)) | ||||
| +			break; | ||||
| + | ||||
| +		if (--retry == 0) | ||||
| +			return -ETIMEDOUT; | ||||
| + | ||||
| +		usleep_range(10, 20); | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * Write SSPHY register | ||||
| + * | ||||
| + * @base - QCOM DWC3 PHY base virtual address. | ||||
| + * @addr - SSPHY address to write. | ||||
| + * @val - value to write. | ||||
| + */ | ||||
| +static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3, | ||||
| +					u32 addr, u32 val) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); | ||||
| +	writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); | ||||
| + | ||||
| +	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); | ||||
| +	if (ret) | ||||
| +		goto err_wait; | ||||
| + | ||||
| +	writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); | ||||
| +	writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); | ||||
| + | ||||
| +	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); | ||||
| +	if (ret) | ||||
| +		goto err_wait; | ||||
| + | ||||
| +	writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); | ||||
| + | ||||
| +	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); | ||||
| + | ||||
| +err_wait: | ||||
| +	if (ret) | ||||
| +		dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * Read SSPHY register. | ||||
| + * | ||||
| + * @base - QCOM DWC3 PHY base virtual address. | ||||
| + * @addr - SSPHY address to read. | ||||
| + */ | ||||
| +static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	writel(addr, base + CR_PROTOCOL_DATA_IN_REG); | ||||
| +	writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG); | ||||
| + | ||||
| +	ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG); | ||||
| +	if (ret) | ||||
| +		goto err_wait; | ||||
| + | ||||
| +	/* | ||||
| +	 * Due to hardware bug, first read of SSPHY register might be | ||||
| +	 * incorrect. Hence as workaround, SW should perform SSPHY register | ||||
| +	 * read twice, but use only second read and ignore first read. | ||||
| +	 */ | ||||
| +	writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); | ||||
| + | ||||
| +	ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); | ||||
| +	if (ret) | ||||
| +		goto err_wait; | ||||
| + | ||||
| +	/* throwaway read */ | ||||
| +	readl(base + CR_PROTOCOL_DATA_OUT_REG); | ||||
| + | ||||
| +	writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG); | ||||
| + | ||||
| +	ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); | ||||
| +	if (ret) | ||||
| +		goto err_wait; | ||||
| + | ||||
| +	*val = readl(base + CR_PROTOCOL_DATA_OUT_REG); | ||||
| + | ||||
| +err_wait: | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static int qcom_dwc3_hs_phy_init(struct phy *phy) | ||||
| +{ | ||||
| +	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); | ||||
| +	int ret; | ||||
| +	u32 val; | ||||
| + | ||||
| +	ret = clk_prepare_enable(phy_dwc3->xo_clk); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = clk_prepare_enable(phy_dwc3->ref_clk); | ||||
| +	if (ret) { | ||||
| +		clk_disable_unprepare(phy_dwc3->xo_clk); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	/* | ||||
| +	 * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel | ||||
| +	 * enable clamping, and disable RETENTION (power-on default is ENABLED) | ||||
| +	 */ | ||||
| +	val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | | ||||
| +		HSUSB_CTRL_RETENABLEN  | HSUSB_CTRL_COMMONONN | | ||||
| +		HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | | ||||
| +		HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID | | ||||
| +		HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70; | ||||
| + | ||||
| +	/* use core clock if external reference is not present */ | ||||
| +	if (!phy_dwc3->xo_clk) | ||||
| +		val |= HSUSB_CTRL_USE_CLKCORE; | ||||
| + | ||||
| +	writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); | ||||
| +	usleep_range(2000, 2200); | ||||
| + | ||||
| +	/* Disable (bypass) VBUS and ID filters */ | ||||
| +	writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qcom_dwc3_hs_phy_exit(struct phy *phy) | ||||
| +{ | ||||
| +	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); | ||||
| + | ||||
| +	clk_disable_unprepare(phy_dwc3->ref_clk); | ||||
| +	clk_disable_unprepare(phy_dwc3->xo_clk); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qcom_dwc3_ss_phy_init(struct phy *phy) | ||||
| +{ | ||||
| +	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); | ||||
| +	int ret; | ||||
| +	u32 data = 0; | ||||
| + | ||||
| +	ret = clk_prepare_enable(phy_dwc3->xo_clk); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = clk_prepare_enable(phy_dwc3->ref_clk); | ||||
| +	if (ret) { | ||||
| +		clk_disable_unprepare(phy_dwc3->xo_clk); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	/* reset phy */ | ||||
| +	data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); | ||||
| +	writel(data | SSUSB_CTRL_SS_PHY_RESET, | ||||
| +		phy_dwc3->base + SSUSB_PHY_CTRL_REG); | ||||
| +	usleep_range(2000, 2200); | ||||
| +	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); | ||||
| + | ||||
| +	/* clear REF_PAD if we don't have XO clk */ | ||||
| +	if (!phy_dwc3->xo_clk) | ||||
| +		data &= ~SSUSB_CTRL_REF_USE_PAD; | ||||
| +	else | ||||
| +		data |= SSUSB_CTRL_REF_USE_PAD; | ||||
| + | ||||
| +	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); | ||||
| + | ||||
| +	/* wait for ref clk to become stable, this can take up to 30ms */ | ||||
| +	msleep(30); | ||||
| + | ||||
| +	data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; | ||||
| +	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); | ||||
| + | ||||
| +	/* | ||||
| +	 * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates | ||||
| +	 * in HS mode instead of SS mode. Workaround it by asserting | ||||
| +	 * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode | ||||
| +	 */ | ||||
| +	ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, 0x102D, &data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	data |= (1 << 7); | ||||
| +	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, 0x102D, data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, 0x1010, &data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	data &= ~0xff0; | ||||
| +	data |= 0x20; | ||||
| +	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, 0x1010, data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	/* | ||||
| +	 * Fix RX Equalization setting as follows | ||||
| +	 * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 | ||||
| +	 * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 | ||||
| +	 * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version | ||||
| +	 * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 | ||||
| +	 */ | ||||
| +	ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, | ||||
| +			SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	data &= ~RX_OVRD_IN_HI_RX_EQ_EN; | ||||
| +	data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; | ||||
| +	data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; | ||||
| +	data |= phy_dwc3->rx_eq << RX_OVRD_IN_HI_RX_EQ_SHIFT; | ||||
| +	data |= RX_OVRD_IN_HI_RX_EQ_OVRD; | ||||
| +	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, | ||||
| +		SSPHY_CTRL_RX_OVRD_IN_HI(0), data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	/* | ||||
| +	 * Set EQ and TX launch amplitudes as follows | ||||
| +	 * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version | ||||
| +	 * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110 | ||||
| +	 * LANE0.TX_OVRD_DRV_LO.EN set to 1. | ||||
| +	 */ | ||||
| +	ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base, | ||||
| +		SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; | ||||
| +	data |= phy_dwc3->tx_deamp_3_5db << TX_OVRD_DRV_LO_PREEMPH_SHIFT; | ||||
| +	data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; | ||||
| +	data |= 0x6E; | ||||
| +	data |= TX_OVRD_DRV_LO_EN; | ||||
| +	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, | ||||
| +		SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); | ||||
| +	if (ret) | ||||
| +		goto err_phy_trans; | ||||
| + | ||||
| +	qcom_dwc3_ss_write_phycreg(phy_dwc3, 0x30, phy_dwc3->mpll); | ||||
| + | ||||
| +	/* | ||||
| +	 * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows | ||||
| +	 * TX_FULL_SWING [26:20] amplitude to 110 | ||||
| +	 * TX_DEEMPH_6DB [19:14] to 32 | ||||
| +	 * TX_DEEMPH_3_5DB [13:8] set based on SoC version | ||||
| +	 * LOS_BIAS [7:3] to 9 | ||||
| +	 */ | ||||
| +	data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1); | ||||
| + | ||||
| +	data &= ~PHY_PARAM_CTRL1_MASK; | ||||
| + | ||||
| +	data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) | | ||||
| +		PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) | | ||||
| +		PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) | | ||||
| +		PHY_PARAM_CTRL1_LOS_BIAS(0x9); | ||||
| + | ||||
| +	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, | ||||
| +				     PHY_PARAM_CTRL1_MASK, data); | ||||
| + | ||||
| +err_phy_trans: | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static int qcom_dwc3_ss_phy_exit(struct phy *phy) | ||||
| +{ | ||||
| +	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); | ||||
| + | ||||
| +	/* Sequence to put SSPHY in low power state: | ||||
| +	 * 1. Clear REF_PHY_EN in PHY_CTRL_REG | ||||
| +	 * 2. Clear REF_USE_PAD in PHY_CTRL_REG | ||||
| +	 * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention | ||||
| +	 */ | ||||
| +	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, | ||||
| +		SSUSB_CTRL_SS_PHY_EN, 0x0); | ||||
| +	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, | ||||
| +		SSUSB_CTRL_REF_USE_PAD, 0x0); | ||||
| +	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, | ||||
| +		SSUSB_CTRL_TEST_POWERDOWN, 0x0); | ||||
| + | ||||
| +	clk_disable_unprepare(phy_dwc3->ref_clk); | ||||
| +	clk_disable_unprepare(phy_dwc3->xo_clk); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = { | ||||
| +	.ops = { | ||||
| +		.init		= qcom_dwc3_hs_phy_init, | ||||
| +		.exit		= qcom_dwc3_hs_phy_exit, | ||||
| +		.owner		= THIS_MODULE, | ||||
| +	}, | ||||
| +	.clk_rate = 60000000, | ||||
| +}; | ||||
| + | ||||
| +static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = { | ||||
| +	.ops = { | ||||
| +		.init		= qcom_dwc3_ss_phy_init, | ||||
| +		.exit		= qcom_dwc3_ss_phy_exit, | ||||
| +		.owner		= THIS_MODULE, | ||||
| +	}, | ||||
| +	.clk_rate = 125000000, | ||||
| +}; | ||||
| + | ||||
| +static const struct of_device_id qcom_dwc3_phy_table[] = { | ||||
| +	{ .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata }, | ||||
| +	{ .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata }, | ||||
| +	{ /* Sentinel */ } | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table); | ||||
| + | ||||
| +static int qcom_dwc3_phy_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct qcom_dwc3_usb_phy	*phy_dwc3; | ||||
| +	struct phy_provider		*phy_provider; | ||||
| +	struct phy			*generic_phy; | ||||
| +	struct resource			*res; | ||||
| +	const struct of_device_id *match; | ||||
| +	const struct qcom_dwc3_phy_drvdata *data; | ||||
| +	struct device_node *np; | ||||
| + | ||||
| +	phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); | ||||
| +	if (!phy_dwc3) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node); | ||||
| +	data = match->data; | ||||
| + | ||||
| +	phy_dwc3->dev = &pdev->dev; | ||||
| + | ||||
| +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| +	phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res); | ||||
| +	if (IS_ERR(phy_dwc3->base)) | ||||
| +		return PTR_ERR(phy_dwc3->base); | ||||
| + | ||||
| +	phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); | ||||
| +	if (IS_ERR(phy_dwc3->ref_clk)) { | ||||
| +		dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); | ||||
| +		return PTR_ERR(phy_dwc3->ref_clk); | ||||
| +	} | ||||
| + | ||||
| +	clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); | ||||
| + | ||||
| +	phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); | ||||
| +	if (IS_ERR(phy_dwc3->xo_clk)) { | ||||
| +		dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); | ||||
| +		phy_dwc3->xo_clk = NULL; | ||||
| +	} | ||||
| + | ||||
| +	/* Parse device node to probe HSIO settings */ | ||||
| +	np = of_node_get(pdev->dev.of_node); | ||||
| +	if (!of_compat_cmp(match->compatible, "qcom,dwc3-ss-usb-phy", | ||||
| +			   strlen(match->compatible))) { | ||||
| + | ||||
| +		if (of_property_read_u32(np, "rx_eq", &phy_dwc3->rx_eq) || | ||||
| +		    of_property_read_u32(np, "tx_deamp_3_5db", | ||||
| +					 &phy_dwc3->tx_deamp_3_5db) || | ||||
| +		    of_property_read_u32(np, "mpll", &phy_dwc3->mpll)) { | ||||
| + | ||||
| +			dev_err(phy_dwc3->dev, "cannot get HSIO settings from device node, using default values\n"); | ||||
| + | ||||
| +			/* Default HSIO settings */ | ||||
| +			phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE; | ||||
| +			phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB; | ||||
| +			phy_dwc3->mpll = SSPHY_MPLL_VALUE; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, | ||||
| +				      &data->ops); | ||||
| + | ||||
| +	if (IS_ERR(generic_phy)) | ||||
| +		return PTR_ERR(generic_phy); | ||||
| + | ||||
| +	phy_set_drvdata(generic_phy, phy_dwc3); | ||||
| +	platform_set_drvdata(pdev, phy_dwc3); | ||||
| + | ||||
| +	phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, | ||||
| +			of_phy_simple_xlate); | ||||
| + | ||||
| +	if (IS_ERR(phy_provider)) | ||||
| +		return PTR_ERR(phy_provider); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static struct platform_driver qcom_dwc3_phy_driver = { | ||||
| +	.probe		= qcom_dwc3_phy_probe, | ||||
| +	.driver		= { | ||||
| +		.name	= "qcom-dwc3-usb-phy", | ||||
| +		.owner	= THIS_MODULE, | ||||
| +		.of_match_table = qcom_dwc3_phy_table, | ||||
| +	}, | ||||
| +}; | ||||
| + | ||||
| +module_platform_driver(qcom_dwc3_phy_driver); | ||||
| + | ||||
| +MODULE_ALIAS("platform:phy-qcom-dwc3"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| +MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); | ||||
| +MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); | ||||
| +MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); | ||||
| @@ -0,0 +1,28 @@ | ||||
| From 48051ece78136e4235a2415a52797db56f8a4478 Mon Sep 17 00:00:00 2001 | ||||
| From: Mathieu Olivari <mathieu@codeaurora.org> | ||||
| Date: Tue, 21 Apr 2015 19:09:07 -0700 | ||||
| Subject: [PATCH 33/69] ARM: qcom: automatically select PCI_DOMAINS if PCI is | ||||
|  enabled | ||||
|  | ||||
| If multiple PCIe devices are present in the system, the kernel will | ||||
| panic at boot time when trying to scan the PCI buses. This happens on | ||||
| IPQ806x based platforms, which has 3 PCIe ports. | ||||
|  | ||||
| Enabling this option allows the kernel to assign the pci-domains | ||||
| according to the device-tree content. This allows multiple PCIe | ||||
| controllers to coexist in the system. | ||||
|  | ||||
| Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> | ||||
| --- | ||||
|  arch/arm/mach-qcom/Kconfig | 1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
|  | ||||
| --- a/arch/arm/mach-qcom/Kconfig | ||||
| +++ b/arch/arm/mach-qcom/Kconfig | ||||
| @@ -6,6 +6,7 @@ menuconfig ARCH_QCOM | ||||
| 	select ARM_AMBA | ||||
| 	select PINCTRL | ||||
| 	select QCOM_SCM if SMP | ||||
| +	select PCI_DOMAINS if PCI | ||||
| 	help | ||||
| 	  Support for Qualcomm's devicetree based systems. | ||||
| @@ -0,0 +1,147 @@ | ||||
| From patchwork Fri Dec  8 09:42:19 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,01/12] ARM: Add Krait L2 register accessor functions | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102101 | ||||
| Message-Id: <1512726150-7204-2-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org, Mark Rutland <mark.rutland@arm.com>, | ||||
|  Russell King <linux@arm.linux.org.uk>, | ||||
|  Courtney Cavin <courtney.cavin@sonymobile.com> | ||||
| Date: Fri,  8 Dec 2017 15:12:19 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| Krait CPUs have a handful of L2 cache controller registers that | ||||
| live behind a cp15 based indirection register. First you program | ||||
| the indirection register (l2cpselr) to point the L2 'window' | ||||
| register (l2cpdr) at what you want to read/write.  Then you | ||||
| read/write the 'window' register to do what you want. The | ||||
| l2cpselr register is not banked per-cpu so we must lock around | ||||
| accesses to it to prevent other CPUs from re-pointing l2cpdr | ||||
| underneath us. | ||||
|  | ||||
| Cc: Mark Rutland <mark.rutland@arm.com> | ||||
| Cc: Russell King <linux@arm.linux.org.uk> | ||||
| Cc: Courtney Cavin <courtney.cavin@sonymobile.com> | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  arch/arm/common/Kconfig                   |  3 ++ | ||||
|  arch/arm/common/Makefile                  |  1 + | ||||
|  arch/arm/common/krait-l2-accessors.c      | 58 +++++++++++++++++++++++++++++++ | ||||
|  arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++ | ||||
|  4 files changed, 82 insertions(+) | ||||
|  create mode 100644 arch/arm/common/krait-l2-accessors.c | ||||
|  create mode 100644 arch/arm/include/asm/krait-l2-accessors.h | ||||
|  | ||||
| --- a/arch/arm/common/Kconfig | ||||
| +++ b/arch/arm/common/Kconfig | ||||
| @@ -7,6 +7,9 @@ config DMABOUNCE | ||||
| 	bool | ||||
| 	select ZONE_DMA | ||||
|  | ||||
| +config KRAIT_L2_ACCESSORS | ||||
| +	bool | ||||
| + | ||||
|  config SHARP_LOCOMO | ||||
| 	bool | ||||
|  | ||||
| --- a/arch/arm/common/Makefile | ||||
| +++ b/arch/arm/common/Makefile | ||||
| @@ -7,6 +7,7 @@ obj-y				+= firmware.o | ||||
|  | ||||
|  obj-$(CONFIG_SA1111)		+= sa1111.o | ||||
|  obj-$(CONFIG_DMABOUNCE)		+= dmabounce.o | ||||
| +obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o | ||||
|  obj-$(CONFIG_SHARP_LOCOMO)	+= locomo.o | ||||
|  obj-$(CONFIG_SHARP_PARAM)	+= sharpsl_param.o | ||||
|  obj-$(CONFIG_SHARP_SCOOP)	+= scoop.o | ||||
| --- /dev/null | ||||
| +++ b/arch/arm/common/krait-l2-accessors.c | ||||
| @@ -0,0 +1,58 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/spinlock.h> | ||||
| +#include <linux/export.h> | ||||
| + | ||||
| +#include <asm/barrier.h> | ||||
| +#include <asm/krait-l2-accessors.h> | ||||
| + | ||||
| +static DEFINE_RAW_SPINLOCK(krait_l2_lock); | ||||
| + | ||||
| +void krait_set_l2_indirect_reg(u32 addr, u32 val) | ||||
| +{ | ||||
| +	unsigned long flags; | ||||
| + | ||||
| +	raw_spin_lock_irqsave(&krait_l2_lock, flags); | ||||
| +	/* | ||||
| +	 * Select the L2 window by poking l2cpselr, then write to the window | ||||
| +	 * via l2cpdr. | ||||
| +	 */ | ||||
| +	asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); | ||||
| +	isb(); | ||||
| +	asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val)); | ||||
| +	isb(); | ||||
| + | ||||
| +	raw_spin_unlock_irqrestore(&krait_l2_lock, flags); | ||||
| +} | ||||
| +EXPORT_SYMBOL(krait_set_l2_indirect_reg); | ||||
| + | ||||
| +u32 krait_get_l2_indirect_reg(u32 addr) | ||||
| +{ | ||||
| +	u32 val; | ||||
| +	unsigned long flags; | ||||
| + | ||||
| +	raw_spin_lock_irqsave(&krait_l2_lock, flags); | ||||
| +	/* | ||||
| +	 * Select the L2 window by poking l2cpselr, then read from the window | ||||
| +	 * via l2cpdr. | ||||
| +	 */ | ||||
| +	asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); | ||||
| +	isb(); | ||||
| +	asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val)); | ||||
| + | ||||
| +	raw_spin_unlock_irqrestore(&krait_l2_lock, flags); | ||||
| + | ||||
| +	return val; | ||||
| +} | ||||
| +EXPORT_SYMBOL(krait_get_l2_indirect_reg); | ||||
| --- /dev/null | ||||
| +++ b/arch/arm/include/asm/krait-l2-accessors.h | ||||
| @@ -0,0 +1,20 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H | ||||
| +#define __ASMARM_KRAIT_L2_ACCESSORS_H | ||||
| + | ||||
| +extern void krait_set_l2_indirect_reg(u32 addr, u32 val); | ||||
| +extern u32 krait_get_l2_indirect_reg(u32 addr); | ||||
| + | ||||
| +#endif | ||||
| @@ -0,0 +1,195 @@ | ||||
| From patchwork Fri Dec  8 09:42:20 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,02/12] clk: mux: Split out register accessors for reuse | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102103 | ||||
| Message-Id: <1512726150-7204-3-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:20 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| We want to reuse the logic in clk-mux.c for other clock drivers | ||||
| that don't use readl as register accessors. Fortunately, there | ||||
| really isn't much to the mux code besides the table indirection | ||||
| and quirk flags if you assume any bit shifting and masking has | ||||
| been done already. Pull that logic out into reusable functions | ||||
| that operate on an optional table and some flags so that other | ||||
| drivers can use the same logic. | ||||
|  | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  drivers/clk/clk-mux.c        | 75 +++++++++++++++++++++++++++----------------- | ||||
|  include/linux/clk-provider.h |  9 ++++-- | ||||
|  2 files changed, 54 insertions(+), 30 deletions(-) | ||||
|  | ||||
| --- a/drivers/clk/clk-mux.c | ||||
| +++ b/drivers/clk/clk-mux.c | ||||
| @@ -26,35 +26,24 @@ | ||||
|   * parent - parent is adjustable through clk_set_parent | ||||
|   */ | ||||
|  | ||||
| -static u8 clk_mux_get_parent(struct clk_hw *hw) | ||||
| +unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, | ||||
| +				unsigned int *table, unsigned long flags) | ||||
|  { | ||||
| -	struct clk_mux *mux = to_clk_mux(hw); | ||||
| 	int num_parents = clk_hw_get_num_parents(hw); | ||||
| -	u32 val; | ||||
| - | ||||
| -	/* | ||||
| -	 * FIXME need a mux-specific flag to determine if val is bitwise or numeric | ||||
| -	 * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 | ||||
| -	 * to 0x7 (index starts at one) | ||||
| -	 * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so | ||||
| -	 * val = 0x4 really means "bit 2, index starts at bit 0" | ||||
| -	 */ | ||||
| -	val = clk_readl(mux->reg) >> mux->shift; | ||||
| -	val &= mux->mask; | ||||
|  | ||||
| -	if (mux->table) { | ||||
| +	if (table) { | ||||
| 		int i; | ||||
|  | ||||
| 		for (i = 0; i < num_parents; i++) | ||||
| -			if (mux->table[i] == val) | ||||
| +			if (table[i] == val) | ||||
| 				return i; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| -	if (val && (mux->flags & CLK_MUX_INDEX_BIT)) | ||||
| +	if (val && (flags & CLK_MUX_INDEX_BIT)) | ||||
| 		val = ffs(val) - 1; | ||||
|  | ||||
| -	if (val && (mux->flags & CLK_MUX_INDEX_ONE)) | ||||
| +	if (val && (flags & CLK_MUX_INDEX_ONE)) | ||||
| 		val--; | ||||
|  | ||||
| 	if (val >= num_parents) | ||||
| @@ -62,23 +51,53 @@ static u8 clk_mux_get_parent(struct clk_ | ||||
|  | ||||
| 	return val; | ||||
|  } | ||||
| +EXPORT_SYMBOL_GPL(clk_mux_get_parent); | ||||
|  | ||||
| -static int clk_mux_set_parent(struct clk_hw *hw, u8 index) | ||||
| +static u8 _clk_mux_get_parent(struct clk_hw *hw) | ||||
|  { | ||||
| 	struct clk_mux *mux = to_clk_mux(hw); | ||||
| 	u32 val; | ||||
| -	unsigned long flags = 0; | ||||
|  | ||||
| -	if (mux->table) { | ||||
| -		index = mux->table[index]; | ||||
| +	/* | ||||
| +	 * FIXME need a mux-specific flag to determine if val is bitwise or | ||||
| +	 * numeric e.g. sys_clkin_ck's clksel field is 3 bits wide, | ||||
| +	 * but ranges from 0x1 to 0x7 (index starts at one) | ||||
| +	 * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so | ||||
| +	 * val = 0x4 really means "bit 2, index starts at bit 0" | ||||
| +	 */ | ||||
| +	val = clk_readl(mux->reg) >> mux->shift; | ||||
| +	val &= mux->mask; | ||||
| + | ||||
| +	return clk_mux_get_parent(hw, val, mux->table, mux->flags); | ||||
| +} | ||||
| + | ||||
| +unsigned int clk_mux_reindex(u8 index, unsigned int *table, | ||||
| +			     unsigned long flags) | ||||
| +{ | ||||
| +	unsigned int val = index; | ||||
| + | ||||
| +	if (table) { | ||||
| +		val = table[val]; | ||||
| 	} else { | ||||
| -		if (mux->flags & CLK_MUX_INDEX_BIT) | ||||
| -			index = 1 << index; | ||||
| +		if (flags & CLK_MUX_INDEX_BIT) | ||||
| +			val = 1 << index; | ||||
|  | ||||
| -		if (mux->flags & CLK_MUX_INDEX_ONE) | ||||
| -			index++; | ||||
| +		if (flags & CLK_MUX_INDEX_ONE) | ||||
| +			val++; | ||||
| 	} | ||||
|  | ||||
| +	return val; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(clk_mux_reindex); | ||||
| + | ||||
| +static int clk_mux_set_parent(struct clk_hw *hw, u8 index) | ||||
| +{ | ||||
| +	struct clk_mux *mux = to_clk_mux(hw); | ||||
| +	u32 val; | ||||
| +	unsigned long flags = 0; | ||||
| + | ||||
| +	index = clk_mux_reindex(index, mux->table, mux->flags); | ||||
| + | ||||
| 	if (mux->lock) | ||||
| 		spin_lock_irqsave(mux->lock, flags); | ||||
| 	else | ||||
| @@ -102,14 +121,14 @@ static int clk_mux_set_parent(struct clk | ||||
|  } | ||||
|  | ||||
|  const struct clk_ops clk_mux_ops = { | ||||
| -	.get_parent = clk_mux_get_parent, | ||||
| +	.get_parent = _clk_mux_get_parent, | ||||
| 	.set_parent = clk_mux_set_parent, | ||||
| 	.determine_rate = __clk_mux_determine_rate, | ||||
|  }; | ||||
|  EXPORT_SYMBOL_GPL(clk_mux_ops); | ||||
|  | ||||
|  const struct clk_ops clk_mux_ro_ops = { | ||||
| -	.get_parent = clk_mux_get_parent, | ||||
| +	.get_parent = _clk_mux_get_parent, | ||||
|  }; | ||||
|  EXPORT_SYMBOL_GPL(clk_mux_ro_ops); | ||||
|  | ||||
| @@ -117,7 +136,7 @@ struct clk_hw *clk_hw_register_mux_table | ||||
| 		const char * const *parent_names, u8 num_parents, | ||||
| 		unsigned long flags, | ||||
| 		void __iomem *reg, u8 shift, u32 mask, | ||||
| -		u8 clk_mux_flags, u32 *table, spinlock_t *lock) | ||||
| +		u8 clk_mux_flags, unsigned int *table, spinlock_t *lock) | ||||
|  { | ||||
| 	struct clk_mux *mux; | ||||
| 	struct clk_hw *hw; | ||||
| --- a/include/linux/clk-provider.h | ||||
| +++ b/include/linux/clk-provider.h | ||||
| @@ -468,7 +468,7 @@ void clk_hw_unregister_divider(struct cl | ||||
|  struct clk_mux { | ||||
| 	struct clk_hw	hw; | ||||
| 	void __iomem	*reg; | ||||
| -	u32		*table; | ||||
| +	unsigned int	*table; | ||||
| 	u32		mask; | ||||
| 	u8		shift; | ||||
| 	u8		flags; | ||||
| @@ -486,6 +486,11 @@ struct clk_mux { | ||||
|  extern const struct clk_ops clk_mux_ops; | ||||
|  extern const struct clk_ops clk_mux_ro_ops; | ||||
|  | ||||
| +unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val, | ||||
| +				unsigned int *table, unsigned long flags); | ||||
| +unsigned int clk_mux_reindex(u8 index, unsigned int *table, | ||||
| +			     unsigned long flags); | ||||
| + | ||||
|  struct clk *clk_register_mux(struct device *dev, const char *name, | ||||
| 		const char * const *parent_names, u8 num_parents, | ||||
| 		unsigned long flags, | ||||
| @@ -506,7 +511,7 @@ struct clk_hw *clk_hw_register_mux_table | ||||
| 		const char * const *parent_names, u8 num_parents, | ||||
| 		unsigned long flags, | ||||
| 		void __iomem *reg, u8 shift, u32 mask, | ||||
| -		u8 clk_mux_flags, u32 *table, spinlock_t *lock); | ||||
| +		u8 clk_mux_flags, unsigned int *table, spinlock_t *lock); | ||||
|  | ||||
|  void clk_unregister_mux(struct clk *clk); | ||||
|  void clk_hw_unregister_mux(struct clk_hw *hw); | ||||
| @@ -0,0 +1,352 @@ | ||||
| From patchwork Fri Dec  8 09:42:21 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,03/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102083 | ||||
| Message-Id: <1512726150-7204-4-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:21 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| HFPLLs are the main frequency source for Krait CPU clocks. Add | ||||
| support for changing the rate of these PLLs. | ||||
|  | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  drivers/clk/qcom/Makefile    |   1 + | ||||
|  drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++ | ||||
|  drivers/clk/qcom/clk-hfpll.h |  54 +++++++++ | ||||
|  3 files changed, 308 insertions(+) | ||||
|  create mode 100644 drivers/clk/qcom/clk-hfpll.c | ||||
|  create mode 100644 drivers/clk/qcom/clk-hfpll.h | ||||
|  | ||||
| --- a/drivers/clk/qcom/Makefile | ||||
| +++ b/drivers/clk/qcom/Makefile | ||||
| @@ -10,6 +10,7 @@ clk-qcom-y += clk-rcg2.o | ||||
|  clk-qcom-y += clk-branch.o | ||||
|  clk-qcom-y += clk-regmap-divider.o | ||||
|  clk-qcom-y += clk-regmap-mux.o | ||||
| +clk-qcom-y += clk-hfpll.o | ||||
|  clk-qcom-y += reset.o | ||||
|  clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o | ||||
|  | ||||
| --- /dev/null | ||||
| +++ b/drivers/clk/qcom/clk-hfpll.c | ||||
| @@ -0,0 +1,253 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/export.h> | ||||
| +#include <linux/regmap.h> | ||||
| +#include <linux/delay.h> | ||||
| +#include <linux/err.h> | ||||
| +#include <linux/clk-provider.h> | ||||
| +#include <linux/spinlock.h> | ||||
| + | ||||
| +#include "clk-regmap.h" | ||||
| +#include "clk-hfpll.h" | ||||
| + | ||||
| +#define PLL_OUTCTRL	BIT(0) | ||||
| +#define PLL_BYPASSNL	BIT(1) | ||||
| +#define PLL_RESET_N	BIT(2) | ||||
| + | ||||
| +/* Initialize a HFPLL at a given rate and enable it. */ | ||||
| +static void __clk_hfpll_init_once(struct clk_hw *hw) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| + | ||||
| +	if (likely(h->init_done)) | ||||
| +		return; | ||||
| + | ||||
| +	/* Configure PLL parameters for integer mode. */ | ||||
| +	if (hd->config_val) | ||||
| +		regmap_write(regmap, hd->config_reg, hd->config_val); | ||||
| +	regmap_write(regmap, hd->m_reg, 0); | ||||
| +	regmap_write(regmap, hd->n_reg, 1); | ||||
| + | ||||
| +	if (hd->user_reg) { | ||||
| +		u32 regval = hd->user_val; | ||||
| +		unsigned long rate; | ||||
| + | ||||
| +		rate = clk_hw_get_rate(hw); | ||||
| + | ||||
| +		/* Pick the right VCO. */ | ||||
| +		if (hd->user_vco_mask && rate > hd->low_vco_max_rate) | ||||
| +			regval |= hd->user_vco_mask; | ||||
| +		regmap_write(regmap, hd->user_reg, regval); | ||||
| +	} | ||||
| + | ||||
| +	if (hd->droop_reg) | ||||
| +		regmap_write(regmap, hd->droop_reg, hd->droop_val); | ||||
| + | ||||
| +	h->init_done = true; | ||||
| +} | ||||
| + | ||||
| +static void __clk_hfpll_enable(struct clk_hw *hw) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| +	u32 val; | ||||
| + | ||||
| +	__clk_hfpll_init_once(hw); | ||||
| + | ||||
| +	/* Disable PLL bypass mode. */ | ||||
| +	regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); | ||||
| + | ||||
| +	/* | ||||
| +	 * H/W requires a 5us delay between disabling the bypass and | ||||
| +	 * de-asserting the reset. Delay 10us just to be safe. | ||||
| +	 */ | ||||
| +	usleep_range(10, 100); | ||||
| + | ||||
| +	/* De-assert active-low PLL reset. */ | ||||
| +	regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); | ||||
| + | ||||
| +	/* Wait for PLL to lock. */ | ||||
| +	if (hd->status_reg) { | ||||
| +		do { | ||||
| +			regmap_read(regmap, hd->status_reg, &val); | ||||
| +		} while (!(val & BIT(hd->lock_bit))); | ||||
| +	} else { | ||||
| +		usleep_range(60, 100); | ||||
| +	} | ||||
| + | ||||
| +	/* Enable PLL output. */ | ||||
| +	regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); | ||||
| +} | ||||
| + | ||||
| +/* Enable an already-configured HFPLL. */ | ||||
| +static int clk_hfpll_enable(struct clk_hw *hw) | ||||
| +{ | ||||
| +	unsigned long flags; | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| +	u32 mode; | ||||
| + | ||||
| +	spin_lock_irqsave(&h->lock, flags); | ||||
| +	regmap_read(regmap, hd->mode_reg, &mode); | ||||
| +	if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) | ||||
| +		__clk_hfpll_enable(hw); | ||||
| +	spin_unlock_irqrestore(&h->lock, flags); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static void __clk_hfpll_disable(struct clk_hfpll *h) | ||||
| +{ | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| + | ||||
| +	/* | ||||
| +	 * Disable the PLL output, disable test mode, enable the bypass mode, | ||||
| +	 * and assert the reset. | ||||
| +	 */ | ||||
| +	regmap_update_bits(regmap, hd->mode_reg, | ||||
| +			   PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); | ||||
| +} | ||||
| + | ||||
| +static void clk_hfpll_disable(struct clk_hw *hw) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	unsigned long flags; | ||||
| + | ||||
| +	spin_lock_irqsave(&h->lock, flags); | ||||
| +	__clk_hfpll_disable(h); | ||||
| +	spin_unlock_irqrestore(&h->lock, flags); | ||||
| +} | ||||
| + | ||||
| +static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, | ||||
| +				 unsigned long *parent_rate) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	unsigned long rrate; | ||||
| + | ||||
| +	rate = clamp(rate, hd->min_rate, hd->max_rate); | ||||
| + | ||||
| +	rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; | ||||
| +	if (rrate > hd->max_rate) | ||||
| +		rrate -= *parent_rate; | ||||
| + | ||||
| +	return rrate; | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * For optimization reasons, assumes no downstream clocks are actively using | ||||
| + * it. | ||||
| + */ | ||||
| +static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, | ||||
| +			      unsigned long parent_rate) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| +	unsigned long flags; | ||||
| +	u32 l_val, val; | ||||
| +	bool enabled; | ||||
| + | ||||
| +	l_val = rate / parent_rate; | ||||
| + | ||||
| +	spin_lock_irqsave(&h->lock, flags); | ||||
| + | ||||
| +	enabled = __clk_is_enabled(hw->clk); | ||||
| +	if (enabled) | ||||
| +		__clk_hfpll_disable(h); | ||||
| + | ||||
| +	/* Pick the right VCO. */ | ||||
| +	if (hd->user_reg && hd->user_vco_mask) { | ||||
| +		regmap_read(regmap, hd->user_reg, &val); | ||||
| +		if (rate <= hd->low_vco_max_rate) | ||||
| +			val &= ~hd->user_vco_mask; | ||||
| +		else | ||||
| +			val |= hd->user_vco_mask; | ||||
| +		regmap_write(regmap, hd->user_reg, val); | ||||
| +	} | ||||
| + | ||||
| +	regmap_write(regmap, hd->l_reg, l_val); | ||||
| + | ||||
| +	if (enabled) | ||||
| +		__clk_hfpll_enable(hw); | ||||
| + | ||||
| +	spin_unlock_irqrestore(&h->lock, flags); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, | ||||
| +					   unsigned long parent_rate) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| +	u32 l_val; | ||||
| + | ||||
| +	regmap_read(regmap, hd->l_reg, &l_val); | ||||
| + | ||||
| +	return l_val * parent_rate; | ||||
| +} | ||||
| + | ||||
| +static void clk_hfpll_init(struct clk_hw *hw) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| +	u32 mode, status; | ||||
| + | ||||
| +	regmap_read(regmap, hd->mode_reg, &mode); | ||||
| +	if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { | ||||
| +		__clk_hfpll_init_once(hw); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
| +	if (hd->status_reg) { | ||||
| +		regmap_read(regmap, hd->status_reg, &status); | ||||
| +		if (!(status & BIT(hd->lock_bit))) { | ||||
| +			WARN(1, "HFPLL %s is ON, but not locked!\n", | ||||
| +			     __clk_get_name(hw->clk)); | ||||
| +			clk_hfpll_disable(hw); | ||||
| +			__clk_hfpll_init_once(hw); | ||||
| +		} | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static int hfpll_is_enabled(struct clk_hw *hw) | ||||
| +{ | ||||
| +	struct clk_hfpll *h = to_clk_hfpll(hw); | ||||
| +	struct hfpll_data const *hd = h->d; | ||||
| +	struct regmap *regmap = h->clkr.regmap; | ||||
| +	u32 mode; | ||||
| + | ||||
| +	regmap_read(regmap, hd->mode_reg, &mode); | ||||
| +	mode &= 0x7; | ||||
| +	return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); | ||||
| +} | ||||
| + | ||||
| +const struct clk_ops clk_ops_hfpll = { | ||||
| +	.enable = clk_hfpll_enable, | ||||
| +	.disable = clk_hfpll_disable, | ||||
| +	.is_enabled = hfpll_is_enabled, | ||||
| +	.round_rate = clk_hfpll_round_rate, | ||||
| +	.set_rate = clk_hfpll_set_rate, | ||||
| +	.recalc_rate = clk_hfpll_recalc_rate, | ||||
| +	.init = clk_hfpll_init, | ||||
| +}; | ||||
| +EXPORT_SYMBOL_GPL(clk_ops_hfpll); | ||||
| --- /dev/null | ||||
| +++ b/drivers/clk/qcom/clk-hfpll.h | ||||
| @@ -0,0 +1,54 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| +#ifndef __QCOM_CLK_HFPLL_H__ | ||||
| +#define __QCOM_CLK_HFPLL_H__ | ||||
| + | ||||
| +#include <linux/clk-provider.h> | ||||
| +#include <linux/spinlock.h> | ||||
| +#include "clk-regmap.h" | ||||
| + | ||||
| +struct hfpll_data { | ||||
| +	u32 mode_reg; | ||||
| +	u32 l_reg; | ||||
| +	u32 m_reg; | ||||
| +	u32 n_reg; | ||||
| +	u32 user_reg; | ||||
| +	u32 droop_reg; | ||||
| +	u32 config_reg; | ||||
| +	u32 status_reg; | ||||
| +	u8  lock_bit; | ||||
| + | ||||
| +	u32 droop_val; | ||||
| +	u32 config_val; | ||||
| +	u32 user_val; | ||||
| +	u32 user_vco_mask; | ||||
| +	unsigned long low_vco_max_rate; | ||||
| + | ||||
| +	unsigned long min_rate; | ||||
| +	unsigned long max_rate; | ||||
| +}; | ||||
| + | ||||
| +struct clk_hfpll { | ||||
| +	struct hfpll_data const *d; | ||||
| +	int init_done; | ||||
| + | ||||
| +	struct clk_regmap clkr; | ||||
| +	spinlock_t lock; | ||||
| +}; | ||||
| + | ||||
| +#define to_clk_hfpll(_hw) \ | ||||
| +	container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) | ||||
| + | ||||
| +extern const struct clk_ops clk_ops_hfpll; | ||||
| + | ||||
| +#endif | ||||
| @@ -0,0 +1,206 @@ | ||||
| From patchwork Fri Dec  8 09:42:22 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,04/12] clk: qcom: Add HFPLL driver | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102079 | ||||
| Message-Id: <1512726150-7204-5-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:22 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| On some devices (MSM8974 for example), the HFPLLs are | ||||
| instantiated within the Krait processor subsystem as separate | ||||
| register regions. Add a driver for these PLLs so that we can | ||||
| provide HFPLL clocks for use by the system. | ||||
|  | ||||
| Cc: <devicetree@vger.kernel.org> | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  .../devicetree/bindings/clock/qcom,hfpll.txt       |  40 ++++++++ | ||||
|  drivers/clk/qcom/Kconfig                           |   8 ++ | ||||
|  drivers/clk/qcom/Makefile                          |   1 + | ||||
|  drivers/clk/qcom/hfpll.c                           | 106 +++++++++++++++++++++ | ||||
|  4 files changed, 155 insertions(+) | ||||
|  create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt | ||||
|  create mode 100644 drivers/clk/qcom/hfpll.c | ||||
|  | ||||
| --- /dev/null | ||||
| +++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt | ||||
| @@ -0,0 +1,40 @@ | ||||
| +High-Frequency PLL (HFPLL) | ||||
| + | ||||
| +PROPERTIES | ||||
| + | ||||
| +- compatible: | ||||
| +	Usage: required | ||||
| +	Value type: <string> | ||||
| +	Definition: must be "qcom,hfpll" | ||||
| + | ||||
| +- reg: | ||||
| +	Usage: required | ||||
| +	Value type: <prop-encoded-array> | ||||
| +	Definition: address and size of HPLL registers. An optional second | ||||
| +		    element specifies the address and size of the alias | ||||
| +		    register region. | ||||
| + | ||||
| +- clock-output-names: | ||||
| +	Usage: required | ||||
| +	Value type: <string> | ||||
| +	Definition: Name of the PLL. Typically hfpllX where X is a CPU number | ||||
| +		    starting at 0. Otherwise hfpll_Y where Y is more specific | ||||
| +		    such as "l2". | ||||
| + | ||||
| +Example: | ||||
| + | ||||
| +1) An HFPLL for the L2 cache. | ||||
| + | ||||
| +	clock-controller@f9016000 { | ||||
| +		compatible = "qcom,hfpll"; | ||||
| +		reg = <0xf9016000 0x30>; | ||||
| +		clock-output-names = "hfpll_l2"; | ||||
| +	}; | ||||
| + | ||||
| +2) An HFPLL for CPU0. This HFPLL has the alias register region. | ||||
| + | ||||
| +	clock-controller@f908a000 { | ||||
| +		compatible = "qcom,hfpll"; | ||||
| +		reg = <0xf908a000 0x30>, <0xf900a000 0x30>; | ||||
| +		clock-output-names = "hfpll0"; | ||||
| +	}; | ||||
| --- a/drivers/clk/qcom/Kconfig | ||||
| +++ b/drivers/clk/qcom/Kconfig | ||||
| @@ -196,3 +196,11 @@ config MSM_MMCC_8996 | ||||
| 	  Support for the multimedia clock controller on msm8996 devices. | ||||
| 	  Say Y if you want to support multimedia devices such as display, | ||||
| 	  graphics, video encode/decode, camera, etc. | ||||
| + | ||||
| +config QCOM_HFPLL | ||||
| +	tristate "High-Frequency PLL (HFPLL) Clock Controller" | ||||
| +	depends on COMMON_CLK_QCOM | ||||
| +	help | ||||
| +	  Support for the high-frequency PLLs present on Qualcomm devices. | ||||
| +	  Say Y if you want to support CPU frequency scaling on devices | ||||
| +	  such as MSM8974, APQ8084, etc. | ||||
| --- a/drivers/clk/qcom/Makefile | ||||
| +++ b/drivers/clk/qcom/Makefile | ||||
| @@ -35,3 +35,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 | ||||
|  obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o | ||||
|  obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o | ||||
|  obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o | ||||
| +obj-$(CONFIG_QCOM_HFPLL) += hfpll.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/clk/qcom/hfpll.c | ||||
| @@ -0,0 +1,106 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/clk-provider.h> | ||||
| +#include <linux/regmap.h> | ||||
| + | ||||
| +#include "clk-regmap.h" | ||||
| +#include "clk-hfpll.h" | ||||
| + | ||||
| +static const struct hfpll_data hdata = { | ||||
| +	.mode_reg = 0x00, | ||||
| +	.l_reg = 0x04, | ||||
| +	.m_reg = 0x08, | ||||
| +	.n_reg = 0x0c, | ||||
| +	.user_reg = 0x10, | ||||
| +	.config_reg = 0x14, | ||||
| +	.config_val = 0x430405d, | ||||
| +	.status_reg = 0x1c, | ||||
| +	.lock_bit = 16, | ||||
| + | ||||
| +	.user_val = 0x8, | ||||
| +	.user_vco_mask = 0x100000, | ||||
| +	.low_vco_max_rate = 1248000000, | ||||
| +	.min_rate = 537600000UL, | ||||
| +	.max_rate = 2900000000UL, | ||||
| +}; | ||||
| + | ||||
| +static const struct of_device_id qcom_hfpll_match_table[] = { | ||||
| +	{ .compatible = "qcom,hfpll" }, | ||||
| +	{ } | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); | ||||
| + | ||||
| +static const struct regmap_config hfpll_regmap_config = { | ||||
| +	.reg_bits	= 32, | ||||
| +	.reg_stride	= 4, | ||||
| +	.val_bits	= 32, | ||||
| +	.max_register	= 0x30, | ||||
| +	.fast_io	= true, | ||||
| +}; | ||||
| + | ||||
| +static int qcom_hfpll_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct resource *res; | ||||
| +	struct device *dev = &pdev->dev; | ||||
| +	void __iomem *base; | ||||
| +	struct regmap *regmap; | ||||
| +	struct clk_hfpll *h; | ||||
| +	struct clk_init_data init = { | ||||
| +		.parent_names = (const char *[]){ "xo" }, | ||||
| +		.num_parents = 1, | ||||
| +		.ops = &clk_ops_hfpll, | ||||
| +	}; | ||||
| + | ||||
| +	h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); | ||||
| +	if (!h) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| +	base = devm_ioremap_resource(dev, res); | ||||
| +	if (IS_ERR(base)) | ||||
| +		return PTR_ERR(base); | ||||
| + | ||||
| +	regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); | ||||
| +	if (IS_ERR(regmap)) | ||||
| +		return PTR_ERR(regmap); | ||||
| + | ||||
| +	if (of_property_read_string_index(dev->of_node, "clock-output-names", | ||||
| +					  0, &init.name)) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	h->d = &hdata; | ||||
| +	h->clkr.hw.init = &init; | ||||
| +	spin_lock_init(&h->lock); | ||||
| + | ||||
| +	return devm_clk_register_regmap(&pdev->dev, &h->clkr); | ||||
| +} | ||||
| + | ||||
| +static struct platform_driver qcom_hfpll_driver = { | ||||
| +	.probe		= qcom_hfpll_probe, | ||||
| +	.driver		= { | ||||
| +		.name	= "qcom-hfpll", | ||||
| +		.of_match_table = qcom_hfpll_match_table, | ||||
| +	}, | ||||
| +}; | ||||
| +module_platform_driver(qcom_hfpll_driver); | ||||
| + | ||||
| +MODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| +MODULE_ALIAS("platform:qcom-hfpll"); | ||||
| @@ -0,0 +1,129 @@ | ||||
| From patchwork Fri Dec  8 09:42:24 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,06/12] clk: qcom: Add IPQ806X's HFPLLs | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102047 | ||||
| Message-Id: <1512726150-7204-7-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:24 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| Describe the HFPLLs present on IPQ806X devices. | ||||
|  | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  drivers/clk/qcom/gcc-ipq806x.c | 82 ++++++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 82 insertions(+) | ||||
|  | ||||
| --- a/drivers/clk/qcom/gcc-ipq806x.c | ||||
| +++ b/drivers/clk/qcom/gcc-ipq806x.c | ||||
| @@ -30,6 +30,7 @@ | ||||
|  #include "clk-pll.h" | ||||
|  #include "clk-rcg.h" | ||||
|  #include "clk-branch.h" | ||||
| +#include "clk-hfpll.h" | ||||
|  #include "reset.h" | ||||
|  | ||||
|  static struct clk_pll pll0 = { | ||||
| @@ -113,6 +114,84 @@ static struct clk_regmap pll8_vote = { | ||||
| 	}, | ||||
|  }; | ||||
|  | ||||
| +static struct hfpll_data hfpll0_data = { | ||||
| +	.mode_reg = 0x3200, | ||||
| +	.l_reg = 0x3208, | ||||
| +	.m_reg = 0x320c, | ||||
| +	.n_reg = 0x3210, | ||||
| +	.config_reg = 0x3204, | ||||
| +	.status_reg = 0x321c, | ||||
| +	.config_val = 0x7845c665, | ||||
| +	.droop_reg = 0x3214, | ||||
| +	.droop_val = 0x0108c000, | ||||
| +	.min_rate = 600000000UL, | ||||
| +	.max_rate = 1800000000UL, | ||||
| +}; | ||||
| + | ||||
| +static struct clk_hfpll hfpll0 = { | ||||
| +	.d = &hfpll0_data, | ||||
| +	.clkr.hw.init = &(struct clk_init_data){ | ||||
| +		.parent_names = (const char *[]){ "pxo" }, | ||||
| +		.num_parents = 1, | ||||
| +		.name = "hfpll0", | ||||
| +		.ops = &clk_ops_hfpll, | ||||
| +		.flags = CLK_IGNORE_UNUSED, | ||||
| +	}, | ||||
| +	.lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), | ||||
| +}; | ||||
| + | ||||
| +static struct hfpll_data hfpll1_data = { | ||||
| +	.mode_reg = 0x3240, | ||||
| +	.l_reg = 0x3248, | ||||
| +	.m_reg = 0x324c, | ||||
| +	.n_reg = 0x3250, | ||||
| +	.config_reg = 0x3244, | ||||
| +	.status_reg = 0x325c, | ||||
| +	.config_val = 0x7845c665, | ||||
| +	.droop_reg = 0x3314, | ||||
| +	.droop_val = 0x0108c000, | ||||
| +	.min_rate = 600000000UL, | ||||
| +	.max_rate = 1800000000UL, | ||||
| +}; | ||||
| + | ||||
| +static struct clk_hfpll hfpll1 = { | ||||
| +	.d = &hfpll1_data, | ||||
| +	.clkr.hw.init = &(struct clk_init_data){ | ||||
| +		.parent_names = (const char *[]){ "pxo" }, | ||||
| +		.num_parents = 1, | ||||
| +		.name = "hfpll1", | ||||
| +		.ops = &clk_ops_hfpll, | ||||
| +		.flags = CLK_IGNORE_UNUSED, | ||||
| +	}, | ||||
| +	.lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), | ||||
| +}; | ||||
| + | ||||
| +static struct hfpll_data hfpll_l2_data = { | ||||
| +	.mode_reg = 0x3300, | ||||
| +	.l_reg = 0x3308, | ||||
| +	.m_reg = 0x330c, | ||||
| +	.n_reg = 0x3310, | ||||
| +	.config_reg = 0x3304, | ||||
| +	.status_reg = 0x331c, | ||||
| +	.config_val = 0x7845c665, | ||||
| +	.droop_reg = 0x3314, | ||||
| +	.droop_val = 0x0108c000, | ||||
| +	.min_rate = 600000000UL, | ||||
| +	.max_rate = 1800000000UL, | ||||
| +}; | ||||
| + | ||||
| +static struct clk_hfpll hfpll_l2 = { | ||||
| +	.d = &hfpll_l2_data, | ||||
| +	.clkr.hw.init = &(struct clk_init_data){ | ||||
| +		.parent_names = (const char *[]){ "pxo" }, | ||||
| +		.num_parents = 1, | ||||
| +		.name = "hfpll_l2", | ||||
| +		.ops = &clk_ops_hfpll, | ||||
| +		.flags = CLK_IGNORE_UNUSED, | ||||
| +	}, | ||||
| +	.lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), | ||||
| +}; | ||||
| + | ||||
|  static struct clk_pll pll14 = { | ||||
| 	.l_reg = 0x31c4, | ||||
| 	.m_reg = 0x31c8, | ||||
| @@ -2801,6 +2880,9 @@ static struct clk_regmap *gcc_ipq806x_cl | ||||
| 	[UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, | ||||
| 	[NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, | ||||
| 	[NSSTCM_CLK] = &nss_tcm_clk.clkr, | ||||
| +	[PLL9] = &hfpll0.clkr, | ||||
| +	[PLL10] = &hfpll1.clkr, | ||||
| +	[PLL12] = &hfpll_l2.clkr, | ||||
|  }; | ||||
|  | ||||
|  static const struct qcom_reset_map gcc_ipq806x_resets[] = { | ||||
| @@ -0,0 +1,241 @@ | ||||
| From patchwork Fri Dec  8 09:42:25 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,07/12] clk: qcom: Add support for Krait clocks | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102051 | ||||
| Message-Id: <1512726150-7204-8-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:25 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| The Krait clocks are made up of a series of muxes and a divider | ||||
| that choose between a fixed rate clock and dedicated HFPLLs for | ||||
| each CPU. Instead of using mmio accesses to remux parents, the | ||||
| Krait implementation exposes the remux control via cp15 | ||||
| registers. Support these clocks. | ||||
|  | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  drivers/clk/qcom/Kconfig     |   4 ++ | ||||
|  drivers/clk/qcom/Makefile    |   1 + | ||||
|  drivers/clk/qcom/clk-krait.c | 134 +++++++++++++++++++++++++++++++++++++++++++ | ||||
|  drivers/clk/qcom/clk-krait.h |  48 ++++++++++++++++ | ||||
|  4 files changed, 187 insertions(+) | ||||
|  create mode 100644 drivers/clk/qcom/clk-krait.c | ||||
|  create mode 100644 drivers/clk/qcom/clk-krait.h | ||||
|  | ||||
| --- a/drivers/clk/qcom/Kconfig | ||||
| +++ b/drivers/clk/qcom/Kconfig | ||||
| @@ -204,3 +204,7 @@ config QCOM_HFPLL | ||||
| 	  Support for the high-frequency PLLs present on Qualcomm devices. | ||||
| 	  Say Y if you want to support CPU frequency scaling on devices | ||||
| 	  such as MSM8974, APQ8084, etc. | ||||
| + | ||||
| +config KRAIT_CLOCKS | ||||
| +	bool | ||||
| +	select KRAIT_L2_ACCESSORS | ||||
| --- a/drivers/clk/qcom/Makefile | ||||
| +++ b/drivers/clk/qcom/Makefile | ||||
| @@ -10,6 +10,7 @@ clk-qcom-y += clk-rcg2.o | ||||
|  clk-qcom-y += clk-branch.o | ||||
|  clk-qcom-y += clk-regmap-divider.o | ||||
|  clk-qcom-y += clk-regmap-mux.o | ||||
| +clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o | ||||
|  clk-qcom-y += clk-hfpll.o | ||||
|  clk-qcom-y += reset.o | ||||
|  clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/clk/qcom/clk-krait.c | ||||
| @@ -0,0 +1,134 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/delay.h> | ||||
| +#include <linux/err.h> | ||||
| +#include <linux/clk-provider.h> | ||||
| +#include <linux/spinlock.h> | ||||
| + | ||||
| +#include <asm/krait-l2-accessors.h> | ||||
| + | ||||
| +#include "clk-krait.h" | ||||
| + | ||||
| +/* Secondary and primary muxes share the same cp15 register */ | ||||
| +static DEFINE_SPINLOCK(krait_clock_reg_lock); | ||||
| + | ||||
| +#define LPL_SHIFT	8 | ||||
| +static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) | ||||
| +{ | ||||
| +	unsigned long flags; | ||||
| +	u32 regval; | ||||
| + | ||||
| +	spin_lock_irqsave(&krait_clock_reg_lock, flags); | ||||
| +	regval = krait_get_l2_indirect_reg(mux->offset); | ||||
| +	regval &= ~(mux->mask << mux->shift); | ||||
| +	regval |= (sel & mux->mask) << mux->shift; | ||||
| +	if (mux->lpl) { | ||||
| +		regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); | ||||
| +		regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); | ||||
| +	} | ||||
| +	krait_set_l2_indirect_reg(mux->offset, regval); | ||||
| +	spin_unlock_irqrestore(&krait_clock_reg_lock, flags); | ||||
| + | ||||
| +	/* Wait for switch to complete. */ | ||||
| +	mb(); | ||||
| +	udelay(1); | ||||
| +} | ||||
| + | ||||
| +static int krait_mux_set_parent(struct clk_hw *hw, u8 index) | ||||
| +{ | ||||
| +	struct krait_mux_clk *mux = to_krait_mux_clk(hw); | ||||
| +	u32 sel; | ||||
| + | ||||
| +	sel = clk_mux_reindex(index, mux->parent_map, 0); | ||||
| +	mux->en_mask = sel; | ||||
| +	/* Don't touch mux if CPU is off as it won't work */ | ||||
| +	if (__clk_is_enabled(hw->clk)) | ||||
| +		__krait_mux_set_sel(mux, sel); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static u8 krait_mux_get_parent(struct clk_hw *hw) | ||||
| +{ | ||||
| +	struct krait_mux_clk *mux = to_krait_mux_clk(hw); | ||||
| +	u32 sel; | ||||
| + | ||||
| +	sel = krait_get_l2_indirect_reg(mux->offset); | ||||
| +	sel >>= mux->shift; | ||||
| +	sel &= mux->mask; | ||||
| +	mux->en_mask = sel; | ||||
| + | ||||
| +	return clk_mux_get_parent(hw, sel, mux->parent_map, 0); | ||||
| +} | ||||
| + | ||||
| +const struct clk_ops krait_mux_clk_ops = { | ||||
| +	.set_parent = krait_mux_set_parent, | ||||
| +	.get_parent = krait_mux_get_parent, | ||||
| +	.determine_rate = __clk_mux_determine_rate_closest, | ||||
| +}; | ||||
| +EXPORT_SYMBOL_GPL(krait_mux_clk_ops); | ||||
| + | ||||
| +/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ | ||||
| +static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, | ||||
| +				  unsigned long *parent_rate) | ||||
| +{ | ||||
| +	*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2); | ||||
| +	return DIV_ROUND_UP(*parent_rate, 2); | ||||
| +} | ||||
| + | ||||
| +static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, | ||||
| +			       unsigned long parent_rate) | ||||
| +{ | ||||
| +	struct krait_div2_clk *d = to_krait_div2_clk(hw); | ||||
| +	unsigned long flags; | ||||
| +	u32 val; | ||||
| +	u32 mask = BIT(d->width) - 1; | ||||
| + | ||||
| +	if (d->lpl) | ||||
| +		mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; | ||||
| + | ||||
| +	spin_lock_irqsave(&krait_clock_reg_lock, flags); | ||||
| +	val = krait_get_l2_indirect_reg(d->offset); | ||||
| +	val &= ~mask; | ||||
| +	krait_set_l2_indirect_reg(d->offset, val); | ||||
| +	spin_unlock_irqrestore(&krait_clock_reg_lock, flags); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static unsigned long | ||||
| +krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | ||||
| +{ | ||||
| +	struct krait_div2_clk *d = to_krait_div2_clk(hw); | ||||
| +	u32 mask = BIT(d->width) - 1; | ||||
| +	u32 div; | ||||
| + | ||||
| +	div = krait_get_l2_indirect_reg(d->offset); | ||||
| +	div >>= d->shift; | ||||
| +	div &= mask; | ||||
| +	div = (div + 1) * 2; | ||||
| + | ||||
| +	return DIV_ROUND_UP(parent_rate, div); | ||||
| +} | ||||
| + | ||||
| +const struct clk_ops krait_div2_clk_ops = { | ||||
| +	.round_rate = krait_div2_round_rate, | ||||
| +	.set_rate = krait_div2_set_rate, | ||||
| +	.recalc_rate = krait_div2_recalc_rate, | ||||
| +}; | ||||
| +EXPORT_SYMBOL_GPL(krait_div2_clk_ops); | ||||
| --- /dev/null | ||||
| +++ b/drivers/clk/qcom/clk-krait.h | ||||
| @@ -0,0 +1,48 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2013, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#ifndef __QCOM_CLK_KRAIT_H | ||||
| +#define __QCOM_CLK_KRAIT_H | ||||
| + | ||||
| +#include <linux/clk-provider.h> | ||||
| + | ||||
| +struct krait_mux_clk { | ||||
| +	unsigned int	*parent_map; | ||||
| +	u32		offset; | ||||
| +	u32		mask; | ||||
| +	u32		shift; | ||||
| +	u32		en_mask; | ||||
| +	bool		lpl; | ||||
| + | ||||
| +	struct clk_hw	hw; | ||||
| +	struct notifier_block   clk_nb; | ||||
| +}; | ||||
| + | ||||
| +#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw) | ||||
| + | ||||
| +extern const struct clk_ops krait_mux_clk_ops; | ||||
| + | ||||
| +struct krait_div2_clk { | ||||
| +	u32		offset; | ||||
| +	u8		width; | ||||
| +	u32		shift; | ||||
| +	bool		lpl; | ||||
| + | ||||
| +	struct clk_hw	hw; | ||||
| +}; | ||||
| + | ||||
| +#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw) | ||||
| + | ||||
| +extern const struct clk_ops krait_div2_clk_ops; | ||||
| + | ||||
| +#endif | ||||
| @@ -0,0 +1,209 @@ | ||||
| From patchwork Fri Dec  8 09:42:26 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,08/12] clk: qcom: Add KPSS ACC/GCC driver | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102023 | ||||
| Message-Id: <1512726150-7204-9-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:26 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| The ACC and GCC regions present in KPSSv1 contain registers to | ||||
| control clocks and power to each Krait CPU and L2. For CPUfreq | ||||
| purposes probe these devices and expose a mux clock that chooses | ||||
| between PXO and PLL8. | ||||
|  | ||||
| Cc: <devicetree@vger.kernel.org> | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt  |  7 ++ | ||||
|  .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt  | 28 +++++++ | ||||
|  drivers/clk/qcom/Kconfig                           |  8 ++ | ||||
|  drivers/clk/qcom/Makefile                          |  1 + | ||||
|  drivers/clk/qcom/kpss-xcc.c                        | 96 ++++++++++++++++++++++ | ||||
|  5 files changed, 140 insertions(+) | ||||
|  create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | ||||
|  create mode 100644 drivers/clk/qcom/kpss-xcc.c | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt | ||||
| +++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt | ||||
| @@ -21,10 +21,17 @@ PROPERTIES | ||||
| 		    the register region. An optional second element specifies | ||||
| 		    the base address and size of the alias register region. | ||||
|  | ||||
| +- clock-output-names: | ||||
| +	Usage: optional | ||||
| +	Value type: <string> | ||||
| +	Definition: Name of the output clock. Typically acpuX_aux where X is a | ||||
| +		    CPU number starting at 0. | ||||
| + | ||||
|  Example: | ||||
|  | ||||
| 	clock-controller@2088000 { | ||||
| 		compatible = "qcom,kpss-acc-v2"; | ||||
| 		reg = <0x02088000 0x1000>, | ||||
| 		      <0x02008000 0x1000>; | ||||
| +		clock-output-names = "acpu0_aux"; | ||||
| 	}; | ||||
| --- /dev/null | ||||
| +++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | ||||
| @@ -0,0 +1,28 @@ | ||||
| +Krait Processor Sub-system (KPSS) Global Clock Controller (GCC) | ||||
| + | ||||
| +PROPERTIES | ||||
| + | ||||
| +- compatible: | ||||
| +	Usage: required | ||||
| +	Value type: <string> | ||||
| +	Definition: should be one of: | ||||
| +			"qcom,kpss-gcc" | ||||
| + | ||||
| +- reg: | ||||
| +	Usage: required | ||||
| +	Value type: <prop-encoded-array> | ||||
| +	Definition: base address and size of the register region | ||||
| + | ||||
| +- clock-output-names: | ||||
| +	Usage: required | ||||
| +	Value type: <string> | ||||
| +	Definition: Name of the output clock. Typically acpu_l2_aux indicating | ||||
| +		    an L2 cache auxiliary clock. | ||||
| + | ||||
| +Example: | ||||
| + | ||||
| +	l2cc: clock-controller@2011000 { | ||||
| +		compatible = "qcom,kpss-gcc"; | ||||
| +		reg = <0x2011000 0x1000>; | ||||
| +		clock-output-names = "acpu_l2_aux"; | ||||
| +	}; | ||||
| --- a/drivers/clk/qcom/Kconfig | ||||
| +++ b/drivers/clk/qcom/Kconfig | ||||
| @@ -205,6 +205,14 @@ config QCOM_HFPLL | ||||
| 	  Say Y if you want to support CPU frequency scaling on devices | ||||
| 	  such as MSM8974, APQ8084, etc. | ||||
|  | ||||
| +config KPSS_XCC | ||||
| +	tristate "KPSS Clock Controller" | ||||
| +	depends on COMMON_CLK_QCOM | ||||
| +	help | ||||
| +	  Support for the Krait ACC and GCC clock controllers. Say Y | ||||
| +	  if you want to support CPU frequency scaling on devices such | ||||
| +	  as MSM8960, APQ8064, etc. | ||||
| + | ||||
|  config KRAIT_CLOCKS | ||||
| 	bool | ||||
| 	select KRAIT_L2_ACCESSORS | ||||
| --- a/drivers/clk/qcom/Makefile | ||||
| +++ b/drivers/clk/qcom/Makefile | ||||
| @@ -36,4 +36,5 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 | ||||
|  obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o | ||||
|  obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o | ||||
|  obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o | ||||
| +obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o | ||||
|  obj-$(CONFIG_QCOM_HFPLL) += hfpll.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/clk/qcom/kpss-xcc.c | ||||
| @@ -0,0 +1,96 @@ | ||||
| +/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/err.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/of_device.h> | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/clk-provider.h> | ||||
| + | ||||
| +static const char *aux_parents[] = { | ||||
| +	"pll8_vote", | ||||
| +	"pxo", | ||||
| +}; | ||||
| + | ||||
| +static unsigned int aux_parent_map[] = { | ||||
| +	3, | ||||
| +	0, | ||||
| +}; | ||||
| + | ||||
| +static const struct of_device_id kpss_xcc_match_table[] = { | ||||
| +	{ .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL }, | ||||
| +	{ .compatible = "qcom,kpss-gcc" }, | ||||
| +	{} | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, kpss_xcc_match_table); | ||||
| + | ||||
| +static int kpss_xcc_driver_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	const struct of_device_id *id; | ||||
| +	struct clk *clk; | ||||
| +	struct resource *res; | ||||
| +	void __iomem *base; | ||||
| +	const char *name; | ||||
| + | ||||
| +	id = of_match_device(kpss_xcc_match_table, &pdev->dev); | ||||
| +	if (!id) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| +	base = devm_ioremap_resource(&pdev->dev, res); | ||||
| +	if (IS_ERR(base)) | ||||
| +		return PTR_ERR(base); | ||||
| + | ||||
| +	if (id->data) { | ||||
| +		if (of_property_read_string_index(pdev->dev.of_node, | ||||
| +						  "clock-output-names", | ||||
| +						  0, &name)) | ||||
| +			return -ENODEV; | ||||
| +		base += 0x14; | ||||
| +	} else { | ||||
| +		name = "acpu_l2_aux"; | ||||
| +		base += 0x28; | ||||
| +	} | ||||
| + | ||||
| +	clk = clk_register_mux_table(&pdev->dev, name, aux_parents, | ||||
| +				     ARRAY_SIZE(aux_parents), 0, base, 0, 0x3, | ||||
| +				     0, aux_parent_map, NULL); | ||||
| + | ||||
| +	platform_set_drvdata(pdev, clk); | ||||
| + | ||||
| +	return PTR_ERR_OR_ZERO(clk); | ||||
| +} | ||||
| + | ||||
| +static int kpss_xcc_driver_remove(struct platform_device *pdev) | ||||
| +{ | ||||
| +	clk_unregister_mux(platform_get_drvdata(pdev)); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static struct platform_driver kpss_xcc_driver = { | ||||
| +	.probe = kpss_xcc_driver_probe, | ||||
| +	.remove = kpss_xcc_driver_remove, | ||||
| +	.driver = { | ||||
| +		.name = "kpss-xcc", | ||||
| +		.of_match_table = kpss_xcc_match_table, | ||||
| +	}, | ||||
| +}; | ||||
| +module_platform_driver(kpss_xcc_driver); | ||||
| + | ||||
| +MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| +MODULE_ALIAS("platform:kpss-xcc"); | ||||
| @@ -0,0 +1,436 @@ | ||||
| From patchwork Fri Dec  8 09:42:27 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,09/12] clk: qcom: Add Krait clock controller driver | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102061 | ||||
| Message-Id: <1512726150-7204-10-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:27 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| The Krait CPU clocks are made up of a primary mux and secondary | ||||
| mux for each CPU and the L2, controlled via cp15 accessors. For | ||||
| Kraits within KPSSv1 each secondary mux accepts a different aux | ||||
| source, but on KPSSv2 each secondary mux accepts the same aux | ||||
| source. | ||||
|  | ||||
| Cc: <devicetree@vger.kernel.org> | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  .../devicetree/bindings/clock/qcom,krait-cc.txt    |  22 ++ | ||||
|  drivers/clk/qcom/Kconfig                           |   8 + | ||||
|  drivers/clk/qcom/Makefile                          |   1 + | ||||
|  drivers/clk/qcom/krait-cc.c                        | 350 +++++++++++++++++++++ | ||||
|  4 files changed, 381 insertions(+) | ||||
|  create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt | ||||
|  create mode 100644 drivers/clk/qcom/krait-cc.c | ||||
|  | ||||
| --- /dev/null | ||||
| +++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt | ||||
| @@ -0,0 +1,22 @@ | ||||
| +Krait Clock Controller | ||||
| + | ||||
| +PROPERTIES | ||||
| + | ||||
| +- compatible: | ||||
| +	Usage: required | ||||
| +	Value type: <string> | ||||
| +	Definition: must be one of: | ||||
| +			"qcom,krait-cc-v1" | ||||
| +			"qcom,krait-cc-v2" | ||||
| + | ||||
| +- #clock-cells: | ||||
| +	Usage: required | ||||
| +	Value type: <u32> | ||||
| +	Definition: must be 1 | ||||
| + | ||||
| +Example: | ||||
| + | ||||
| +	kraitcc: clock-controller { | ||||
| +		compatible = "qcom,krait-cc-v1"; | ||||
| +		#clock-cells = <1>; | ||||
| +	}; | ||||
| --- a/drivers/clk/qcom/Kconfig | ||||
| +++ b/drivers/clk/qcom/Kconfig | ||||
| @@ -213,6 +213,14 @@ config KPSS_XCC | ||||
| 	  if you want to support CPU frequency scaling on devices such | ||||
| 	  as MSM8960, APQ8064, etc. | ||||
|  | ||||
| +config KRAITCC | ||||
| +	tristate "Krait Clock Controller" | ||||
| +	depends on COMMON_CLK_QCOM && ARM | ||||
| +	select KRAIT_CLOCKS | ||||
| +	help | ||||
| +	  Support for the Krait CPU clocks on Qualcomm devices. | ||||
| +	  Say Y if you want to support CPU frequency scaling. | ||||
| + | ||||
|  config KRAIT_CLOCKS | ||||
| 	bool | ||||
| 	select KRAIT_L2_ACCESSORS | ||||
| --- a/drivers/clk/qcom/Makefile | ||||
| +++ b/drivers/clk/qcom/Makefile | ||||
| @@ -38,3 +38,4 @@ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o | ||||
|  obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o | ||||
|  obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o | ||||
|  obj-$(CONFIG_QCOM_HFPLL) += hfpll.o | ||||
| +obj-$(CONFIG_KRAITCC) += krait-cc.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/clk/qcom/krait-cc.c | ||||
| @@ -0,0 +1,350 @@ | ||||
| +/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/err.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/of_device.h> | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/clk-provider.h> | ||||
| +#include <linux/slab.h> | ||||
| + | ||||
| +#include "clk-krait.h" | ||||
| + | ||||
| +static unsigned int sec_mux_map[] = { | ||||
| +	2, | ||||
| +	0, | ||||
| +}; | ||||
| + | ||||
| +static unsigned int pri_mux_map[] = { | ||||
| +	1, | ||||
| +	2, | ||||
| +	0, | ||||
| +}; | ||||
| + | ||||
| +static int | ||||
| +krait_add_div(struct device *dev, int id, const char *s, unsigned int offset) | ||||
| +{ | ||||
| +	struct krait_div2_clk *div; | ||||
| +	struct clk_init_data init = { | ||||
| +		.num_parents = 1, | ||||
| +		.ops = &krait_div2_clk_ops, | ||||
| +		.flags = CLK_SET_RATE_PARENT, | ||||
| +	}; | ||||
| +	const char *p_names[1]; | ||||
| +	struct clk *clk; | ||||
| + | ||||
| +	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); | ||||
| +	if (!div) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	div->width = 2; | ||||
| +	div->shift = 6; | ||||
| +	div->lpl = id >= 0; | ||||
| +	div->offset = offset; | ||||
| +	div->hw.init = &init; | ||||
| + | ||||
| +	init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); | ||||
| +	if (!init.name) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	init.parent_names = p_names; | ||||
| +	p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); | ||||
| +	if (!p_names[0]) { | ||||
| +		kfree(init.name); | ||||
| +		return -ENOMEM; | ||||
| +	} | ||||
| + | ||||
| +	clk = devm_clk_register(dev, &div->hw); | ||||
| +	kfree(p_names[0]); | ||||
| +	kfree(init.name); | ||||
| + | ||||
| +	return PTR_ERR_OR_ZERO(clk); | ||||
| +} | ||||
| + | ||||
| +static int | ||||
| +krait_add_sec_mux(struct device *dev, int id, const char *s, | ||||
| +		  unsigned int offset, bool unique_aux) | ||||
| +{ | ||||
| +	struct krait_mux_clk *mux; | ||||
| +	static const char *sec_mux_list[] = { | ||||
| +		"acpu_aux", | ||||
| +		"qsb", | ||||
| +	}; | ||||
| +	struct clk_init_data init = { | ||||
| +		.parent_names = sec_mux_list, | ||||
| +		.num_parents = ARRAY_SIZE(sec_mux_list), | ||||
| +		.ops = &krait_mux_clk_ops, | ||||
| +		.flags = CLK_SET_RATE_PARENT, | ||||
| +	}; | ||||
| +	struct clk *clk; | ||||
| + | ||||
| +	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); | ||||
| +	if (!mux) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	mux->offset = offset; | ||||
| +	mux->lpl = id >= 0; | ||||
| +	mux->mask = 0x3; | ||||
| +	mux->shift = 2; | ||||
| +	mux->parent_map = sec_mux_map; | ||||
| +	mux->hw.init = &init; | ||||
| + | ||||
| +	init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); | ||||
| +	if (!init.name) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	if (unique_aux) { | ||||
| +		sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); | ||||
| +		if (!sec_mux_list[0]) { | ||||
| +			clk = ERR_PTR(-ENOMEM); | ||||
| +			goto err_aux; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	clk = devm_clk_register(dev, &mux->hw); | ||||
| + | ||||
| +	if (unique_aux) | ||||
| +		kfree(sec_mux_list[0]); | ||||
| +err_aux: | ||||
| +	kfree(init.name); | ||||
| +	return PTR_ERR_OR_ZERO(clk); | ||||
| +} | ||||
| + | ||||
| +static struct clk * | ||||
| +krait_add_pri_mux(struct device *dev, int id, const char *s, | ||||
| +		  unsigned int offset) | ||||
| +{ | ||||
| +	struct krait_mux_clk *mux; | ||||
| +	const char *p_names[3]; | ||||
| +	struct clk_init_data init = { | ||||
| +		.parent_names = p_names, | ||||
| +		.num_parents = ARRAY_SIZE(p_names), | ||||
| +		.ops = &krait_mux_clk_ops, | ||||
| +		.flags = CLK_SET_RATE_PARENT, | ||||
| +	}; | ||||
| +	struct clk *clk; | ||||
| + | ||||
| +	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); | ||||
| +	if (!mux) | ||||
| +		return ERR_PTR(-ENOMEM); | ||||
| + | ||||
| +	mux->mask = 0x3; | ||||
| +	mux->shift = 0; | ||||
| +	mux->offset = offset; | ||||
| +	mux->lpl = id >= 0; | ||||
| +	mux->parent_map = pri_mux_map; | ||||
| +	mux->hw.init = &init; | ||||
| + | ||||
| +	init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); | ||||
| +	if (!init.name) | ||||
| +		return ERR_PTR(-ENOMEM); | ||||
| + | ||||
| +	p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); | ||||
| +	if (!p_names[0]) { | ||||
| +		clk = ERR_PTR(-ENOMEM); | ||||
| +		goto err_p0; | ||||
| +	} | ||||
| + | ||||
| +	p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); | ||||
| +	if (!p_names[1]) { | ||||
| +		clk = ERR_PTR(-ENOMEM); | ||||
| +		goto err_p1; | ||||
| +	} | ||||
| + | ||||
| +	p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); | ||||
| +	if (!p_names[2]) { | ||||
| +		clk = ERR_PTR(-ENOMEM); | ||||
| +		goto err_p2; | ||||
| +	} | ||||
| + | ||||
| +	clk = devm_clk_register(dev, &mux->hw); | ||||
| + | ||||
| +	kfree(p_names[2]); | ||||
| +err_p2: | ||||
| +	kfree(p_names[1]); | ||||
| +err_p1: | ||||
| +	kfree(p_names[0]); | ||||
| +err_p0: | ||||
| +	kfree(init.name); | ||||
| +	return clk; | ||||
| +} | ||||
| + | ||||
| +/* id < 0 for L2, otherwise id == physical CPU number */ | ||||
| +static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	unsigned int offset; | ||||
| +	void *p = NULL; | ||||
| +	const char *s; | ||||
| +	struct clk *clk; | ||||
| + | ||||
| +	if (id >= 0) { | ||||
| +		offset = 0x4501 + (0x1000 * id); | ||||
| +		s = p = kasprintf(GFP_KERNEL, "%d", id); | ||||
| +		if (!s) | ||||
| +			return ERR_PTR(-ENOMEM); | ||||
| +	} else { | ||||
| +		offset = 0x500; | ||||
| +		s = "_l2"; | ||||
| +	} | ||||
| + | ||||
| +	ret = krait_add_div(dev, id, s, offset); | ||||
| +	if (ret) { | ||||
| +		clk = ERR_PTR(ret); | ||||
| +		goto err; | ||||
| +	} | ||||
| + | ||||
| +	ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); | ||||
| +	if (ret) { | ||||
| +		clk = ERR_PTR(ret); | ||||
| +		goto err; | ||||
| +	} | ||||
| + | ||||
| +	clk = krait_add_pri_mux(dev, id, s, offset); | ||||
| +err: | ||||
| +	kfree(p); | ||||
| +	return clk; | ||||
| +} | ||||
| + | ||||
| +static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) | ||||
| +{ | ||||
| +	unsigned int idx = clkspec->args[0]; | ||||
| +	struct clk **clks = data; | ||||
| + | ||||
| +	if (idx >= 5) { | ||||
| +		pr_err("%s: invalid clock index %d\n", __func__, idx); | ||||
| +		return ERR_PTR(-EINVAL); | ||||
| +	} | ||||
| + | ||||
| +	return clks[idx] ? : ERR_PTR(-ENODEV); | ||||
| +} | ||||
| + | ||||
| +static const struct of_device_id krait_cc_match_table[] = { | ||||
| +	{ .compatible = "qcom,krait-cc-v1", (void *)1UL }, | ||||
| +	{ .compatible = "qcom,krait-cc-v2" }, | ||||
| +	{} | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, krait_cc_match_table); | ||||
| + | ||||
| +static int krait_cc_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct device *dev = &pdev->dev; | ||||
| +	const struct of_device_id *id; | ||||
| +	unsigned long cur_rate, aux_rate; | ||||
| +	int cpu; | ||||
| +	struct clk *clk; | ||||
| +	struct clk **clks; | ||||
| +	struct clk *l2_pri_mux_clk; | ||||
| + | ||||
| +	id = of_match_device(krait_cc_match_table, dev); | ||||
| +	if (!id) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	/* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ | ||||
| +	clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1); | ||||
| +	if (IS_ERR(clk)) | ||||
| +		return PTR_ERR(clk); | ||||
| + | ||||
| +	if (!id->data) { | ||||
| +		clk = clk_register_fixed_factor(dev, "acpu_aux", | ||||
| +						"gpll0_vote", 0, 1, 2); | ||||
| +		if (IS_ERR(clk)) | ||||
| +			return PTR_ERR(clk); | ||||
| +	} | ||||
| + | ||||
| +	/* Krait configurations have at most 4 CPUs and one L2 */ | ||||
| +	clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); | ||||
| +	if (!clks) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	for_each_possible_cpu(cpu) { | ||||
| +		clk = krait_add_clks(dev, cpu, id->data); | ||||
| +		if (IS_ERR(clk)) | ||||
| +			return PTR_ERR(clk); | ||||
| +		clks[cpu] = clk; | ||||
| +	} | ||||
| + | ||||
| +	l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); | ||||
| +	if (IS_ERR(l2_pri_mux_clk)) | ||||
| +		return PTR_ERR(l2_pri_mux_clk); | ||||
| +	clks[4] = l2_pri_mux_clk; | ||||
| + | ||||
| +	/* | ||||
| +	 * We don't want the CPU or L2 clocks to be turned off at late init | ||||
| +	 * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the | ||||
| +	 * refcount of these clocks. Any cpufreq/hotplug manager can assume | ||||
| +	 * that the clocks have already been prepared and enabled by the time | ||||
| +	 * they take over. | ||||
| +	 */ | ||||
| +	for_each_online_cpu(cpu) { | ||||
| +		clk_prepare_enable(l2_pri_mux_clk); | ||||
| +		WARN(clk_prepare_enable(clks[cpu]), | ||||
| +		     "Unable to turn on CPU%d clock", cpu); | ||||
| +	} | ||||
| + | ||||
| +	/* | ||||
| +	 * Force reinit of HFPLLs and muxes to overwrite any potential | ||||
| +	 * incorrect configuration of HFPLLs and muxes by the bootloader. | ||||
| +	 * While at it, also make sure the cores are running at known rates | ||||
| +	 * and print the current rate. | ||||
| +	 * | ||||
| +	 * The clocks are set to aux clock rate first to make sure the | ||||
| +	 * secondary mux is not sourcing off of QSB. The rate is then set to | ||||
| +	 * two different rates to force a HFPLL reinit under all | ||||
| +	 * circumstances. | ||||
| +	 */ | ||||
| +	cur_rate = clk_get_rate(l2_pri_mux_clk); | ||||
| +	aux_rate = 384000000; | ||||
| +	if (cur_rate == 1) { | ||||
| +		pr_info("L2 @ QSB rate. Forcing new rate.\n"); | ||||
| +		cur_rate = aux_rate; | ||||
| +	} | ||||
| +	clk_set_rate(l2_pri_mux_clk, aux_rate); | ||||
| +	clk_set_rate(l2_pri_mux_clk, 2); | ||||
| +	clk_set_rate(l2_pri_mux_clk, cur_rate); | ||||
| +	pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); | ||||
| +	for_each_possible_cpu(cpu) { | ||||
| +		clk = clks[cpu]; | ||||
| +		cur_rate = clk_get_rate(clk); | ||||
| +		if (cur_rate == 1) { | ||||
| +			pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu); | ||||
| +			cur_rate = aux_rate; | ||||
| +		} | ||||
| + | ||||
| +		clk_set_rate(clk, aux_rate); | ||||
| +		clk_set_rate(clk, 2); | ||||
| +		clk_set_rate(clk, cur_rate); | ||||
| +		pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000); | ||||
| +	} | ||||
| + | ||||
| +	of_clk_add_provider(dev->of_node, krait_of_get, clks); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static struct platform_driver krait_cc_driver = { | ||||
| +	.probe = krait_cc_probe, | ||||
| +	.driver = { | ||||
| +		.name = "krait-cc", | ||||
| +		.of_match_table = krait_cc_match_table, | ||||
| +	}, | ||||
| +}; | ||||
| +module_platform_driver(krait_cc_driver); | ||||
| + | ||||
| +MODULE_DESCRIPTION("Krait CPU Clock Driver"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| +MODULE_ALIAS("platform:krait-cc"); | ||||
| @@ -0,0 +1,160 @@ | ||||
| From patchwork Fri Dec  8 09:42:28 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,10/12] clk: qcom: Add safe switch hook for krait mux clocks | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102057 | ||||
| Message-Id: <1512726150-7204-11-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:28 +0530 | ||||
|  | ||||
| When the Hfplls are reprogrammed during the rate change, | ||||
| the primary muxes which are sourced from the same hfpll | ||||
| for higher frequencies, needs to be switched to the 'safe | ||||
| secondary mux' as the parent for that small window. This | ||||
| is done by registering a clk notifier for the muxes and | ||||
| switching to the safe parent in the PRE_RATE_CHANGE notifier | ||||
| and back to the original parent in the POST_RATE_CHANGE notifier. | ||||
|  | ||||
| Signed-off-by: Sricharan R <sricharan@codeaurora.org> | ||||
| --- | ||||
|  drivers/clk/qcom/clk-krait.c |  2 ++ | ||||
|  drivers/clk/qcom/clk-krait.h |  3 +++ | ||||
|  drivers/clk/qcom/krait-cc.c  | 56 ++++++++++++++++++++++++++++++++++++++++++++ | ||||
|  3 files changed, 61 insertions(+) | ||||
|  | ||||
| --- a/drivers/clk/qcom/clk-krait.c | ||||
| +++ b/drivers/clk/qcom/clk-krait.c | ||||
| @@ -60,6 +60,8 @@ static int krait_mux_set_parent(struct c | ||||
| 	if (__clk_is_enabled(hw->clk)) | ||||
| 		__krait_mux_set_sel(mux, sel); | ||||
|  | ||||
| +	mux->reparent = true; | ||||
| + | ||||
| 	return 0; | ||||
|  } | ||||
|  | ||||
| --- a/drivers/clk/qcom/clk-krait.h | ||||
| +++ b/drivers/clk/qcom/clk-krait.h | ||||
| @@ -23,6 +23,9 @@ struct krait_mux_clk { | ||||
| 	u32		shift; | ||||
| 	u32		en_mask; | ||||
| 	bool		lpl; | ||||
| +	u8		safe_sel; | ||||
| +	u8		old_index; | ||||
| +	bool		reparent; | ||||
|  | ||||
| 	struct clk_hw	hw; | ||||
| 	struct notifier_block   clk_nb; | ||||
| --- a/drivers/clk/qcom/krait-cc.c | ||||
| +++ b/drivers/clk/qcom/krait-cc.c | ||||
| @@ -35,6 +35,49 @@ static unsigned int pri_mux_map[] = { | ||||
| 	0, | ||||
|  }; | ||||
|  | ||||
| +/* | ||||
| + * Notifier function for switching the muxes to safe parent | ||||
| + * while the hfpll is getting reprogrammed. | ||||
| + */ | ||||
| +static int krait_notifier_cb(struct notifier_block *nb, | ||||
| +			     unsigned long event, | ||||
| +			     void *data) | ||||
| +{ | ||||
| +	int ret = 0; | ||||
| +	struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk, | ||||
| +						 clk_nb); | ||||
| +	/* Switch to safe parent */ | ||||
| +	if (event == PRE_RATE_CHANGE) { | ||||
| +		mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw); | ||||
| +		ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel); | ||||
| +		mux->reparent = false; | ||||
| +	/* | ||||
| +	 * By the time POST_RATE_CHANGE notifier is called, | ||||
| +	 * clk framework itself would have changed the parent for the new rate. | ||||
| +	 * Only otherwise, put back to the old parent. | ||||
| +	 */ | ||||
| +	} else if (event == POST_RATE_CHANGE) { | ||||
| +		if (!mux->reparent) | ||||
| +			ret = krait_mux_clk_ops.set_parent(&mux->hw, | ||||
| +							   mux->old_index); | ||||
| +	} | ||||
| + | ||||
| +	return notifier_from_errno(ret); | ||||
| +} | ||||
| + | ||||
| +static int krait_notifier_register(struct device *dev, struct clk *clk, | ||||
| +				   struct krait_mux_clk *mux) | ||||
| +{ | ||||
| +	int ret = 0; | ||||
| + | ||||
| +	mux->clk_nb.notifier_call = krait_notifier_cb; | ||||
| +	ret = clk_notifier_register(clk, &mux->clk_nb); | ||||
| +	if (ret) | ||||
| +		dev_err(dev, "failed to register clock notifier: %d\n", ret); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  static int | ||||
|  krait_add_div(struct device *dev, int id, const char *s, unsigned int offset) | ||||
|  { | ||||
| @@ -79,6 +122,7 @@ static int | ||||
|  krait_add_sec_mux(struct device *dev, int id, const char *s, | ||||
| 		  unsigned int offset, bool unique_aux) | ||||
|  { | ||||
| +	int ret; | ||||
| 	struct krait_mux_clk *mux; | ||||
| 	static const char *sec_mux_list[] = { | ||||
| 		"acpu_aux", | ||||
| @@ -102,6 +146,7 @@ krait_add_sec_mux(struct device *dev, in | ||||
| 	mux->shift = 2; | ||||
| 	mux->parent_map = sec_mux_map; | ||||
| 	mux->hw.init = &init; | ||||
| +	mux->safe_sel = 0; | ||||
|  | ||||
| 	init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); | ||||
| 	if (!init.name) | ||||
| @@ -117,6 +162,11 @@ krait_add_sec_mux(struct device *dev, in | ||||
|  | ||||
| 	clk = devm_clk_register(dev, &mux->hw); | ||||
|  | ||||
| +	ret = krait_notifier_register(dev, clk, mux); | ||||
| +	if (ret) | ||||
| +		goto unique_aux; | ||||
| + | ||||
| +unique_aux: | ||||
| 	if (unique_aux) | ||||
| 		kfree(sec_mux_list[0]); | ||||
|  err_aux: | ||||
| @@ -128,6 +178,7 @@ static struct clk * | ||||
|  krait_add_pri_mux(struct device *dev, int id, const char *s, | ||||
| 		  unsigned int offset) | ||||
|  { | ||||
| +	int ret; | ||||
| 	struct krait_mux_clk *mux; | ||||
| 	const char *p_names[3]; | ||||
| 	struct clk_init_data init = { | ||||
| @@ -148,6 +199,7 @@ krait_add_pri_mux(struct device *dev, in | ||||
| 	mux->lpl = id >= 0; | ||||
| 	mux->parent_map = pri_mux_map; | ||||
| 	mux->hw.init = &init; | ||||
| +	mux->safe_sel = 2; | ||||
|  | ||||
| 	init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); | ||||
| 	if (!init.name) | ||||
| @@ -173,6 +225,10 @@ krait_add_pri_mux(struct device *dev, in | ||||
|  | ||||
| 	clk = devm_clk_register(dev, &mux->hw); | ||||
|  | ||||
| +	ret = krait_notifier_register(dev, clk, mux); | ||||
| +	if (ret) | ||||
| +		goto err_p3; | ||||
| +err_p3: | ||||
| 	kfree(p_names[2]); | ||||
|  err_p2: | ||||
| 	kfree(p_names[1]); | ||||
| @@ -0,0 +1,307 @@ | ||||
| From patchwork Fri Dec  8 09:42:29 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,11/12] cpufreq: Add module to register cpufreq on Krait CPUs | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102075 | ||||
| Message-Id: <1512726150-7204-12-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:29 +0530 | ||||
|  | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
|  | ||||
| Register a cpufreq-generic device whenever we detect that a | ||||
| "qcom,krait" compatible CPU is present in DT. | ||||
|  | ||||
| Cc: <devicetree@vger.kernel.org> | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| --- | ||||
|  .../devicetree/bindings/arm/msm/qcom,pvs.txt       |  38 ++++ | ||||
|  drivers/cpufreq/Kconfig.arm                        |   9 + | ||||
|  drivers/cpufreq/Makefile                           |   1 + | ||||
|  drivers/cpufreq/qcom-cpufreq.c                     | 204 +++++++++++++++++++++ | ||||
|  4 files changed, 252 insertions(+) | ||||
|  create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt | ||||
|  create mode 100644 drivers/cpufreq/qcom-cpufreq.c | ||||
|  | ||||
| --- /dev/null | ||||
| +++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt | ||||
| @@ -0,0 +1,38 @@ | ||||
| +Qualcomm Process Voltage Scaling Tables | ||||
| + | ||||
| +The node name is required to be "qcom,pvs". There shall only be one | ||||
| +such node present in the root of the tree. | ||||
| + | ||||
| +PROPERTIES | ||||
| + | ||||
| +- qcom,pvs-format-a or qcom,pvs-format-b: | ||||
| +	Usage: required | ||||
| +	Value type: <empty> | ||||
| +	Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties. | ||||
| +		    If qcom,pvs-format-a is used the table is two columns | ||||
| +		    (frequency and voltage in that order). If qcom,pvs-format-b 		    is used the table is three columns (frequency, voltage, | ||||
| +		    and current in that order). | ||||
| + | ||||
| +- qcom,speedX-pvsY-bin-vZ: | ||||
| +	Usage: required | ||||
| +	Value type: <prop-encoded-array> | ||||
| +	Definition: The PVS table corresponding to the speed bin X, pvs bin Y, | ||||
| +		    and version Z. | ||||
| +Example: | ||||
| + | ||||
| +	qcom,pvs { | ||||
| +		qcom,pvs-format-a; | ||||
| +		qcom,speed0-pvs0-bin-v0 = | ||||
| +			<  384000000  950000 >, | ||||
| +			<  486000000  975000 >, | ||||
| +			<  594000000 1000000 >, | ||||
| +			<  702000000 1025000 >, | ||||
| +			<  810000000 1075000 >, | ||||
| +			<  918000000 1100000 >, | ||||
| +			< 1026000000 1125000 >, | ||||
| +			< 1134000000 1175000 >, | ||||
| +			< 1242000000 1200000 >, | ||||
| +			< 1350000000 1225000 >, | ||||
| +			< 1458000000 1237500 >, | ||||
| +			< 1512000000 1250000 >; | ||||
| +	}; | ||||
| --- a/drivers/cpufreq/Kconfig.arm | ||||
| +++ b/drivers/cpufreq/Kconfig.arm | ||||
| @@ -100,6 +100,15 @@ config ARM_OMAP2PLUS_CPUFREQ | ||||
| 	depends on ARCH_OMAP2PLUS | ||||
| 	default ARCH_OMAP2PLUS | ||||
|  | ||||
| +config ARM_QCOM_CPUFREQ | ||||
| +	tristate "Qualcomm based" | ||||
| +	depends on ARCH_QCOM | ||||
| +	select PM_OPP | ||||
| +	help | ||||
| +	  This adds the CPUFreq driver for Qualcomm SoC based boards. | ||||
| + | ||||
| +	  If in doubt, say N. | ||||
| + | ||||
|  config ARM_S3C_CPUFREQ | ||||
| 	bool | ||||
| 	help | ||||
| --- a/drivers/cpufreq/Makefile | ||||
| +++ b/drivers/cpufreq/Makefile | ||||
| @@ -62,6 +62,7 @@ obj-$(CONFIG_ARM_MT8173_CPUFREQ)	+= mt81 | ||||
|  obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)	+= omap-cpufreq.o | ||||
|  obj-$(CONFIG_ARM_PXA2xx_CPUFREQ)	+= pxa2xx-cpufreq.o | ||||
|  obj-$(CONFIG_PXA3xx)			+= pxa3xx-cpufreq.o | ||||
| +obj-$(CONFIG_ARM_QCOM_CPUFREQ)		+= qcom-cpufreq.o | ||||
|  obj-$(CONFIG_ARM_S3C24XX_CPUFREQ)	+= s3c24xx-cpufreq.o | ||||
|  obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o | ||||
|  obj-$(CONFIG_ARM_S3C2410_CPUFREQ)	+= s3c2410-cpufreq.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/cpufreq/qcom-cpufreq.c | ||||
| @@ -0,0 +1,204 @@ | ||||
| +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/cpu.h> | ||||
| +#include <linux/err.h> | ||||
| +#include <linux/init.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/pm_opp.h> | ||||
| +#include <linux/slab.h> | ||||
| +#include "cpufreq-dt.h" | ||||
| + | ||||
| +static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver) | ||||
| +{ | ||||
| +	void __iomem *base; | ||||
| +	u32 pte_efuse; | ||||
| + | ||||
| +	*speed = *pvs = *pvs_ver = 0; | ||||
| + | ||||
| +	base = ioremap(0x007000c0, 4); | ||||
| +	if (!base) { | ||||
| +		pr_warn("Unable to read efuse data. Defaulting to 0!\n"); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
| +	pte_efuse = readl_relaxed(base); | ||||
| +	iounmap(base); | ||||
| + | ||||
| +	*speed = pte_efuse & 0xf; | ||||
| +	if (*speed == 0xf) | ||||
| +		*speed = (pte_efuse >> 4) & 0xf; | ||||
| + | ||||
| +	if (*speed == 0xf) { | ||||
| +		*speed = 0; | ||||
| +		pr_warn("Speed bin: Defaulting to %d\n", *speed); | ||||
| +	} else { | ||||
| +		pr_info("Speed bin: %d\n", *speed); | ||||
| +	} | ||||
| + | ||||
| +	*pvs = (pte_efuse >> 10) & 0x7; | ||||
| +	if (*pvs == 0x7) | ||||
| +		*pvs = (pte_efuse >> 13) & 0x7; | ||||
| + | ||||
| +	if (*pvs == 0x7) { | ||||
| +		*pvs = 0; | ||||
| +		pr_warn("PVS bin: Defaulting to %d\n", *pvs); | ||||
| +	} else { | ||||
| +		pr_info("PVS bin: %d\n", *pvs); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver) | ||||
| +{ | ||||
| +	u32 pte_efuse, redundant_sel; | ||||
| +	void __iomem *base; | ||||
| + | ||||
| +	*speed = 0; | ||||
| +	*pvs = 0; | ||||
| +	*pvs_ver = 0; | ||||
| + | ||||
| +	base = ioremap(0xfc4b80b0, 8); | ||||
| +	if (!base) { | ||||
| +		pr_warn("Unable to read efuse data. Defaulting to 0!\n"); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
| +	pte_efuse = readl_relaxed(base); | ||||
| +	redundant_sel = (pte_efuse >> 24) & 0x7; | ||||
| +	*speed = pte_efuse & 0x7; | ||||
| +	/* 4 bits of PVS are in efuse register bits 31, 8-6. */ | ||||
| +	*pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); | ||||
| +	*pvs_ver = (pte_efuse >> 4) & 0x3; | ||||
| + | ||||
| +	switch (redundant_sel) { | ||||
| +	case 1: | ||||
| +		*speed = (pte_efuse >> 27) & 0xf; | ||||
| +		break; | ||||
| +	case 2: | ||||
| +		*pvs = (pte_efuse >> 27) & 0xf; | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	/* Check SPEED_BIN_BLOW_STATUS */ | ||||
| +	if (pte_efuse & BIT(3)) { | ||||
| +		pr_info("Speed bin: %d\n", *speed); | ||||
| +	} else { | ||||
| +		pr_warn("Speed bin not set. Defaulting to 0!\n"); | ||||
| +		*speed = 0; | ||||
| +	} | ||||
| + | ||||
| +	/* Check PVS_BLOW_STATUS */ | ||||
| +	pte_efuse = readl_relaxed(base + 0x4) & BIT(21); | ||||
| +	if (pte_efuse) { | ||||
| +		pr_info("PVS bin: %d\n", *pvs); | ||||
| +	} else { | ||||
| +		pr_warn("PVS bin not set. Defaulting to 0!\n"); | ||||
| +		*pvs = 0; | ||||
| +	} | ||||
| + | ||||
| +	pr_info("PVS version: %d\n", *pvs_ver); | ||||
| +	iounmap(base); | ||||
| +} | ||||
| + | ||||
| +static int __init qcom_cpufreq_populate_opps(void) | ||||
| +{ | ||||
| +	int len, rows, cols, i, k, speed, pvs, pvs_ver; | ||||
| +	char table_name[] = "qcom,speedXX-pvsXX-bin-vXX"; | ||||
| +	struct device_node *np; | ||||
| +	struct device *dev; | ||||
| +	int cpu = 0; | ||||
| + | ||||
| +	np = of_find_node_by_name(NULL, "qcom,pvs"); | ||||
| +	if (!np) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	if (of_property_read_bool(np, "qcom,pvs-format-a")) { | ||||
| +		get_krait_bin_format_a(&speed, &pvs, &pvs_ver); | ||||
| +		cols = 2; | ||||
| +	} else if (of_property_read_bool(np, "qcom,pvs-format-b")) { | ||||
| +		get_krait_bin_format_b(&speed, &pvs, &pvs_ver); | ||||
| +		cols = 3; | ||||
| +	} else { | ||||
| +		return -ENODEV; | ||||
| +	} | ||||
| + | ||||
| +	snprintf(table_name, sizeof(table_name), | ||||
| +		 "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver); | ||||
| + | ||||
| +	if (!of_find_property(np, table_name, &len)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	len /= sizeof(u32); | ||||
| +	if (len % cols || len == 0) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	rows = len / cols; | ||||
| + | ||||
| +	for (i = 0, k = 0; i < rows; i++) { | ||||
| +		u32 freq, volt; | ||||
| + | ||||
| +		of_property_read_u32_index(np, table_name, k++, &freq); | ||||
| +		of_property_read_u32_index(np, table_name, k++, &volt); | ||||
| +		while (k % cols) | ||||
| +			k++; /* Skip uA entries if present */ | ||||
| +		for (cpu = 0; cpu < num_possible_cpus(); cpu++) { | ||||
| +			dev = get_cpu_device(cpu); | ||||
| +			if (!dev) | ||||
| +				return -ENODEV; | ||||
| +			if (dev_pm_opp_add(dev, freq, volt)) | ||||
| +				pr_warn("failed to add OPP %u\n", freq); | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int __init qcom_cpufreq_driver_init(void) | ||||
| +{ | ||||
| +	struct cpufreq_dt_platform_data pdata = { .independent_clocks = true }; | ||||
| +	struct platform_device_info devinfo = { | ||||
| +		.name = "cpufreq-dt", | ||||
| +		.data = &pdata, | ||||
| +		.size_data = sizeof(pdata), | ||||
| +	}; | ||||
| +	struct device *cpu_dev; | ||||
| +	struct device_node *np; | ||||
| +	int ret; | ||||
| + | ||||
| +	cpu_dev = get_cpu_device(0); | ||||
| +	if (!cpu_dev) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	np = of_node_get(cpu_dev->of_node); | ||||
| +	if (!np) | ||||
| +		return -ENOENT; | ||||
| + | ||||
| +	if (!of_device_is_compatible(np, "qcom,krait")) { | ||||
| +		of_node_put(np); | ||||
| +		return -ENODEV; | ||||
| +	} | ||||
| +	of_node_put(np); | ||||
| + | ||||
| +	ret = qcom_cpufreq_populate_opps(); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo)); | ||||
| +} | ||||
| +module_init(qcom_cpufreq_driver_init); | ||||
| + | ||||
| +MODULE_DESCRIPTION("Qualcomm CPUfreq driver"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| @@ -0,0 +1,65 @@ | ||||
| From patchwork Fri Dec  8 09:42:30 2017 | ||||
| Content-Type: text/plain; charset="utf-8" | ||||
| MIME-Version: 1.0 | ||||
| Content-Transfer-Encoding: 7bit | ||||
| Subject: [v4,12/12] cpufreq: dt: Reintroduce independent_clocks platform data | ||||
| From: Sricharan R <sricharan@codeaurora.org> | ||||
| X-Patchwork-Id: 10102073 | ||||
| Message-Id: <1512726150-7204-13-git-send-email-sricharan@codeaurora.org> | ||||
| To: mturquette@baylibre.com, sboyd@codeaurora.org, | ||||
|  devicetree@vger.kernel.org, linux-pm@vger.kernel.org, | ||||
|  linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, | ||||
|  viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org | ||||
| Cc: sricharan@codeaurora.org | ||||
| Date: Fri,  8 Dec 2017 15:12:30 +0530 | ||||
|  | ||||
| The Platform data was removed earlier by, | ||||
| 'commit eb96924acddc ("cpufreq: dt: Kill platform-data")' | ||||
| since there were no users at that time. | ||||
| Now this is required when the each of the cpu clocks | ||||
| can be scaled independently, which is the case | ||||
| for krait cores. So reintroduce it. | ||||
|  | ||||
| Signed-off-by: Sricharan R <sricharan@codeaurora.org> | ||||
| --- | ||||
|  drivers/cpufreq/cpufreq-dt.c | 7 ++++++- | ||||
|  drivers/cpufreq/cpufreq-dt.h | 6 ++++++ | ||||
|  2 files changed, 12 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/cpufreq/cpufreq-dt.c | ||||
| +++ b/drivers/cpufreq/cpufreq-dt.c | ||||
| @@ -220,7 +220,10 @@ static int cpufreq_init(struct cpufreq_p | ||||
| 	} | ||||
|  | ||||
| 	if (fallback) { | ||||
| -		cpumask_setall(policy->cpus); | ||||
| +		struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data(); | ||||
| + | ||||
| +		if (!pd || !pd->independent_clocks) | ||||
| +			cpumask_setall(policy->cpus); | ||||
|  | ||||
| 		/* | ||||
| 		 * OPP tables are initialized only for policy->cpu, do it for | ||||
| @@ -372,6 +375,8 @@ static int dt_cpufreq_probe(struct platf | ||||
| 	if (data && data->have_governor_per_policy) | ||||
| 		dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY; | ||||
|  | ||||
| +	dt_cpufreq_driver.driver_data = data; | ||||
| + | ||||
| 	ret = cpufreq_register_driver(&dt_cpufreq_driver); | ||||
| 	if (ret) | ||||
| 		dev_err(&pdev->dev, "failed register driver: %d\n", ret); | ||||
| --- a/drivers/cpufreq/cpufreq-dt.h | ||||
| +++ b/drivers/cpufreq/cpufreq-dt.h | ||||
| @@ -13,6 +13,12 @@ | ||||
|  #include <linux/types.h> | ||||
|  | ||||
|  struct cpufreq_dt_platform_data { | ||||
| +	/* | ||||
| +	 * True when each CPU has its own clock to control its | ||||
| +	 * frequency, false when all CPUs are controlled by a single | ||||
| +	 * clock. | ||||
| +	 */ | ||||
| +	bool independent_clocks; | ||||
| 	bool have_governor_per_policy; | ||||
|  }; | ||||
| @@ -0,0 +1,72 @@ | ||||
| From c7c6a0f50f9ac3620c611ce06ba1f9fafea0444e Mon Sep 17 00:00:00 2001 | ||||
| From: Archit Taneja <architt@codeaurora.org> | ||||
| Date: Mon, 3 Aug 2015 10:38:14 +0530 | ||||
| Subject: [PATCH 47/69] mtd: nand: Create a BBT flag to access bad block | ||||
|  markers in raw mode | ||||
|  | ||||
| Some controllers can access the factory bad block marker from OOB only | ||||
| when they read it in raw mode. When ECC is enabled, these controllers | ||||
| discard reading/writing bad block markers, preventing access to them | ||||
| altogether. | ||||
|  | ||||
| The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks. | ||||
| This results in the nand driver's ecc->read_oob() op to be called, which | ||||
| works with ECC enabled. | ||||
|  | ||||
| Create a new BBT option flag that tells nand_bbt to force the mode to | ||||
| MTD_OPS_RAW. This would result in the correct op being called for the | ||||
| underlying nand controller driver. | ||||
|  | ||||
| Reviewed-by: Andy Gross <agross@codeaurora.org> | ||||
| Signed-off-by: Archit Taneja <architt@codeaurora.org> | ||||
| --- | ||||
|  drivers/mtd/nand/nand_base.c | 6 +++++- | ||||
|  drivers/mtd/nand/nand_bbt.c  | 6 +++++- | ||||
|  include/linux/mtd/bbm.h      | 6 ++++++ | ||||
|  3 files changed, 16 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/mtd/nand/nand_base.c | ||||
| +++ b/drivers/mtd/nand/nand_base.c | ||||
| @@ -481,7 +481,11 @@ static int nand_default_block_markbad(st | ||||
| 	} else { | ||||
| 		ops.len = ops.ooblen = 1; | ||||
| 	} | ||||
| -	ops.mode = MTD_OPS_PLACE_OOB; | ||||
| + | ||||
| +	if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW)) | ||||
| +		ops.mode = MTD_OPS_RAW; | ||||
| +	else | ||||
| +		ops.mode = MTD_OPS_PLACE_OOB; | ||||
|  | ||||
| 	/* Write to first/last page(s) if necessary */ | ||||
| 	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) | ||||
| --- a/drivers/mtd/nand/nand_bbt.c | ||||
| +++ b/drivers/mtd/nand/nand_bbt.c | ||||
| @@ -420,7 +420,11 @@ static int scan_block_fast(struct mtd_in | ||||
| 	ops.oobbuf = buf; | ||||
| 	ops.ooboffs = 0; | ||||
| 	ops.datbuf = NULL; | ||||
| -	ops.mode = MTD_OPS_PLACE_OOB; | ||||
| + | ||||
| +	if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW)) | ||||
| +		ops.mode = MTD_OPS_RAW; | ||||
| +	else | ||||
| +		ops.mode = MTD_OPS_PLACE_OOB; | ||||
|  | ||||
| 	for (j = 0; j < numpages; j++) { | ||||
| 		/* | ||||
| --- a/include/linux/mtd/bbm.h | ||||
| +++ b/include/linux/mtd/bbm.h | ||||
| @@ -116,6 +116,12 @@ struct nand_bbt_descr { | ||||
|  #define NAND_BBT_NO_OOB_BBM	0x00080000 | ||||
|  | ||||
|  /* | ||||
| + * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To | ||||
| + * be used by controllers which can access BBM only when ECC is disabled, i.e, | ||||
| + * when in RAW access mode | ||||
| + */ | ||||
| +#define NAND_BBT_ACCESS_BBM_RAW        0x00100000 | ||||
| +/* | ||||
|   * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr | ||||
|   * was allocated dynamicaly and must be freed in nand_release(). Has no meaning | ||||
|   * in nand_chip.bbt_options. | ||||
| @@ -0,0 +1,26 @@ | ||||
| From 5c294df1715d673f94f3b0a6e1ea3a426ca35e6e Mon Sep 17 00:00:00 2001 | ||||
| From: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| Date: Thu, 28 Apr 2016 16:20:12 +0300 | ||||
| Subject: [PATCH 48/69] PM / OPP: HACK: Allow to set regulator without opp_list | ||||
|  | ||||
| Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| --- | ||||
|  drivers/base/power/opp/core.c | 3 ++- | ||||
|  1 file changed, 2 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/base/power/opp/core.c | ||||
| +++ b/drivers/base/power/opp/core.c | ||||
| @@ -1277,11 +1277,13 @@ | ||||
| 	if (!opp_table) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
|  | ||||
| +#if 0 | ||||
| 	/* This should be called before OPPs are initialized */ | ||||
| 	if (WARN_ON(!list_empty(&opp_table->opp_list))) { | ||||
| 		ret = -EBUSY; | ||||
| 		goto err; | ||||
| 	} | ||||
| +#endif | ||||
|  | ||||
| 	/* Already have regulators set */ | ||||
| 	if (opp_table->regulators) { | ||||
| @@ -0,0 +1,147 @@ | ||||
| From c949f08cf20fe82971fbdb4015daa38210da492e Mon Sep 17 00:00:00 2001 | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
| Date: Fri, 18 Sep 2015 17:52:06 -0700 | ||||
| Subject: [PATCH 49/69] PM / OPP: Support adjusting OPP voltages at runtime | ||||
|  | ||||
| On some SoCs the Adaptive Voltage Scaling (AVS) technique is | ||||
| employed to optimize the operating voltage of a device. At a | ||||
| given frequency, the hardware monitors dynamic factors and either | ||||
| makes a suggestion for how much to adjust a voltage for the | ||||
| current frequency, or it automatically adjusts the voltage | ||||
| without software intervention. Add an API to the OPP library for | ||||
| the former case, so that AVS type devices can update the voltages | ||||
| for an OPP when the hardware determines the voltage should | ||||
| change. The assumption is that drivers like CPUfreq or devfreq | ||||
| will register for the OPP notifiers and adjust the voltage | ||||
| according to suggestions that AVS makes. | ||||
|  | ||||
| Cc: Nishanth Menon <nm@ti.com> | ||||
| Acked-by: Viresh Kumar <viresh.kumar@linaro.org> | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| Acked-by: Viresh Kumar <viresh.kumar@linaro.org> | ||||
| Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| --- | ||||
|  drivers/base/power/opp/core.c | 77 +++++++++++++++++++++++++++++++++++++++++++ | ||||
|  include/linux/pm_opp.h        | 11 +++++++ | ||||
|  2 files changed, 88 insertions(+) | ||||
|  | ||||
| --- a/drivers/base/power/opp/core.c | ||||
| +++ b/drivers/base/power/opp/core.c | ||||
| @@ -1605,6 +1605,83 @@ unlock: | ||||
|  } | ||||
|  | ||||
|  /** | ||||
| + * dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP | ||||
| + * @dev:		device for which we do this operation | ||||
| + * @freq:		OPP frequency to adjust voltage of | ||||
| + * @u_volt:		new OPP voltage | ||||
| + * | ||||
| + * Change the voltage of an OPP with an RCU operation. | ||||
| + * | ||||
| + * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the | ||||
| + * copy operation, returns 0 if no modifcation was done OR modification was | ||||
| + * successful. | ||||
| + * | ||||
| + * Locking: The internal device_opp and opp structures are RCU protected. | ||||
| + * Hence this function internally uses RCU updater strategy with mutex locks to | ||||
| + * keep the integrity of the internal data structures. Callers should ensure | ||||
| + * that this function is *NOT* called under RCU protection or in contexts where | ||||
| + * mutex locking or synchronize_rcu() blocking calls cannot be used. | ||||
| + */ | ||||
| +int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, | ||||
| +			      unsigned long u_volt) | ||||
| +{ | ||||
| +	struct opp_table *opp_table; | ||||
| +	struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); | ||||
| +	int r = 0; | ||||
| + | ||||
| +	/* keep the node allocated */ | ||||
| +	new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL); | ||||
| +	if (!new_opp) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	mutex_lock(&opp_table_lock); | ||||
| + | ||||
| +	/* Find the opp_table */ | ||||
| +	opp_table = _find_opp_table(dev); | ||||
| +	if (IS_ERR(opp_table)) { | ||||
| +		r = PTR_ERR(opp_table); | ||||
| +		dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); | ||||
| +		goto unlock; | ||||
| +	} | ||||
| + | ||||
| +	/* Do we have the frequency? */ | ||||
| +	list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { | ||||
| +		if (tmp_opp->rate == freq) { | ||||
| +			opp = tmp_opp; | ||||
| +			break; | ||||
| +		} | ||||
| +	} | ||||
| +	if (IS_ERR(opp)) { | ||||
| +		r = PTR_ERR(opp); | ||||
| +		goto unlock; | ||||
| +	} | ||||
| + | ||||
| +	/* Is update really needed? */ | ||||
| +	if (opp->supplies[0].u_volt == u_volt) | ||||
| +		goto unlock; | ||||
| +	/* copy the old data over */ | ||||
| +	*new_opp = *opp; | ||||
| + | ||||
| +	/* plug in new node */ | ||||
| +	new_opp->supplies[0].u_volt = u_volt; | ||||
| + | ||||
| +	list_replace(&opp->node, &new_opp->node); | ||||
| +	mutex_unlock(&opp_table_lock); | ||||
| +	kfree(opp); | ||||
| + | ||||
| +	/* Notify the change of the OPP */ | ||||
| +	blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE, | ||||
| +				 new_opp); | ||||
| + | ||||
| +	return 0; | ||||
| + | ||||
| +unlock: | ||||
| +	mutex_unlock(&opp_table_lock); | ||||
| +	kfree(new_opp); | ||||
| +	return r; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
|   * dev_pm_opp_enable() - Enable a specific OPP | ||||
|   * @dev:	device for which we do this operation | ||||
|   * @freq:	OPP frequency to enable | ||||
| --- a/include/linux/pm_opp.h | ||||
| +++ b/include/linux/pm_opp.h | ||||
| @@ -25,6 +25,7 @@ struct opp_table; | ||||
|  | ||||
|  enum dev_pm_opp_event { | ||||
| 	OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, | ||||
| +	OPP_EVENT_ADJUST_VOLTAGE, | ||||
|  }; | ||||
|  | ||||
|  /** | ||||
| @@ -108,6 +109,9 @@ int dev_pm_opp_add(struct device *dev, u | ||||
| 		   unsigned long u_volt); | ||||
|  void dev_pm_opp_remove(struct device *dev, unsigned long freq); | ||||
|  | ||||
| +int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, | ||||
| +			      unsigned long u_volt); | ||||
| + | ||||
|  int dev_pm_opp_enable(struct device *dev, unsigned long freq); | ||||
|  | ||||
|  int dev_pm_opp_disable(struct device *dev, unsigned long freq); | ||||
| @@ -208,6 +212,13 @@ static inline void dev_pm_opp_remove(str | ||||
|  { | ||||
|  } | ||||
|  | ||||
| +static inline int | ||||
| +dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, | ||||
| +			  unsigned long u_volt) | ||||
| +{ | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq) | ||||
|  { | ||||
| 	return 0; | ||||
| @@ -0,0 +1,51 @@ | ||||
| From d06ca5e7a3cf726f5be5ffd96e93ccd798b8c09a Mon Sep 17 00:00:00 2001 | ||||
| From: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| Date: Thu, 12 May 2016 14:41:33 +0300 | ||||
| Subject: [PATCH 51/69] PM / OPP: Add a helper to get an opp regulator for | ||||
|  device | ||||
|  | ||||
| Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| --- | ||||
|  drivers/base/power/opp/core.c | 21 +++++++++++++++++++++ | ||||
|  include/linux/pm_opp.h        |  1 + | ||||
|  2 files changed, 22 insertions(+) | ||||
|  | ||||
| --- a/drivers/base/power/opp/core.c | ||||
| +++ b/drivers/base/power/opp/core.c | ||||
| @@ -126,6 +126,27 @@ unsigned long dev_pm_opp_get_freq(struct | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); | ||||
|  | ||||
| +struct regulator *dev_pm_opp_get_regulator(struct device *dev) | ||||
| +{ | ||||
| +	struct opp_table *opp_table; | ||||
| +	struct regulator *reg; | ||||
| + | ||||
| +	rcu_read_lock(); | ||||
| + | ||||
| +	opp_table = _find_opp_table(dev); | ||||
| +	if (IS_ERR(opp_table)) { | ||||
| +		rcu_read_unlock(); | ||||
| +		return ERR_CAST(opp_table); | ||||
| +	} | ||||
| + | ||||
| +	reg = opp_table->regulators[0]; | ||||
| + | ||||
| +	rcu_read_unlock(); | ||||
| + | ||||
| +	return reg; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(dev_pm_opp_get_regulator); | ||||
| + | ||||
|  /** | ||||
|   * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not | ||||
|   * @opp: opp for which turbo mode is being verified | ||||
| --- a/include/linux/pm_opp.h | ||||
| +++ b/include/linux/pm_opp.h | ||||
| @@ -85,6 +85,7 @@ enum dev_pm_opp_event { | ||||
|  unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); | ||||
|  | ||||
|  unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); | ||||
| +struct regulator *dev_pm_opp_get_regulator(struct device *dev); | ||||
|  | ||||
|  bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); | ||||
| @@ -0,0 +1,38 @@ | ||||
| From 4533c285c2aedce6d4434d7b877066de3b1ecb33 Mon Sep 17 00:00:00 2001 | ||||
| From: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| Date: Thu, 25 Aug 2016 18:43:35 +0300 | ||||
| Subject: [PATCH 52/69] PM / OPP: Update the voltage tolerance when adjusting | ||||
|  the OPP | ||||
|  | ||||
| When the voltage is adjusted, the voltage tolerance is not updated. | ||||
| This can lead to situations where the voltage min value is greater | ||||
| than the voltage max value. The final result is triggering a BUG() | ||||
| in the regulator core. | ||||
| Fix this by updating the voltage tolerance values too. | ||||
|  | ||||
| Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| --- | ||||
|  drivers/base/power/opp/core.c | 5 +++++ | ||||
|  1 file changed, 5 insertions(+) | ||||
|  | ||||
| --- a/drivers/base/power/opp/core.c | ||||
| +++ b/drivers/base/power/opp/core.c | ||||
| @@ -1649,6 +1649,7 @@ int dev_pm_opp_adjust_voltage(struct dev | ||||
| 	struct opp_table *opp_table; | ||||
| 	struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); | ||||
| 	int r = 0; | ||||
| +	unsigned long tol; | ||||
|  | ||||
| 	/* keep the node allocated */ | ||||
| 	new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL); | ||||
| @@ -1685,6 +1686,10 @@ int dev_pm_opp_adjust_voltage(struct dev | ||||
|  | ||||
| 	/* plug in new node */ | ||||
| 	new_opp->supplies[0].u_volt = u_volt; | ||||
| +	tol = u_volt * opp_table->voltage_tolerance_v1 / 100; | ||||
| +	new_opp->supplies[0].u_volt = u_volt; | ||||
| +	new_opp->supplies[0].u_volt_min = u_volt - tol; | ||||
| +	new_opp->supplies[0].u_volt_max = u_volt + tol; | ||||
|  | ||||
| 	list_replace(&opp->node, &new_opp->node); | ||||
| 	mutex_unlock(&opp_table_lock); | ||||
| @@ -0,0 +1,55 @@ | ||||
| From ef10381ca4d01848ebedb4afb2c78feb8052f103 Mon Sep 17 00:00:00 2001 | ||||
| From: Adrian Panella <ianchi74@outlook.com> | ||||
| Date: Thu, 9 Mar 2017 08:26:54 +0100 | ||||
| Subject: [PATCH 53/69] regulator: add smb208 support | ||||
|  | ||||
| Signed-off-by: Adrian Panella <ianchi74@outlook.com> | ||||
| --- | ||||
|  Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 4 ++++ | ||||
|  drivers/regulator/qcom_rpm-regulator.c             | 9 +++++++++ | ||||
|  2 files changed, 13 insertions(+) | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt | ||||
| +++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt | ||||
| @@ -61,6 +61,7 @@ Regulator nodes are identified by their | ||||
| 		    "qcom,rpm-pm8901-regulators" | ||||
| 		    "qcom,rpm-pm8921-regulators" | ||||
| 		    "qcom,rpm-pm8018-regulators" | ||||
| +		    "qcom,rpm-smb208-regulators" | ||||
|  | ||||
|  - vdd_l0_l1_lvs-supply: | ||||
|  - vdd_l2_l11_l12-supply: | ||||
| @@ -171,6 +172,9 @@ pm8018: | ||||
| 	s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, | ||||
| 	l12, l14, lvs1 | ||||
|  | ||||
| +smb208: | ||||
| +	s1a, s1b, s2a, s2b | ||||
| + | ||||
|  The content of each sub-node is defined by the standard binding for regulators - | ||||
|  see regulator.txt - with additional custom properties described below: | ||||
|  | ||||
| --- a/drivers/regulator/qcom_rpm-regulator.c | ||||
| +++ b/drivers/regulator/qcom_rpm-regulator.c | ||||
| @@ -933,12 +933,21 @@ static const struct rpm_regulator_data r | ||||
| 	{ } | ||||
|  }; | ||||
|  | ||||
| +static const struct rpm_regulator_data rpm_smb208_regulators[] = { | ||||
| +	{ "s1a",  QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" }, | ||||
| +	{ "s1b",  QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" }, | ||||
| +	{ "s2a",  QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" }, | ||||
| +	{ "s2b",  QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" }, | ||||
| +	{ } | ||||
| +}; | ||||
| + | ||||
|  static const struct of_device_id rpm_of_match[] = { | ||||
| 	{ .compatible = "qcom,rpm-pm8018-regulators", | ||||
| 		.data = &rpm_pm8018_regulators }, | ||||
| 	{ .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators }, | ||||
| 	{ .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators }, | ||||
| 	{ .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators }, | ||||
| +	{ .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators }, | ||||
| 	{ } | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, rpm_of_match); | ||||
| @@ -0,0 +1,131 @@ | ||||
| From 10577f74c35bd395951d1b2382c8d821089b5745 Mon Sep 17 00:00:00 2001 | ||||
| From: Stephen Boyd <sboyd@codeaurora.org> | ||||
| Date: Fri, 18 Sep 2015 17:52:08 -0700 | ||||
| Subject: [PATCH 54/69] cpufreq-dt: Handle OPP voltage adjust events | ||||
|  | ||||
| On some SoCs the Adaptive Voltage Scaling (AVS) technique is | ||||
| employed to optimize the operating voltage of a device. At a | ||||
| given frequency, the hardware monitors dynamic factors and either | ||||
| makes a suggestion for how much to adjust a voltage for the | ||||
| current frequency, or it automatically adjusts the voltage | ||||
| without software intervention. | ||||
|  | ||||
| In the former case, an AVS driver will call | ||||
| dev_pm_opp_modify_voltage() and update the voltage for the | ||||
| particular OPP the CPUs are using. Add an OPP notifier to | ||||
| cpufreq-dt so that we can adjust the voltage of the CPU when AVS | ||||
| updates the OPP. | ||||
|  | ||||
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> | ||||
| Acked-by: Viresh Kumar <viresh.kumar@linaro.org> | ||||
| Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| --- | ||||
|  drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++-- | ||||
|  1 file changed, 65 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/drivers/cpufreq/cpufreq-dt.c | ||||
| +++ b/drivers/cpufreq/cpufreq-dt.c | ||||
| @@ -32,6 +32,9 @@ struct private_data { | ||||
| 	struct device *cpu_dev; | ||||
| 	struct thermal_cooling_device *cdev; | ||||
| 	const char *reg_name; | ||||
| +	struct notifier_block opp_nb; | ||||
| +	struct mutex lock; | ||||
| +	unsigned long opp_freq; | ||||
|  }; | ||||
|  | ||||
|  static struct freq_attr *cpufreq_dt_attr[] = { | ||||
| @@ -43,9 +46,16 @@ static struct freq_attr *cpufreq_dt_attr | ||||
|  static int set_target(struct cpufreq_policy *policy, unsigned int index) | ||||
|  { | ||||
| 	struct private_data *priv = policy->driver_data; | ||||
| +	int ret; | ||||
| +	unsigned long target_freq = policy->freq_table[index].frequency * 1000; | ||||
| + | ||||
| +	mutex_lock(&priv->lock); | ||||
| +	ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq); | ||||
| +	if (!ret) | ||||
| +		priv->opp_freq = target_freq; | ||||
| +	mutex_unlock(&priv->lock); | ||||
|  | ||||
| -	return dev_pm_opp_set_rate(priv->cpu_dev, | ||||
| -				   policy->freq_table[index].frequency * 1000); | ||||
| +	return ret; | ||||
|  } | ||||
|  | ||||
|  /* | ||||
| @@ -86,6 +96,39 @@ node_put: | ||||
| 	return name; | ||||
|  } | ||||
|  | ||||
| +static int opp_notifier(struct notifier_block *nb, unsigned long event, | ||||
| +			void *data) | ||||
| +{ | ||||
| +	struct dev_pm_opp *opp = data; | ||||
| +	struct private_data *priv = container_of(nb, struct private_data, | ||||
| +						 opp_nb); | ||||
| +	struct device *cpu_dev = priv->cpu_dev; | ||||
| +	struct regulator *cpu_reg; | ||||
| +	unsigned long volt, freq; | ||||
| +	int ret = 0; | ||||
| + | ||||
| +	if (event == OPP_EVENT_ADJUST_VOLTAGE) { | ||||
| +		cpu_reg = dev_pm_opp_get_regulator(cpu_dev); | ||||
| +		if (IS_ERR(cpu_reg)) { | ||||
| +			ret = PTR_ERR(cpu_reg); | ||||
| +			goto out; | ||||
| +		} | ||||
| +		volt = dev_pm_opp_get_voltage(opp); | ||||
| +		freq = dev_pm_opp_get_freq(opp); | ||||
| + | ||||
| +		mutex_lock(&priv->lock); | ||||
| +		if (freq == priv->opp_freq) { | ||||
| +			ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt); | ||||
| +		} | ||||
| +		mutex_unlock(&priv->lock); | ||||
| +		if (ret) | ||||
| +			dev_err(cpu_dev, "failed to scale voltage: %d\n", ret); | ||||
| +	} | ||||
| + | ||||
| +out: | ||||
| +	return notifier_from_errno(ret); | ||||
| +} | ||||
| + | ||||
|  static int resources_available(void) | ||||
|  { | ||||
| 	struct device *cpu_dev; | ||||
| @@ -152,6 +195,7 @@ static int cpufreq_init(struct cpufreq_p | ||||
| 	bool fallback = false; | ||||
| 	const char *name; | ||||
| 	int ret; | ||||
| +	struct srcu_notifier_head *opp_srcu_head; | ||||
|  | ||||
| 	cpu_dev = get_cpu_device(policy->cpu); | ||||
| 	if (!cpu_dev) { | ||||
| @@ -241,13 +285,16 @@ static int cpufreq_init(struct cpufreq_p | ||||
| 		goto out_free_opp; | ||||
| 	} | ||||
|  | ||||
| +	mutex_init(&priv->lock); | ||||
| +	dev_pm_opp_register_notifier(cpu_dev, &priv->opp_nb); | ||||
| + | ||||
| 	priv->reg_name = name; | ||||
| 	priv->opp_table = opp_table; | ||||
|  | ||||
| 	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); | ||||
| 	if (ret) { | ||||
| 		dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); | ||||
| -		goto out_free_priv; | ||||
| +		goto out_unregister_nb; | ||||
| 	} | ||||
|  | ||||
| 	priv->cpu_dev = cpu_dev; | ||||
| @@ -283,6 +343,8 @@ static int cpufreq_init(struct cpufreq_p | ||||
|  | ||||
|  out_free_cpufreq_table: | ||||
| 	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); | ||||
| +out_unregister_nb: | ||||
| +	dev_pm_opp_unregister_notifier(cpu_dev, &priv->opp_nb); | ||||
|  out_free_priv: | ||||
| 	kfree(priv); | ||||
|  out_free_opp: | ||||
| @@ -0,0 +1,90 @@ | ||||
| From 0759cdff49f1cf361bf503c13f7bcb33da43ab95 Mon Sep 17 00:00:00 2001 | ||||
| From: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| Date: Tue, 8 Sep 2015 11:24:41 +0300 | ||||
| Subject: [PATCH 55/69] cpufreq-dt: Add L2 frequency scaling support | ||||
|  | ||||
| Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| --- | ||||
|  drivers/cpufreq/cpufreq-dt.c | 41 ++++++++++++++++++++++++++++++++++++++++- | ||||
|  include/linux/cpufreq.h      |  2 ++ | ||||
|  2 files changed, 42 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/cpufreq/cpufreq-dt.c | ||||
| +++ b/drivers/cpufreq/cpufreq-dt.c | ||||
| @@ -48,11 +48,41 @@ static int set_target(struct cpufreq_pol | ||||
| 	struct private_data *priv = policy->driver_data; | ||||
| 	int ret; | ||||
| 	unsigned long target_freq = policy->freq_table[index].frequency * 1000; | ||||
| +	struct clk *l2_clk = policy->l2_clk; | ||||
| +	unsigned int l2_freq; | ||||
| +	unsigned long new_l2_freq = 0; | ||||
|  | ||||
| 	mutex_lock(&priv->lock); | ||||
| 	ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq); | ||||
| -	if (!ret) | ||||
| + | ||||
| +	if (!ret) { | ||||
| +		if (!IS_ERR(l2_clk) && policy->l2_rate[0] && policy->l2_rate[1] && | ||||
| +				policy->l2_rate[2]) { | ||||
| +			static unsigned long krait_l2[CONFIG_NR_CPUS] = { }; | ||||
| +			int cpu, ret = 0; | ||||
| + | ||||
| +			if (target_freq >= policy->l2_rate[2]) | ||||
| +				new_l2_freq = policy->l2_rate[2]; | ||||
| +			else if (target_freq >= policy->l2_rate[1]) | ||||
| +				new_l2_freq = policy->l2_rate[1]; | ||||
| +			else | ||||
| +				new_l2_freq = policy->l2_rate[0]; | ||||
| + | ||||
| +			krait_l2[policy->cpu] = new_l2_freq; | ||||
| +			for_each_present_cpu(cpu) | ||||
| +				new_l2_freq = max(new_l2_freq, krait_l2[cpu]); | ||||
| + | ||||
| +			l2_freq = clk_get_rate(l2_clk); | ||||
| + | ||||
| +			if (l2_freq != new_l2_freq) { | ||||
| +				/* scale l2 with the core */ | ||||
| +				ret = clk_set_rate(l2_clk, new_l2_freq); | ||||
| +			} | ||||
| +		} | ||||
| + | ||||
| 		priv->opp_freq = target_freq; | ||||
| +	} | ||||
| + | ||||
| 	mutex_unlock(&priv->lock); | ||||
|  | ||||
| 	return ret; | ||||
| @@ -196,6 +226,8 @@ static int cpufreq_init(struct cpufreq_p | ||||
| 	const char *name; | ||||
| 	int ret; | ||||
| 	struct srcu_notifier_head *opp_srcu_head; | ||||
| +	struct device_node *l2_np; | ||||
| +	struct clk *l2_clk = NULL; | ||||
|  | ||||
| 	cpu_dev = get_cpu_device(policy->cpu); | ||||
| 	if (!cpu_dev) { | ||||
| @@ -303,6 +335,13 @@ static int cpufreq_init(struct cpufreq_p | ||||
|  | ||||
| 	policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000; | ||||
|  | ||||
| +	l2_clk = clk_get(cpu_dev, "l2"); | ||||
| +	if (!IS_ERR(l2_clk)) | ||||
| +		policy->l2_clk = l2_clk; | ||||
| +	l2_np = of_find_node_by_name(NULL, "qcom,l2"); | ||||
| +	if (l2_np) | ||||
| +		of_property_read_u32_array(l2_np, "qcom,l2-rates", policy->l2_rate, 3); | ||||
| + | ||||
| 	ret = cpufreq_table_validate_and_show(policy, freq_table); | ||||
| 	if (ret) { | ||||
| 		dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, | ||||
| --- a/include/linux/cpufreq.h | ||||
| +++ b/include/linux/cpufreq.h | ||||
| @@ -73,6 +73,8 @@ struct cpufreq_policy { | ||||
| 	unsigned int		cpu;    /* cpu managing this policy, must be online */ | ||||
|  | ||||
| 	struct clk		*clk; | ||||
| +	struct clk		*l2_clk; /* L2 clock */ | ||||
| +	unsigned int		l2_rate[3]; /* L2 bus clock rate thresholds */ | ||||
| 	struct cpufreq_cpuinfo	cpuinfo;/* see above */ | ||||
|  | ||||
| 	unsigned int		min;    /* in kHz */ | ||||
| @@ -0,0 +1,23 @@ | ||||
| From 001a8dcb56ced58c518aaa10a4f0ba5e878705b6 Mon Sep 17 00:00:00 2001 | ||||
| From: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| Date: Tue, 17 May 2016 16:15:43 +0300 | ||||
| Subject: [PATCH 56/69] cpufreq-dt: Add missing rcu locks | ||||
|  | ||||
| Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> | ||||
| --- | ||||
|  drivers/cpufreq/cpufreq-dt.c | 2 ++ | ||||
|  1 file changed, 2 insertions(+) | ||||
|  | ||||
| --- a/drivers/cpufreq/cpufreq-dt.c | ||||
| +++ b/drivers/cpufreq/cpufreq-dt.c | ||||
| @@ -143,8 +143,10 @@ static int opp_notifier(struct notifier_ | ||||
| 			ret = PTR_ERR(cpu_reg); | ||||
| 			goto out; | ||||
| 		} | ||||
| +		rcu_read_lock(); | ||||
| 		volt = dev_pm_opp_get_voltage(opp); | ||||
| 		freq = dev_pm_opp_get_freq(opp); | ||||
| +		rcu_read_unlock(); | ||||
|  | ||||
| 		mutex_lock(&priv->lock); | ||||
| 		if (freq == priv->opp_freq) { | ||||
| @@ -0,0 +1,29 @@ | ||||
| From 04ca10340f1b4d92e849724d322a7ca225d11539 Mon Sep 17 00:00:00 2001 | ||||
| From: Lina Iyer <lina.iyer@linaro.org> | ||||
| Date: Wed, 25 Mar 2015 14:25:29 -0600 | ||||
| Subject: [PATCH 59/69] ARM: cpuidle: Add cpuidle support for QCOM cpus | ||||
|  | ||||
| Define ARM_QCOM_CPUIDLE config item to enable cpuidle support. | ||||
|  | ||||
| Cc: Stephen Boyd <sboyd@codeaurora.org> | ||||
| Cc: Arnd Bergmann <arnd@arndb.de> | ||||
| Cc: Kevin Hilman <khilman@linaro.org> | ||||
| Cc: Daniel Lezcano <daniel.lezcano@linaro.org> | ||||
| Signed-off-by: Lina Iyer <lina.iyer@linaro.org> | ||||
| --- | ||||
|  drivers/cpuidle/Kconfig.arm | 7 +++++++ | ||||
|  1 file changed, 7 insertions(+) | ||||
|  | ||||
| --- a/drivers/cpuidle/Kconfig.arm | ||||
| +++ b/drivers/cpuidle/Kconfig.arm | ||||
| @@ -75,3 +75,10 @@ config ARM_MVEBU_V7_CPUIDLE | ||||
| 	depends on ARCH_MVEBU && !ARM64 | ||||
| 	help | ||||
| 	  Select this to enable cpuidle on Armada 370, 38x and XP processors. | ||||
| + | ||||
| +config ARM_QCOM_CPUIDLE | ||||
| +	bool "CPU Idle Driver for QCOM processors" | ||||
| +	depends on ARCH_QCOM | ||||
| +	select ARM_CPUIDLE | ||||
| +	help | ||||
| +	  Select this to enable cpuidle on QCOM processors. | ||||
| @@ -0,0 +1,62 @@ | ||||
| From fa71139b55e114aa8c3c4823ff8ee7d49ee810d4 Mon Sep 17 00:00:00 2001 | ||||
| From: Mathieu Olivari <mathieu@codeaurora.org> | ||||
| Date: Wed, 29 Apr 2015 15:21:46 -0700 | ||||
| Subject: [PATCH 60/69] HACK: arch: arm: force ZRELADDR on arch-qcom | ||||
|  | ||||
| ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended | ||||
| on most ARM architectures. This automatically calculate ZRELADDR by | ||||
| masking PHYS_OFFSET with 0xf8000000. | ||||
|  | ||||
| However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware | ||||
| network accelerators, and the bootloader removes this section from the | ||||
| layout passed from the ATAGS (when used). | ||||
|  | ||||
| For newer bootloader, when DT is used, this is not a problem, we just | ||||
| reserve this memory in the device tree. But if the bootloader doesn't | ||||
| have DT support, then ATAGS have to be used. In this case, the ARM | ||||
| decompressor will position the kernel in this low mem, which will not be | ||||
| in the RAM section mapped by the bootloader, which means the kernel will | ||||
| freeze in the middle of the boot process trying to map the memory. | ||||
|  | ||||
| As a work around, this patch allows disabling AUTO_ZRELADDR when | ||||
| ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders | ||||
| which don't support device-tree, which is the case on certain early | ||||
| IPQ806x based designs. | ||||
|  | ||||
| Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> | ||||
| --- | ||||
|  arch/arm/Kconfig                 | 2 +- | ||||
|  arch/arm/Makefile                | 2 ++ | ||||
|  arch/arm/mach-qcom/Makefile.boot | 1 + | ||||
|  3 files changed, 4 insertions(+), 1 deletion(-) | ||||
|  create mode 100644 arch/arm/mach-qcom/Makefile.boot | ||||
|  | ||||
| --- a/arch/arm/Kconfig | ||||
| +++ b/arch/arm/Kconfig | ||||
| @@ -341,7 +341,7 @@ | ||||
| 	depends on MMU | ||||
| 	select ARM_HAS_SG_CHAIN | ||||
| 	select ARM_PATCH_PHYS_VIRT | ||||
| -	select AUTO_ZRELADDR | ||||
| +	select AUTO_ZRELADDR if !ARCH_QCOM | ||||
| 	select TIMER_OF | ||||
| 	select COMMON_CLK | ||||
| 	select GENERIC_CLOCKEVENTS | ||||
| --- a/arch/arm/Makefile | ||||
| +++ b/arch/arm/Makefile | ||||
| @@ -255,9 +255,11 @@ MACHINE  := arch/arm/mach-$(word 1,$(mac | ||||
|  else | ||||
|  MACHINE  := | ||||
|  endif | ||||
| +ifeq ($(CONFIG_ARCH_QCOM),) | ||||
|  ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y) | ||||
|  MACHINE  := | ||||
|  endif | ||||
| +endif | ||||
|  | ||||
|  machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) | ||||
|  platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y))) | ||||
| --- /dev/null | ||||
| +++ b/arch/arm/mach-qcom/Makefile.boot | ||||
| @@ -0,0 +1 @@ | ||||
| +zreladdr-y+= 0x42208000 | ||||
| @@ -0,0 +1,23 @@ | ||||
| From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001 | ||||
| From: John Crispin <john@phrozen.org> | ||||
| Date: Thu, 9 Mar 2017 09:31:44 +0100 | ||||
| Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting | ||||
|  | ||||
| Signed-off-by: John Crispin <john@phrozen.org> | ||||
| --- | ||||
|  drivers/mtd/qcom_smem_part.c | 4 ++++ | ||||
|  1 file changed, 4 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/qcom_smem_part.c | ||||
| +++ b/drivers/mtd/qcom_smem_part.c | ||||
| @@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st | ||||
| 		m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); | ||||
| 		m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); | ||||
|  | ||||
| +		/* "rootfs" conflicts with OpenWrt auto mounting */ | ||||
| +		if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs")) | ||||
| +			m_part->name = "ubi"; | ||||
| + | ||||
| 		/* | ||||
| 		 * The last SMEM partition may have its size marked as | ||||
| 		 * something like 0xffffffff, which means "until the end of the | ||||
| @@ -0,0 +1,25 @@ | ||||
| From a16fcf911a020e46439a3bb3e702463fc3159831 Mon Sep 17 00:00:00 2001 | ||||
| From: Abhishek Sahu <absahu@codeaurora.org> | ||||
| Date: Wed, 18 Nov 2015 12:38:56 +0530 | ||||
| Subject: [PATCH 62/69] ipq806x: gcc: Added the enable regs and mask for PRNG | ||||
|  | ||||
| kernel got hanged while reading from /dev/hwrng at the | ||||
| time of PRNG clock enable | ||||
|  | ||||
| Change-Id: I89856c7e19e6639508e6a2774304583a3ec91172 | ||||
| Signed-off-by: Abhishek Sahu <absahu@codeaurora.org> | ||||
| --- | ||||
|  drivers/clk/qcom/gcc-ipq806x.c | 2 ++ | ||||
|  1 file changed, 2 insertions(+) | ||||
|  | ||||
| --- a/drivers/clk/qcom/gcc-ipq806x.c | ||||
| +++ b/drivers/clk/qcom/gcc-ipq806x.c | ||||
| @@ -1233,6 +1233,8 @@ static struct clk_rcg prng_src = { | ||||
| 		.parent_map = gcc_pxo_pll8_map, | ||||
| 	}, | ||||
| 	.clkr = { | ||||
| +		.enable_reg = 0x2e80, | ||||
| +		.enable_mask = BIT(11), | ||||
| 		.hw.init = &(struct clk_init_data){ | ||||
| 			.name = "prng_src", | ||||
| 			.parent_names = gcc_pxo_pll8, | ||||
| @@ -0,0 +1,627 @@ | ||||
| From 3302e1e1a3cfa4e67fda2a61d6f0c42205d40932 Mon Sep 17 00:00:00 2001 | ||||
| From: Rajith Cherian <rajith@codeaurora.org> | ||||
| Date: Tue, 14 Feb 2017 18:30:43 +0530 | ||||
| Subject: [PATCH] ipq8064: tsens: Base tsens driver for IPQ8064 | ||||
|  | ||||
| Add TSENS driver template to support IPQ8064. | ||||
| This is a base file copied from tsens-8960.c | ||||
|  | ||||
| Change-Id: I47c573fdfa2d898243c6a6ba952d1632f91391f7 | ||||
| Signed-off-by: Rajith Cherian <rajith@codeaurora.org> | ||||
|  | ||||
| ipq8064: tsens: TSENS driver support for IPQ8064 | ||||
|  | ||||
| Support for IPQ8064 tsens driver. The driver works | ||||
| with the thermal framework. The driver overrides the | ||||
| following fucntionalities: | ||||
|  | ||||
| 1. Get current temperature. | ||||
| 2. Get/Set trip temperatures. | ||||
| 3. Enabled/Disable trip points. | ||||
| 4. ISR for threshold generated interrupt. | ||||
| 5. Notify userspace when trip points are hit. | ||||
|  | ||||
| Change-Id: I8bc7204fd627d10875ab13fc1de8cb6c2ed7a918 | ||||
| Signed-off-by: Rajith Cherian <rajith@codeaurora.org> | ||||
| --- | ||||
|  .../devicetree/bindings/thermal/qcom-tsens.txt     |   1 + | ||||
|  drivers/thermal/qcom/Makefile                      |   3 +- | ||||
|  drivers/thermal/qcom/tsens-ipq8064.c               | 551 +++++++++++++++++++++ | ||||
|  drivers/thermal/qcom/tsens.c                       |   3 + | ||||
|  drivers/thermal/qcom/tsens.h                       |   2 +- | ||||
|  5 files changed, 558 insertions(+), 2 deletions(-) | ||||
|  create mode 100644 drivers/thermal/qcom/tsens-ipq8064.c | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt | ||||
| +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt | ||||
| @@ -5,6 +5,7 @@ Required properties: | ||||
|   - "qcom,msm8916-tsens" : For 8916 Family of SoCs | ||||
|   - "qcom,msm8974-tsens" : For 8974 Family of SoCs | ||||
|   - "qcom,msm8996-tsens" : For 8996 Family of SoCs | ||||
| + - "qcom,ipq8064-tsens" : For IPQ8064 | ||||
|  | ||||
|  - reg: Address range of the thermal registers | ||||
|  - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. | ||||
| --- a/drivers/thermal/qcom/Makefile | ||||
| +++ b/drivers/thermal/qcom/Makefile | ||||
| @@ -1,2 +1,3 @@ | ||||
|  obj-$(CONFIG_QCOM_TSENS)	+= qcom_tsens.o | ||||
| -qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o | ||||
| +qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o \ | ||||
| +				tsens-ipq8064.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/thermal/qcom/tsens-ipq8064.c | ||||
| @@ -0,0 +1,551 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2015, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + * | ||||
| + */ | ||||
| + | ||||
| +#include <linux/platform_device.h> | ||||
| +#include <linux/delay.h> | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/regmap.h> | ||||
| +#include <linux/thermal.h> | ||||
| +#include <linux/nvmem-consumer.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/interrupt.h> | ||||
| +#include "tsens.h" | ||||
| + | ||||
| +#define CAL_MDEGC		30000 | ||||
| + | ||||
| +#define CONFIG_ADDR		0x3640 | ||||
| +/* CONFIG_ADDR bitmasks */ | ||||
| +#define CONFIG			0x9b | ||||
| +#define CONFIG_MASK		0xf | ||||
| +#define CONFIG_SHIFT		0 | ||||
| + | ||||
| +#define STATUS_CNTL_8064	0x3660 | ||||
| +#define CNTL_ADDR		0x3620 | ||||
| +/* CNTL_ADDR bitmasks */ | ||||
| +#define EN			BIT(0) | ||||
| +#define SW_RST			BIT(1) | ||||
| +#define SENSOR0_EN		BIT(3) | ||||
| +#define SLP_CLK_ENA		BIT(26) | ||||
| +#define MEASURE_PERIOD		1 | ||||
| +#define SENSOR0_SHIFT		3 | ||||
| + | ||||
| +/* INT_STATUS_ADDR bitmasks */ | ||||
| +#define MIN_STATUS_MASK		BIT(0) | ||||
| +#define LOWER_STATUS_CLR	BIT(1) | ||||
| +#define UPPER_STATUS_CLR	BIT(2) | ||||
| +#define MAX_STATUS_MASK		BIT(3) | ||||
| + | ||||
| +#define THRESHOLD_ADDR		0x3624 | ||||
| +/* THRESHOLD_ADDR bitmasks */ | ||||
| +#define THRESHOLD_MAX_CODE		0x20000 | ||||
| +#define THRESHOLD_MIN_CODE		0 | ||||
| +#define THRESHOLD_MAX_LIMIT_SHIFT	24 | ||||
| +#define THRESHOLD_MIN_LIMIT_SHIFT	16 | ||||
| +#define THRESHOLD_UPPER_LIMIT_SHIFT	8 | ||||
| +#define THRESHOLD_LOWER_LIMIT_SHIFT	0 | ||||
| +#define THRESHOLD_MAX_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ | ||||
| +						THRESHOLD_MAX_LIMIT_SHIFT) | ||||
| +#define THRESHOLD_MIN_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ | ||||
| +						THRESHOLD_MIN_LIMIT_SHIFT) | ||||
| +#define THRESHOLD_UPPER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ | ||||
| +						THRESHOLD_UPPER_LIMIT_SHIFT) | ||||
| +#define THRESHOLD_LOWER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \ | ||||
| +						THRESHOLD_LOWER_LIMIT_SHIFT) | ||||
| + | ||||
| +/* Initial temperature threshold values */ | ||||
| +#define LOWER_LIMIT_TH		0x9d /* 95C */ | ||||
| +#define UPPER_LIMIT_TH		0xa6 /* 105C */ | ||||
| +#define MIN_LIMIT_TH		0x0 | ||||
| +#define MAX_LIMIT_TH		0xff | ||||
| + | ||||
| +#define S0_STATUS_ADDR		0x3628 | ||||
| +#define STATUS_ADDR_OFFSET	2 | ||||
| +#define SENSOR_STATUS_SIZE	4 | ||||
| +#define INT_STATUS_ADDR		0x363c | ||||
| +#define TRDY_MASK		BIT(7) | ||||
| +#define TIMEOUT_US		100 | ||||
| + | ||||
| +#define TSENS_EN		BIT(0) | ||||
| +#define TSENS_SW_RST		BIT(1) | ||||
| +#define TSENS_ADC_CLK_SEL	BIT(2) | ||||
| +#define SENSOR0_EN		BIT(3) | ||||
| +#define SENSOR1_EN		BIT(4) | ||||
| +#define SENSOR2_EN		BIT(5) | ||||
| +#define SENSOR3_EN		BIT(6) | ||||
| +#define SENSOR4_EN		BIT(7) | ||||
| +#define SENSORS_EN		(SENSOR0_EN | SENSOR1_EN | \ | ||||
| +				SENSOR2_EN | SENSOR3_EN | SENSOR4_EN) | ||||
| +#define TSENS_8064_SENSOR5_EN				BIT(8) | ||||
| +#define TSENS_8064_SENSOR6_EN				BIT(9) | ||||
| +#define TSENS_8064_SENSOR7_EN				BIT(10) | ||||
| +#define TSENS_8064_SENSOR8_EN				BIT(11) | ||||
| +#define TSENS_8064_SENSOR9_EN				BIT(12) | ||||
| +#define TSENS_8064_SENSOR10_EN				BIT(13) | ||||
| +#define TSENS_8064_SENSORS_EN				(SENSORS_EN | \ | ||||
| +						TSENS_8064_SENSOR5_EN | \ | ||||
| +						TSENS_8064_SENSOR6_EN | \ | ||||
| +						TSENS_8064_SENSOR7_EN | \ | ||||
| +						TSENS_8064_SENSOR8_EN | \ | ||||
| +						TSENS_8064_SENSOR9_EN | \ | ||||
| +						TSENS_8064_SENSOR10_EN) | ||||
| + | ||||
| +#define TSENS_8064_SEQ_SENSORS	5 | ||||
| +#define TSENS_8064_S4_S5_OFFSET	40 | ||||
| +#define TSENS_FACTOR		1 | ||||
| + | ||||
| +/* Trips: from very hot to very cold */ | ||||
| +enum tsens_trip_type { | ||||
| +	TSENS_TRIP_STAGE3 = 0, | ||||
| +	TSENS_TRIP_STAGE2, | ||||
| +	TSENS_TRIP_STAGE1, | ||||
| +	TSENS_TRIP_STAGE0, | ||||
| +	TSENS_TRIP_NUM, | ||||
| +}; | ||||
| + | ||||
| +u32 tsens_8064_slope[] = { | ||||
| +			1176, 1176, 1154, 1176, | ||||
| +			1111, 1132, 1132, 1199, | ||||
| +			1132, 1199, 1132 | ||||
| +			}; | ||||
| + | ||||
| +/* Temperature on y axis and ADC-code on x-axis */ | ||||
| +static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s) | ||||
| +{ | ||||
| +	int degcbeforefactor, degc; | ||||
| + | ||||
| +	degcbeforefactor = (adc_code * s->slope) + s->offset; | ||||
| + | ||||
| +	if (degcbeforefactor == 0) | ||||
| +		degc = degcbeforefactor; | ||||
| +	else if (degcbeforefactor > 0) | ||||
| +		degc = (degcbeforefactor + TSENS_FACTOR/2) | ||||
| +			/ TSENS_FACTOR; | ||||
| +	else | ||||
| +		degc = (degcbeforefactor - TSENS_FACTOR/2) | ||||
| +			/ TSENS_FACTOR; | ||||
| + | ||||
| +	return degc; | ||||
| +} | ||||
| + | ||||
| +static int degC_to_code(int degC, const struct tsens_sensor *s) | ||||
| +{ | ||||
| +	int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2)) | ||||
| +			/ s->slope; | ||||
| + | ||||
| +	if (code > THRESHOLD_MAX_CODE) | ||||
| +		code = THRESHOLD_MAX_CODE; | ||||
| +	else if (code < THRESHOLD_MIN_CODE) | ||||
| +		code = THRESHOLD_MIN_CODE; | ||||
| +	return code; | ||||
| +} | ||||
| + | ||||
| +static int suspend_ipq8064(struct tsens_device *tmdev) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	unsigned int mask; | ||||
| +	struct regmap *map = tmdev->map; | ||||
| + | ||||
| +	ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	mask = SLP_CLK_ENA | EN; | ||||
| + | ||||
| +	ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int resume_ipq8064(struct tsens_device *tmdev) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	struct regmap *map = tmdev->map; | ||||
| + | ||||
| +	ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static void notify_uspace_tsens_fn(struct work_struct *work) | ||||
| +{ | ||||
| +	struct tsens_sensor *s = container_of(work, struct tsens_sensor, | ||||
| +								notify_work); | ||||
| + | ||||
| +	sysfs_notify(&s->tzd->device.kobj, NULL, "type"); | ||||
| +} | ||||
| + | ||||
| +static void tsens_scheduler_fn(struct work_struct *work) | ||||
| +{ | ||||
| +	struct tsens_device *tmdev = container_of(work, struct tsens_device, | ||||
| +					tsens_work); | ||||
| +	unsigned int threshold, threshold_low, code, reg, sensor, mask; | ||||
| +	unsigned int sensor_addr; | ||||
| +	bool upper_th_x, lower_th_x; | ||||
| +	int adc_code, ret; | ||||
| + | ||||
| +	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); | ||||
| +	if (ret) | ||||
| +		return; | ||||
| +	reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR; | ||||
| +	ret = regmap_write(tmdev->map, STATUS_CNTL_8064, reg); | ||||
| +	if (ret) | ||||
| +		return; | ||||
| + | ||||
| +	mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR); | ||||
| +	ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &threshold); | ||||
| +	if (ret) | ||||
| +		return; | ||||
| +	threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) | ||||
| +				>> THRESHOLD_LOWER_LIMIT_SHIFT; | ||||
| +	threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) | ||||
| +				>> THRESHOLD_UPPER_LIMIT_SHIFT; | ||||
| + | ||||
| +	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); | ||||
| +	if (ret) | ||||
| +		return; | ||||
| + | ||||
| +	ret = regmap_read(tmdev->map, CNTL_ADDR, &sensor); | ||||
| +	if (ret) | ||||
| +		return; | ||||
| +	sensor &= (uint32_t) TSENS_8064_SENSORS_EN; | ||||
| +	sensor >>= SENSOR0_SHIFT; | ||||
| + | ||||
| +	/* Constraint: There is only 1 interrupt control register for all | ||||
| +	 * 11 temperature sensor. So monitoring more than 1 sensor based | ||||
| +	 * on interrupts will yield inconsistent result. To overcome this | ||||
| +	 * issue we will monitor only sensor 0 which is the master sensor. | ||||
| +	 */ | ||||
| + | ||||
| +	/* Skip if the sensor is disabled */ | ||||
| +	if (sensor & 1) { | ||||
| +		ret = regmap_read(tmdev->map, tmdev->sensor[0].status, &code); | ||||
| +		if (ret) | ||||
| +			return; | ||||
| +		upper_th_x = code >= threshold; | ||||
| +		lower_th_x = code <= threshold_low; | ||||
| +		if (upper_th_x) | ||||
| +			mask |= UPPER_STATUS_CLR; | ||||
| +		if (lower_th_x) | ||||
| +			mask |= LOWER_STATUS_CLR; | ||||
| +		if (upper_th_x || lower_th_x) { | ||||
| +			/* Notify user space */ | ||||
| +			schedule_work(&tmdev->sensor[0].notify_work); | ||||
| +			regmap_read(tmdev->map, sensor_addr, &adc_code); | ||||
| +			pr_debug("Trigger (%d degrees) for sensor %d\n", | ||||
| +				code_to_degC(adc_code, &tmdev->sensor[0]), 0); | ||||
| +		} | ||||
| +	} | ||||
| +	regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask); | ||||
| + | ||||
| +	/* force memory to sync */ | ||||
| +	mb(); | ||||
| +} | ||||
| + | ||||
| +static irqreturn_t tsens_isr(int irq, void *data) | ||||
| +{ | ||||
| +	struct tsens_device *tmdev = data; | ||||
| + | ||||
| +	schedule_work(&tmdev->tsens_work); | ||||
| +	return IRQ_HANDLED; | ||||
| +} | ||||
| + | ||||
| +static void hw_init(struct tsens_device *tmdev) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0; | ||||
| +	unsigned int reg_status_cntl = 0; | ||||
| + | ||||
| +	regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); | ||||
| +	regmap_write(tmdev->map, CNTL_ADDR, reg_cntl | TSENS_SW_RST); | ||||
| + | ||||
| +	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) | ||||
| +		| (((1 << tmdev->num_sensors) - 1) << SENSOR0_SHIFT); | ||||
| +	regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | ||||
| +	regmap_read(tmdev->map, STATUS_CNTL_8064, ®_status_cntl); | ||||
| +	reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR | ||||
| +			| MIN_STATUS_MASK | MAX_STATUS_MASK; | ||||
| +	regmap_write(tmdev->map, STATUS_CNTL_8064, reg_status_cntl); | ||||
| +	reg_cntl |= TSENS_EN; | ||||
| +	regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | ||||
| + | ||||
| +	regmap_read(tmdev->map, CONFIG_ADDR, ®_cfg); | ||||
| +	reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT); | ||||
| +	regmap_write(tmdev->map, CONFIG_ADDR, reg_cfg); | ||||
| + | ||||
| +	reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT) | ||||
| +		| (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT) | ||||
| +		| (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) | ||||
| +		| (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT); | ||||
| + | ||||
| +	regmap_write(tmdev->map, THRESHOLD_ADDR, reg_thr); | ||||
| + | ||||
| +	ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr, | ||||
| +			IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev); | ||||
| +	if (ret < 0) { | ||||
| +		pr_err("%s: request_irq FAIL: %d\n", __func__, ret); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
| +	INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn); | ||||
| +} | ||||
| + | ||||
| +static int init_ipq8064(struct tsens_device *tmdev) | ||||
| +{ | ||||
| +	int ret, i; | ||||
| +	u32 reg_cntl, offset = 0; | ||||
| + | ||||
| +	init_common(tmdev); | ||||
| +	if (!tmdev->map) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	/* | ||||
| +	 * The status registers for each sensor are discontiguous | ||||
| +	 * because some SoCs have 5 sensors while others have more | ||||
| +	 * but the control registers stay in the same place, i.e | ||||
| +	 * directly after the first 5 status registers. | ||||
| +	 */ | ||||
| +	for (i = 0; i < tmdev->num_sensors; i++) { | ||||
| +		if (i >= TSENS_8064_SEQ_SENSORS) | ||||
| +			offset = TSENS_8064_S4_S5_OFFSET; | ||||
| + | ||||
| +		tmdev->sensor[i].status = S0_STATUS_ADDR + offset | ||||
| +					+ (i << STATUS_ADDR_OFFSET); | ||||
| +		tmdev->sensor[i].slope = tsens_8064_slope[i]; | ||||
| +		INIT_WORK(&tmdev->sensor[i].notify_work, | ||||
| +						notify_uspace_tsens_fn); | ||||
| +	} | ||||
| + | ||||
| +	reg_cntl = SW_RST; | ||||
| +	ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); | ||||
| +	reg_cntl &= ~SW_RST; | ||||
| +	ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, | ||||
| +					 CONFIG_MASK, CONFIG); | ||||
| + | ||||
| +	reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; | ||||
| +	ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	reg_cntl |= EN; | ||||
| +	ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int calibrate_ipq8064(struct tsens_device *tmdev) | ||||
| +{ | ||||
| +	int i; | ||||
| +	char *data, *data_backup; | ||||
| + | ||||
| +	ssize_t num_read = tmdev->num_sensors; | ||||
| +	struct tsens_sensor *s = tmdev->sensor; | ||||
| + | ||||
| +	data = qfprom_read(tmdev->dev, "calib"); | ||||
| +	if (IS_ERR(data)) { | ||||
| +		pr_err("Calibration not found.\n"); | ||||
| +		return PTR_ERR(data); | ||||
| +	} | ||||
| + | ||||
| +	data_backup = qfprom_read(tmdev->dev, "calib_backup"); | ||||
| +	if (IS_ERR(data_backup)) { | ||||
| +		pr_err("Backup calibration not found.\n"); | ||||
| +		return PTR_ERR(data_backup); | ||||
| +	} | ||||
| + | ||||
| +	for (i = 0; i < num_read; i++) { | ||||
| +		s[i].calib_data = readb_relaxed(data + i); | ||||
| +		s[i].calib_data_backup = readb_relaxed(data_backup + i); | ||||
| + | ||||
| +		if (s[i].calib_data_backup) | ||||
| +			s[i].calib_data = s[i].calib_data_backup; | ||||
| +		if (!s[i].calib_data) { | ||||
| +			pr_err("QFPROM TSENS calibration data not present\n"); | ||||
| +			return -ENODEV; | ||||
| +		} | ||||
| +		s[i].slope = tsens_8064_slope[i]; | ||||
| +		s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope); | ||||
| +	} | ||||
| + | ||||
| +	hw_init(tmdev); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	u32 code, trdy; | ||||
| +	const struct tsens_sensor *s = &tmdev->sensor[id]; | ||||
| +	unsigned long timeout; | ||||
| + | ||||
| +	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); | ||||
| +	do { | ||||
| +		ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); | ||||
| +		if (ret) | ||||
| +			return ret; | ||||
| +		if (!(trdy & TRDY_MASK)) | ||||
| +			continue; | ||||
| +		ret = regmap_read(tmdev->map, s->status, &code); | ||||
| +		if (ret) | ||||
| +			return ret; | ||||
| +		*temp = code_to_degC(code, s); | ||||
| +		return 0; | ||||
| +	} while (time_before(jiffies, timeout)); | ||||
| + | ||||
| +	return -ETIMEDOUT; | ||||
| +} | ||||
| + | ||||
| +static int set_trip_temp_ipq8064(void *data, int trip, int temp) | ||||
| +{ | ||||
| +	unsigned int reg_th, reg_cntl; | ||||
| +	int ret, code, code_chk, hi_code, lo_code; | ||||
| +	const struct tsens_sensor *s = data; | ||||
| +	struct tsens_device *tmdev = s->tmdev; | ||||
| + | ||||
| +	code_chk = code = degC_to_code(temp, s); | ||||
| + | ||||
| +	if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = regmap_read(tmdev->map, THRESHOLD_ADDR, ®_th); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK) | ||||
| +			>> THRESHOLD_UPPER_LIMIT_SHIFT; | ||||
| +	lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK) | ||||
| +			>> THRESHOLD_LOWER_LIMIT_SHIFT; | ||||
| + | ||||
| +	switch (trip) { | ||||
| +	case TSENS_TRIP_STAGE3: | ||||
| +		code <<= THRESHOLD_MAX_LIMIT_SHIFT; | ||||
| +		reg_th &= ~THRESHOLD_MAX_LIMIT_MASK; | ||||
| +		break; | ||||
| +	case TSENS_TRIP_STAGE2: | ||||
| +		if (code_chk <= lo_code) | ||||
| +			return -EINVAL; | ||||
| +		code <<= THRESHOLD_UPPER_LIMIT_SHIFT; | ||||
| +		reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK; | ||||
| +		break; | ||||
| +	case TSENS_TRIP_STAGE1: | ||||
| +		if (code_chk >= hi_code) | ||||
| +			return -EINVAL; | ||||
| +		code <<= THRESHOLD_LOWER_LIMIT_SHIFT; | ||||
| +		reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK; | ||||
| +		break; | ||||
| +	case TSENS_TRIP_STAGE0: | ||||
| +		code <<= THRESHOLD_MIN_LIMIT_SHIFT; | ||||
| +		reg_th &= ~THRESHOLD_MIN_LIMIT_MASK; | ||||
| +		break; | ||||
| +	default: | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	ret = regmap_write(tmdev->map, THRESHOLD_ADDR, reg_th | code); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int set_trip_activate_ipq8064(void *data, int trip, | ||||
| +					enum thermal_trip_activation_mode mode) | ||||
| +{ | ||||
| +	unsigned int reg_cntl, mask, val; | ||||
| +	const struct tsens_sensor *s = data; | ||||
| +	struct tsens_device *tmdev = s->tmdev; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (!tmdev || trip < 0) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	switch (trip) { | ||||
| +	case TSENS_TRIP_STAGE3: | ||||
| +		mask = MAX_STATUS_MASK; | ||||
| +		break; | ||||
| +	case TSENS_TRIP_STAGE2: | ||||
| +		mask = UPPER_STATUS_CLR; | ||||
| +		break; | ||||
| +	case TSENS_TRIP_STAGE1: | ||||
| +		mask = LOWER_STATUS_CLR; | ||||
| +		break; | ||||
| +	case TSENS_TRIP_STAGE0: | ||||
| +		mask = MIN_STATUS_MASK; | ||||
| +		break; | ||||
| +	default: | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) | ||||
| +		val = reg_cntl | mask; | ||||
| +	else | ||||
| +		val = reg_cntl & ~mask; | ||||
| + | ||||
| +	ret = regmap_write(tmdev->map, STATUS_CNTL_8064, val); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* force memory to sync */ | ||||
| +	mb(); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +const struct tsens_ops ops_ipq8064 = { | ||||
| +	.init		= init_ipq8064, | ||||
| +	.calibrate	= calibrate_ipq8064, | ||||
| +	.get_temp	= get_temp_ipq8064, | ||||
| +	.suspend	= suspend_ipq8064, | ||||
| +	.resume		= resume_ipq8064, | ||||
| +	.set_trip_temp	= set_trip_temp_ipq8064, | ||||
| +	.set_trip_activate = set_trip_activate_ipq8064, | ||||
| +}; | ||||
| + | ||||
| +const struct tsens_data data_ipq8064 = { | ||||
| +	.num_sensors	= 11, | ||||
| +	.ops		= &ops_ipq8064, | ||||
| +}; | ||||
| --- a/drivers/thermal/qcom/tsens.c | ||||
| +++ b/drivers/thermal/qcom/tsens.c | ||||
| @@ -72,6 +72,9 @@ static const struct of_device_id tsens_t | ||||
| 	}, { | ||||
| 		.compatible = "qcom,msm8996-tsens", | ||||
| 		.data = &data_8996, | ||||
| +	}, { | ||||
| +		.compatible = "qcom,ipq8064-tsens", | ||||
| +		.data = &data_ipq8064, | ||||
| 	}, | ||||
| 	{} | ||||
|  }; | ||||
| --- a/drivers/thermal/qcom/tsens.h | ||||
| +++ b/drivers/thermal/qcom/tsens.h | ||||
| @@ -89,6 +89,6 @@ void compute_intercept_slope(struct tsen | ||||
|  int init_common(struct tsens_device *); | ||||
|  int get_temp_common(struct tsens_device *, int, int *); | ||||
|  | ||||
| -extern const struct tsens_data data_8916, data_8974, data_8960, data_8996; | ||||
| +extern const struct tsens_data data_8916, data_8974, data_8960, data_8996, data_ipq8064; | ||||
|  | ||||
|  #endif /* __QCOM_TSENS_H__ */ | ||||
| @@ -0,0 +1,453 @@ | ||||
| From 4e87400732c77765afae2ea89ed43837457aa604 Mon Sep 17 00:00:00 2001 | ||||
| From: Rajith Cherian <rajith@codeaurora.org> | ||||
| Date: Wed, 1 Feb 2017 19:00:26 +0530 | ||||
| Subject: [PATCH] ipq8064: tsens: Support for configurable interrupts | ||||
|  | ||||
| Provide support for adding configurable high and | ||||
| configurable low trip temperatures. An interrupts is | ||||
| also triggerred when these trip points are hit. The | ||||
| interrupts can be activated or deactivated from sysfs. | ||||
| This functionality is made available only if | ||||
| CONFIG_THERMAL_WRITABLE_TRIPS is defined. | ||||
|  | ||||
| Change-Id: Ib73f3f9459de4fffce7bb985a0312a88291f4934 | ||||
| Signed-off-by: Rajith Cherian <rajith@codeaurora.org> | ||||
| --- | ||||
|  .../devicetree/bindings/thermal/qcom-tsens.txt     |  4 ++ | ||||
|  drivers/thermal/of-thermal.c                       | 63 ++++++++++++++++++---- | ||||
|  drivers/thermal/qcom/tsens.c                       | 43 ++++++++++++--- | ||||
|  drivers/thermal/qcom/tsens.h                       | 11 ++++ | ||||
|  drivers/thermal/thermal_core.c                     | 44 ++++++++++++++- | ||||
|  include/linux/thermal.h                            | 14 +++++ | ||||
|  6 files changed, 162 insertions(+), 17 deletions(-) | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt | ||||
| +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt | ||||
| @@ -12,11 +12,15 @@ Required properties: | ||||
|  - Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify | ||||
|  nvmem cells | ||||
|  | ||||
| +Optional properties: | ||||
| +- interrupts: Interrupt which gets triggered when threshold is hit | ||||
| + | ||||
|  Example: | ||||
|  tsens: thermal-sensor@900000 { | ||||
| 		compatible = "qcom,msm8916-tsens"; | ||||
| 		reg = <0x4a8000 0x2000>; | ||||
| 		nvmem-cells = <&tsens_caldata>, <&tsens_calsel>; | ||||
| 		nvmem-cell-names = "caldata", "calsel"; | ||||
| +		interrupts = <0 178 0>; | ||||
| 		#thermal-sensor-cells = <1>; | ||||
| 	}; | ||||
| --- a/drivers/thermal/of-thermal.c | ||||
| +++ b/drivers/thermal/of-thermal.c | ||||
| @@ -95,7 +95,7 @@ static int of_thermal_get_temp(struct th | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (!data->ops->get_temp) | ||||
| +	if (!data->ops->get_temp || (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return data->ops->get_temp(data->sensor_data, temp); | ||||
| @@ -106,7 +106,8 @@ static int of_thermal_set_trips(struct t | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (!data->ops || !data->ops->set_trips) | ||||
| +	if (!data->ops || !data->ops->set_trips | ||||
| +			|| (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return data->ops->set_trips(data->sensor_data, low, high); | ||||
| @@ -192,6 +193,9 @@ static int of_thermal_set_emul_temp(stru | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| +	if (data->mode == THERMAL_DEVICE_DISABLED) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| 	return data->ops->set_emul_temp(data->sensor_data, temp); | ||||
|  } | ||||
|  | ||||
| @@ -200,7 +204,7 @@ static int of_thermal_get_trend(struct t | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (!data->ops->get_trend) | ||||
| +	if (!data->ops->get_trend || (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return data->ops->get_trend(data->sensor_data, trip, trend); | ||||
| @@ -286,7 +290,9 @@ static int of_thermal_set_mode(struct th | ||||
| 	mutex_unlock(&tz->lock); | ||||
|  | ||||
| 	data->mode = mode; | ||||
| -	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | ||||
| + | ||||
| +	if (mode == THERMAL_DEVICE_ENABLED) | ||||
| +		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | ||||
|  | ||||
| 	return 0; | ||||
|  } | ||||
| @@ -296,7 +302,8 @@ static int of_thermal_get_trip_type(stru | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (trip >= data->ntrips || trip < 0) | ||||
| +	if (trip >= data->ntrips || trip < 0 | ||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EDOM; | ||||
|  | ||||
| 	*type = data->trips[trip].type; | ||||
| @@ -304,12 +311,39 @@ static int of_thermal_get_trip_type(stru | ||||
| 	return 0; | ||||
|  } | ||||
|  | ||||
| +static int of_thermal_activate_trip_type(struct thermal_zone_device *tz, | ||||
| +			int trip, enum thermal_trip_activation_mode mode) | ||||
| +{ | ||||
| +	struct __thermal_zone *data = tz->devdata; | ||||
| + | ||||
| +	if (trip >= data->ntrips || trip < 0 | ||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| +		return -EDOM; | ||||
| + | ||||
| +	/* | ||||
| +	 * The configurable_hi and configurable_lo trip points can be | ||||
| +	 * activated and deactivated. | ||||
| +	 */ | ||||
| + | ||||
| +	if (data->ops->set_trip_activate) { | ||||
| +		int ret; | ||||
| + | ||||
| +		ret = data->ops->set_trip_activate(data->sensor_data, | ||||
| +								trip, mode); | ||||
| +		if (ret) | ||||
| +			return ret; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip, | ||||
| 				    int *temp) | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (trip >= data->ntrips || trip < 0) | ||||
| +	if (trip >= data->ntrips || trip < 0 | ||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EDOM; | ||||
|  | ||||
| 	*temp = data->trips[trip].temperature; | ||||
| @@ -322,7 +356,8 @@ static int of_thermal_set_trip_temp(stru | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (trip >= data->ntrips || trip < 0) | ||||
| +	if (trip >= data->ntrips || trip < 0 | ||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EDOM; | ||||
|  | ||||
| 	if (data->ops->set_trip_temp) { | ||||
| @@ -344,7 +379,8 @@ static int of_thermal_get_trip_hyst(stru | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (trip >= data->ntrips || trip < 0) | ||||
| +	if (trip >= data->ntrips || trip < 0 | ||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EDOM; | ||||
|  | ||||
| 	*hyst = data->trips[trip].hysteresis; | ||||
| @@ -357,7 +393,8 @@ static int of_thermal_set_trip_hyst(stru | ||||
|  { | ||||
| 	struct __thermal_zone *data = tz->devdata; | ||||
|  | ||||
| -	if (trip >= data->ntrips || trip < 0) | ||||
| +	if (trip >= data->ntrips || trip < 0 | ||||
| +				|| (data->mode == THERMAL_DEVICE_DISABLED)) | ||||
| 		return -EDOM; | ||||
|  | ||||
| 	/* thermal framework should take care of data->mask & (1 << trip) */ | ||||
| @@ -432,6 +469,9 @@ thermal_zone_of_add_sensor(struct device | ||||
| 	if (ops->set_emul_temp) | ||||
| 		tzd->ops->set_emul_temp = of_thermal_set_emul_temp; | ||||
|  | ||||
| +	if (ops->set_trip_activate) | ||||
| +		tzd->ops->set_trip_activate = of_thermal_activate_trip_type; | ||||
| + | ||||
| 	mutex_unlock(&tzd->lock); | ||||
|  | ||||
| 	return tzd; | ||||
| @@ -726,7 +766,10 @@ static const char * const trip_types[] = | ||||
| 	[THERMAL_TRIP_ACTIVE]	= "active", | ||||
| 	[THERMAL_TRIP_PASSIVE]	= "passive", | ||||
| 	[THERMAL_TRIP_HOT]	= "hot", | ||||
| -	[THERMAL_TRIP_CRITICAL]	= "critical", | ||||
| +	[THERMAL_TRIP_CRITICAL]	= "critical_high", | ||||
| +	[THERMAL_TRIP_CONFIGURABLE_HI] = "configurable_hi", | ||||
| +	[THERMAL_TRIP_CONFIGURABLE_LOW] = "configurable_lo", | ||||
| +	[THERMAL_TRIP_CRITICAL_LOW] = "critical_low", | ||||
|  }; | ||||
|  | ||||
|  /** | ||||
| --- a/drivers/thermal/qcom/tsens.c | ||||
| +++ b/drivers/thermal/qcom/tsens.c | ||||
| @@ -31,7 +31,7 @@ static int tsens_get_temp(void *data, in | ||||
|  | ||||
|  static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend) | ||||
|  { | ||||
| -	const struct tsens_sensor *s = p; | ||||
| +	struct tsens_sensor *s = p; | ||||
| 	struct tsens_device *tmdev = s->tmdev; | ||||
|  | ||||
| 	if (tmdev->ops->get_trend) | ||||
| @@ -40,9 +40,10 @@ static int tsens_get_trend(void *p, int | ||||
| 	return -ENOTSUPP; | ||||
|  } | ||||
|  | ||||
| -static int  __maybe_unused tsens_suspend(struct device *dev) | ||||
| +static int  __maybe_unused tsens_suspend(void *data) | ||||
|  { | ||||
| -	struct tsens_device *tmdev = dev_get_drvdata(dev); | ||||
| +	struct tsens_sensor *s = data; | ||||
| +	struct tsens_device *tmdev = s->tmdev; | ||||
|  | ||||
| 	if (tmdev->ops && tmdev->ops->suspend) | ||||
| 		return tmdev->ops->suspend(tmdev); | ||||
| @@ -50,9 +51,10 @@ static int  __maybe_unused tsens_suspend | ||||
| 	return 0; | ||||
|  } | ||||
|  | ||||
| -static int __maybe_unused tsens_resume(struct device *dev) | ||||
| +static int __maybe_unused tsens_resume(void *data) | ||||
|  { | ||||
| -	struct tsens_device *tmdev = dev_get_drvdata(dev); | ||||
| +	struct tsens_sensor *s = data; | ||||
| +	struct tsens_device *tmdev = s->tmdev; | ||||
|  | ||||
| 	if (tmdev->ops && tmdev->ops->resume) | ||||
| 		return tmdev->ops->resume(tmdev); | ||||
| @@ -60,6 +62,30 @@ static int __maybe_unused tsens_resume(s | ||||
| 	return 0; | ||||
|  } | ||||
|  | ||||
| +static int  __maybe_unused tsens_set_trip_temp(void *data, int trip, int temp) | ||||
| +{ | ||||
| +	struct tsens_sensor *s = data; | ||||
| +	struct tsens_device *tmdev = s->tmdev; | ||||
| + | ||||
| +	if (tmdev->ops && tmdev->ops->set_trip_temp) | ||||
| +		return tmdev->ops->set_trip_temp(s, trip, temp); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int __maybe_unused tsens_activate_trip_type(void *data, int trip, | ||||
| +					enum thermal_trip_activation_mode mode) | ||||
| +{ | ||||
| +	struct tsens_sensor *s = data; | ||||
| +	struct tsens_device *tmdev = s->tmdev; | ||||
| + | ||||
| +	if (tmdev->ops && tmdev->ops->set_trip_activate) | ||||
| +		return tmdev->ops->set_trip_activate(s, trip, mode); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| + | ||||
|  static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); | ||||
|  | ||||
|  static const struct of_device_id tsens_table[] = { | ||||
| @@ -83,6 +109,8 @@ MODULE_DEVICE_TABLE(of, tsens_table); | ||||
|  static const struct thermal_zone_of_device_ops tsens_of_ops = { | ||||
| 	.get_temp = tsens_get_temp, | ||||
| 	.get_trend = tsens_get_trend, | ||||
| +	.set_trip_temp = tsens_set_trip_temp, | ||||
| +	.set_trip_activate = tsens_activate_trip_type, | ||||
|  }; | ||||
|  | ||||
|  static int tsens_register(struct tsens_device *tmdev) | ||||
| @@ -131,7 +159,7 @@ static int tsens_probe(struct platform_d | ||||
| 	if (id) | ||||
| 		data = id->data; | ||||
| 	else | ||||
| -		data = &data_8960; | ||||
| +		return -EINVAL; | ||||
|  | ||||
| 	if (data->num_sensors <= 0) { | ||||
| 		dev_err(dev, "invalid number of sensors\n"); | ||||
| @@ -146,6 +174,9 @@ static int tsens_probe(struct platform_d | ||||
| 	tmdev->dev = dev; | ||||
| 	tmdev->num_sensors = data->num_sensors; | ||||
| 	tmdev->ops = data->ops; | ||||
| + | ||||
| +	tmdev->tsens_irq = platform_get_irq(pdev, 0); | ||||
| + | ||||
| 	for (i = 0;  i < tmdev->num_sensors; i++) { | ||||
| 		if (data->hw_ids) | ||||
| 			tmdev->sensor[i].hw_id = data->hw_ids[i]; | ||||
| --- a/drivers/thermal/qcom/tsens.h | ||||
| +++ b/drivers/thermal/qcom/tsens.h | ||||
| @@ -24,9 +24,12 @@ struct tsens_device; | ||||
|  struct tsens_sensor { | ||||
| 	struct tsens_device		*tmdev; | ||||
| 	struct thermal_zone_device	*tzd; | ||||
| +	struct work_struct		notify_work; | ||||
| 	int				offset; | ||||
| 	int				id; | ||||
| 	int				hw_id; | ||||
| +	int				calib_data; | ||||
| +	int				calib_data_backup; | ||||
| 	int				slope; | ||||
| 	u32				status; | ||||
|  }; | ||||
| @@ -41,6 +44,9 @@ struct tsens_sensor { | ||||
|   * @suspend: Function to suspend the tsens device | ||||
|   * @resume: Function to resume the tsens device | ||||
|   * @get_trend: Function to get the thermal/temp trend | ||||
| + * @set_trip_temp: Function to set trip temp | ||||
| + * @get_trip_temp: Function to get trip temp | ||||
| + * @set_trip_activate: Function to activate trip points | ||||
|   */ | ||||
|  struct tsens_ops { | ||||
| 	/* mandatory callbacks */ | ||||
| @@ -53,6 +59,9 @@ struct tsens_ops { | ||||
| 	int (*suspend)(struct tsens_device *); | ||||
| 	int (*resume)(struct tsens_device *); | ||||
| 	int (*get_trend)(struct tsens_device *, int, enum thermal_trend *); | ||||
| +	int (*set_trip_temp)(void *, int, int); | ||||
| +	int (*set_trip_activate)(void *, int, | ||||
| +					enum thermal_trip_activation_mode); | ||||
|  }; | ||||
|  | ||||
|  /** | ||||
| @@ -76,11 +85,13 @@ struct tsens_context { | ||||
|  struct tsens_device { | ||||
| 	struct device			*dev; | ||||
| 	u32				num_sensors; | ||||
| +	u32				tsens_irq; | ||||
| 	struct regmap			*map; | ||||
| 	struct regmap_field		*status_field; | ||||
| 	struct tsens_context		ctx; | ||||
| 	bool				trdy; | ||||
| 	const struct tsens_ops		*ops; | ||||
| +	struct work_struct		tsens_work; | ||||
| 	struct tsens_sensor		sensor[0]; | ||||
|  }; | ||||
|  | ||||
| --- a/drivers/thermal/thermal_sysfs.c | ||||
| +++ b/drivers/thermal/thermal_sysfs.c | ||||
| @@ -115,12 +115,48 @@ | ||||
| 		return sprintf(buf, "passive\n"); | ||||
| 	case THERMAL_TRIP_ACTIVE: | ||||
| 		return sprintf(buf, "active\n"); | ||||
| +	case THERMAL_TRIP_CONFIGURABLE_HI: | ||||
| +		return sprintf(buf, "configurable_hi\n"); | ||||
| +	case THERMAL_TRIP_CONFIGURABLE_LOW: | ||||
| +		return sprintf(buf, "configurable_low\n"); | ||||
| +	case THERMAL_TRIP_CRITICAL_LOW: | ||||
| +		return sprintf(buf, "critical_low\n"); | ||||
| 	default: | ||||
| 		return sprintf(buf, "unknown\n"); | ||||
| 	} | ||||
|  } | ||||
|  | ||||
|  static ssize_t | ||||
| +trip_point_type_activate(struct device *dev, struct device_attribute *attr, | ||||
| +						const char *buf, size_t count) | ||||
| +{ | ||||
| +	struct thermal_zone_device *tz = to_thermal_zone(dev); | ||||
| +	int trip, ret; | ||||
| +	char *enabled = "enabled"; | ||||
| +	char *disabled = "disabled"; | ||||
| + | ||||
| +	if (!tz->ops->set_trip_activate) | ||||
| +		return -EPERM; | ||||
| + | ||||
| +	if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	if (!strncmp(buf, enabled, strlen(enabled))) | ||||
| +		ret = tz->ops->set_trip_activate(tz, trip, | ||||
| +				THERMAL_TRIP_ACTIVATION_ENABLED); | ||||
| +	else if (!strncmp(buf, disabled, strlen(disabled))) | ||||
| +		ret = tz->ops->set_trip_activate(tz, trip, | ||||
| +				THERMAL_TRIP_ACTIVATION_DISABLED); | ||||
| +	else | ||||
| +		ret = -EINVAL; | ||||
| + | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return count; | ||||
| +} | ||||
| + | ||||
| +static ssize_t | ||||
|  trip_point_temp_store(struct device *dev, struct device_attribute *attr, | ||||
| 		      const char *buf, size_t count) | ||||
|  { | ||||
| @@ -562,6 +598,12 @@ | ||||
| 		tz->trip_type_attrs[indx].attr.show = trip_point_type_show; | ||||
| 		attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; | ||||
|  | ||||
| +		if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS)) { | ||||
| +			tz->trip_type_attrs[indx].attr.store | ||||
| +						= trip_point_type_activate; | ||||
| +			tz->trip_type_attrs[indx].attr.attr.mode |= S_IWUSR; | ||||
| +		} | ||||
| + | ||||
| 		/* create trip temp attribute */ | ||||
| 		snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, | ||||
| 			 "trip_point_%d_temp", indx); | ||||
| --- a/include/linux/thermal.h | ||||
| +++ b/include/linux/thermal.h | ||||
| @@ -78,11 +78,19 @@ enum thermal_device_mode { | ||||
| 	THERMAL_DEVICE_ENABLED, | ||||
|  }; | ||||
|  | ||||
| +enum thermal_trip_activation_mode { | ||||
| +	THERMAL_TRIP_ACTIVATION_DISABLED = 0, | ||||
| +	THERMAL_TRIP_ACTIVATION_ENABLED, | ||||
| +}; | ||||
| + | ||||
|  enum thermal_trip_type { | ||||
| 	THERMAL_TRIP_ACTIVE = 0, | ||||
| 	THERMAL_TRIP_PASSIVE, | ||||
| 	THERMAL_TRIP_HOT, | ||||
| 	THERMAL_TRIP_CRITICAL, | ||||
| +	THERMAL_TRIP_CONFIGURABLE_HI, | ||||
| +	THERMAL_TRIP_CONFIGURABLE_LOW, | ||||
| +	THERMAL_TRIP_CRITICAL_LOW, | ||||
|  }; | ||||
|  | ||||
|  enum thermal_trend { | ||||
| @@ -120,6 +128,8 @@ struct thermal_zone_device_ops { | ||||
| 		enum thermal_trip_type *); | ||||
| 	int (*get_trip_temp) (struct thermal_zone_device *, int, int *); | ||||
| 	int (*set_trip_temp) (struct thermal_zone_device *, int, int); | ||||
| +	int (*set_trip_activate) (struct thermal_zone_device *, int, | ||||
| +					enum thermal_trip_activation_mode); | ||||
| 	int (*get_trip_hyst) (struct thermal_zone_device *, int, int *); | ||||
| 	int (*set_trip_hyst) (struct thermal_zone_device *, int, int); | ||||
| 	int (*get_crit_temp) (struct thermal_zone_device *, int *); | ||||
| @@ -363,6 +373,8 @@ struct thermal_genl_event { | ||||
|   *		   temperature. | ||||
|   * @set_trip_temp: a pointer to a function that sets the trip temperature on | ||||
|   *		   hardware. | ||||
| + * @activate_trip_type: a pointer to a function to enable/disable trip | ||||
| + *		temperature interrupts | ||||
|   */ | ||||
|  struct thermal_zone_of_device_ops { | ||||
| 	int (*get_temp)(void *, int *); | ||||
| @@ -370,6 +382,8 @@ struct thermal_zone_of_device_ops { | ||||
| 	int (*set_trips)(void *, int, int); | ||||
| 	int (*set_emul_temp)(void *, int); | ||||
| 	int (*set_trip_temp)(void *, int, int); | ||||
| +	int (*set_trip_activate)(void *, int, | ||||
| +				enum thermal_trip_activation_mode); | ||||
|  }; | ||||
|  | ||||
|  /** | ||||
| @@ -0,0 +1,93 @@ | ||||
| From d30840e2b1cf79d90392e6051b0c0b6006d29d8b Mon Sep 17 00:00:00 2001 | ||||
| From: John Crispin <john@phrozen.org> | ||||
| Date: Thu, 9 Mar 2017 09:32:40 +0100 | ||||
| Subject: [PATCH 64/69] clk: clk-rpm fixes | ||||
|  | ||||
| Signed-off-by: John Crispin <john@phrozen.org> | ||||
| --- | ||||
|  .../devicetree/bindings/clock/qcom,rpmcc.txt       |  1 + | ||||
|  drivers/clk/qcom/clk-rpm.c                         | 35 ++++++++++++++++++++++ | ||||
|  include/dt-bindings/clock/qcom,rpmcc.h             |  4 +++ | ||||
|  3 files changed, 40 insertions(+) | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt | ||||
| +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt | ||||
| @@ -13,6 +13,7 @@ Required properties : | ||||
| 			"qcom,rpmcc-msm8916", "qcom,rpmcc" | ||||
| 			"qcom,rpmcc-msm8974", "qcom,rpmcc" | ||||
| 			"qcom,rpmcc-apq8064", "qcom,rpmcc" | ||||
| +			"qcom,rpmcc-ipq806x", "qcom,rpmcc" | ||||
|  | ||||
|  - #clock-cells : shall contain 1 | ||||
|  | ||||
| --- a/drivers/clk/qcom/clk-rpm.c | ||||
| +++ b/drivers/clk/qcom/clk-rpm.c | ||||
| @@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a | ||||
|  DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); | ||||
|  DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); | ||||
|  | ||||
| +/* ipq806x */ | ||||
| +DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); | ||||
| +DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); | ||||
| +DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); | ||||
| +DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); | ||||
| +DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); | ||||
| +DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); | ||||
| +DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); | ||||
| +DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); | ||||
| + | ||||
|  static struct clk_rpm *apq8064_clks[] = { | ||||
| 	[RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, | ||||
| 	[RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, | ||||
| @@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] = | ||||
| 	[RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, | ||||
|  }; | ||||
|  | ||||
| +static struct clk_rpm *ipq806x_clks[] = { | ||||
| +	[RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, | ||||
| +	[RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, | ||||
| +	[RPM_CFPB_CLK] = &ipq806x_cfpb_clk, | ||||
| +	[RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, | ||||
| +	[RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, | ||||
| +	[RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, | ||||
| +	[RPM_EBI1_CLK] = &ipq806x_ebi1_clk, | ||||
| +	[RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, | ||||
| +	[RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, | ||||
| +	[RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, | ||||
| +	[RPM_SFPB_CLK] = &ipq806x_sfpb_clk, | ||||
| +	[RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, | ||||
| +	[RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, | ||||
| +	[RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, | ||||
| +	[RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, | ||||
| +	[RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, | ||||
| +}; | ||||
| + | ||||
|  static const struct rpm_clk_desc rpm_clk_apq8064 = { | ||||
| 	.clks = apq8064_clks, | ||||
| 	.num_clks = ARRAY_SIZE(apq8064_clks), | ||||
|  }; | ||||
|  | ||||
| +static const struct rpm_clk_desc rpm_clk_ipq806x = { | ||||
| +	.clks = ipq806x_clks, | ||||
| +	.num_clks = ARRAY_SIZE(ipq806x_clks), | ||||
| +}; | ||||
| + | ||||
|  static const struct of_device_id rpm_clk_match_table[] = { | ||||
| 	{ .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, | ||||
| +	{ .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, | ||||
| 	{ } | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, rpm_clk_match_table); | ||||
| --- a/include/dt-bindings/clock/qcom,rpmcc.h | ||||
| +++ b/include/dt-bindings/clock/qcom,rpmcc.h | ||||
| @@ -37,6 +37,10 @@ | ||||
|  #define RPM_SYS_FABRIC_A_CLK			19 | ||||
|  #define RPM_SFPB_CLK				20 | ||||
|  #define RPM_SFPB_A_CLK				21 | ||||
| +#define RPM_NSS_FABRIC_0_CLK				22 | ||||
| +#define RPM_NSS_FABRIC_0_A_CLK				23 | ||||
| +#define RPM_NSS_FABRIC_1_CLK				24 | ||||
| +#define RPM_NSS_FABRIC_1_A_CLK				25 | ||||
|  | ||||
|  /* SMD RPM clocks */ | ||||
|  #define RPM_SMD_XO_CLK_SRC				0 | ||||
| @@ -0,0 +1,21 @@ | ||||
| From 4d8e29642661397a339ac3485f212c6360445421 Mon Sep 17 00:00:00 2001 | ||||
| From: John Crispin <john@phrozen.org> | ||||
| Date: Thu, 9 Mar 2017 09:33:32 +0100 | ||||
| Subject: [PATCH 65/69] arm: override compiler flags | ||||
|  | ||||
| Signed-off-by: John Crispin <john@phrozen.org> | ||||
| --- | ||||
|  arch/arm/Makefile | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| --- a/arch/arm/Makefile | ||||
| +++ b/arch/arm/Makefile | ||||
| @@ -67,7 +67,7 @@ KBUILD_CFLAGS	+= $(call cc-option,-fno-i | ||||
|  # macro, but instead defines a whole series of macros which makes | ||||
|  # testing for a specific architecture or later rather impossible. | ||||
|  arch-$(CONFIG_CPU_32v7M)	=-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m | ||||
| -arch-$(CONFIG_CPU_32v7)		=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) | ||||
| +arch-$(CONFIG_CPU_32v7)		=-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15 | ||||
|  arch-$(CONFIG_CPU_32v6)		=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) | ||||
|  # Only override the compiler option if ARMv6. The ARMv6K extensions are | ||||
|  # always available in ARMv7 | ||||
| @@ -0,0 +1,166 @@ | ||||
| From a37b0c9113647b2120cf1a18cfc46afdb3f1fccc Mon Sep 17 00:00:00 2001 | ||||
| From: John Crispin <blogic@openwrt.org> | ||||
| Date: Tue, 12 Aug 2014 20:49:27 +0200 | ||||
| Subject: [PATCH 66/69] GPIO: add named gpio exports | ||||
|  | ||||
| Signed-off-by: John Crispin <blogic@openwrt.org> | ||||
| --- | ||||
|  drivers/gpio/gpiolib-of.c     | 68 +++++++++++++++++++++++++++++++++++++++++++ | ||||
|  drivers/gpio/gpiolib-sysfs.c  | 10 ++++++- | ||||
|  include/asm-generic/gpio.h    |  6 ++++ | ||||
|  include/linux/gpio/consumer.h |  8 +++++ | ||||
|  4 files changed, 91 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/gpio/gpiolib-of.c | ||||
| +++ b/drivers/gpio/gpiolib-of.c | ||||
| @@ -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" | ||||
|  | ||||
| @@ -506,3 +508,69 @@ void of_gpiochip_remove(struct gpio_chip | ||||
| 	gpiochip_remove_pin_ranges(chip); | ||||
| 	of_node_put(chip->of_node); | ||||
|  } | ||||
| + | ||||
| +static struct of_device_id gpio_export_ids[] = { | ||||
| +	{ .compatible = "gpio-export" }, | ||||
| +	{ /* sentinel */ } | ||||
| +}; | ||||
| + | ||||
| +static int __init 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 (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), | ||||
| +	}, | ||||
| +}; | ||||
| + | ||||
| +static int __init of_gpio_export_init(void) | ||||
| +{ | ||||
| +	return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe); | ||||
| +} | ||||
| +device_initcall(of_gpio_export_init); | ||||
| --- a/drivers/gpio/gpiolib-sysfs.c | ||||
| +++ b/drivers/gpio/gpiolib-sysfs.c | ||||
| @@ -553,7 +553,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; | ||||
| @@ -615,6 +615,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, | ||||
| @@ -636,6 +638,12 @@ err_unlock: | ||||
| 	gpiod_dbg(desc, "%s: status %d\n", __func__, status); | ||||
| 	return status; | ||||
|  } | ||||
| +EXPORT_SYMBOL_GPL(__gpiod_export); | ||||
| + | ||||
| +int gpiod_export(struct gpio_desc *desc, bool direction_may_change) | ||||
| +{ | ||||
| +	return __gpiod_export(desc, direction_may_change, NULL); | ||||
| +} | ||||
|  EXPORT_SYMBOL_GPL(gpiod_export); | ||||
|  | ||||
|  static int match_export(struct device *dev, const void *desc) | ||||
| --- a/include/asm-generic/gpio.h | ||||
| +++ b/include/asm-generic/gpio.h | ||||
| @@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g | ||||
| 	return gpiod_export(gpio_to_desc(gpio), direction_may_change); | ||||
|  } | ||||
|  | ||||
| +int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); | ||||
| +static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) | ||||
| +{ | ||||
| +	return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); | ||||
| +} | ||||
| + | ||||
|  static inline int gpio_export_link(struct device *dev, const char *name, | ||||
| 				   unsigned gpio) | ||||
|  { | ||||
| --- a/include/linux/gpio/consumer.h | ||||
| +++ b/include/linux/gpio/consumer.h | ||||
| @@ -451,6 +451,7 @@ static inline struct gpio_desc *devm_get | ||||
|  | ||||
|  #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); | ||||
| @@ -458,6 +459,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) | ||||
|  { | ||||
| @@ -0,0 +1,189 @@ | ||||
| From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001 | ||||
| From: Adrian Panella <ianchi74@outlook.com> | ||||
| Date: Thu, 9 Mar 2017 09:37:17 +0100 | ||||
| Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments | ||||
|  | ||||
| The command-line arguments provided by the boot loader will be | ||||
| appended to a new device tree property: bootloader-args. | ||||
| If there is a property "append-rootblock" in DT under /chosen | ||||
| and a root= option in bootloaders command line it will be parsed | ||||
| and added to DT bootargs with the form: <append-rootblock>XX. | ||||
| Only command line ATAG will be processed, the rest of the ATAGs | ||||
| sent by bootloader will be ignored. | ||||
| This is usefull in dual boot systems, to get the current root partition | ||||
| without afecting the rest of the system. | ||||
|  | ||||
| Signed-off-by: Adrian Panella <ianchi74@outlook.com> | ||||
| --- | ||||
|  arch/arm/Kconfig                        | 11 +++++ | ||||
|  arch/arm/boot/compressed/atags_to_fdt.c | 72 ++++++++++++++++++++++++++++++++- | ||||
|  init/main.c                             | 16 ++++++++ | ||||
|  3 files changed, 98 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/arch/arm/Kconfig | ||||
| +++ b/arch/arm/Kconfig | ||||
| @@ -1938,6 +1938,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN | ||||
| 	  The command-line arguments provided by the boot loader will be | ||||
| 	  appended to the the device tree bootargs property. | ||||
|  | ||||
| +config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE | ||||
| +	bool "Append rootblock parsing bootloader's kernel arguments" | ||||
| +	help | ||||
| +	  The command-line arguments provided by the boot loader will be | ||||
| +	  appended to a new device tree property: bootloader-args. | ||||
| +	  If there is a property "append-rootblock" in DT under /chosen | ||||
| +	  and a root= option in bootloaders command line it will be parsed | ||||
| +	  and added to DT bootargs with the form: <append-rootblock>XX. | ||||
| +	  Only command line ATAG will be processed, the rest of the ATAGs | ||||
| +	  sent by bootloader will be ignored. | ||||
| + | ||||
|  endchoice | ||||
|  | ||||
|  config CMDLINE | ||||
| --- a/arch/arm/boot/compressed/atags_to_fdt.c | ||||
| +++ b/arch/arm/boot/compressed/atags_to_fdt.c | ||||
| @@ -4,6 +4,8 @@ | ||||
|  | ||||
|  #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) | ||||
|  #define do_extend_cmdline 1 | ||||
| +#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) | ||||
| +#define do_extend_cmdline 1 | ||||
|  #else | ||||
|  #define do_extend_cmdline 0 | ||||
|  #endif | ||||
| @@ -67,6 +69,59 @@ static uint32_t get_cell_size(const void | ||||
| 	return cell_size; | ||||
|  } | ||||
|  | ||||
| +#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) | ||||
| + | ||||
| +static char *append_rootblock(char *dest, const char *str, int len, void *fdt) | ||||
| +{ | ||||
| +	char *ptr, *end; | ||||
| +	char *root="root="; | ||||
| +	int i, l; | ||||
| +	const char *rootblock; | ||||
| + | ||||
| +	//ARM doesn't have __HAVE_ARCH_STRSTR, so search manually | ||||
| +	ptr = str - 1; | ||||
| + | ||||
| +	do { | ||||
| +		//first find an 'r' at the begining or after a space | ||||
| +		do { | ||||
| +			ptr++; | ||||
| +			ptr = strchr(ptr, 'r'); | ||||
| +			if(!ptr) return dest; | ||||
| + | ||||
| +		} while (ptr != str && *(ptr-1) != ' '); | ||||
| + | ||||
| +		//then check for the rest | ||||
| +		for(i = 1; i <= 4; i++) | ||||
| +			if(*(ptr+i) != *(root+i)) break; | ||||
| + | ||||
| +	} while (i != 5); | ||||
| + | ||||
| +	end = strchr(ptr, ' '); | ||||
| +	end = end ? (end - 1) : (strchr(ptr, 0) - 1); | ||||
| + | ||||
| +	//find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX ) | ||||
| +	for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++); | ||||
| +	ptr = end + 1; | ||||
| + | ||||
| +	/* if append-rootblock property is set use it to append to command line */ | ||||
| +	rootblock = getprop(fdt, "/chosen", "append-rootblock", &l); | ||||
| +	if(rootblock != NULL) { | ||||
| +		if(*dest != ' ') { | ||||
| +			*dest = ' '; | ||||
| +			dest++; | ||||
| +			len++; | ||||
| +		} | ||||
| +		if (len + l + i <= COMMAND_LINE_SIZE) { | ||||
| +			memcpy(dest, rootblock, l); | ||||
| +			dest += l - 1; | ||||
| +			memcpy(dest, ptr, i); | ||||
| +			dest += i; | ||||
| +		} | ||||
| +	} | ||||
| +	return dest; | ||||
| +} | ||||
| +#endif | ||||
| + | ||||
|  static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) | ||||
|  { | ||||
| 	char cmdline[COMMAND_LINE_SIZE]; | ||||
| @@ -86,12 +141,21 @@ static void merge_fdt_bootargs(void *fdt | ||||
|  | ||||
| 	/* and append the ATAG_CMDLINE */ | ||||
| 	if (fdt_cmdline) { | ||||
| + | ||||
| +#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) | ||||
| +		//save original bootloader args | ||||
| +		//and append ubi.mtd with root partition number to current cmdline | ||||
| +		setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline); | ||||
| +		ptr = append_rootblock(ptr, fdt_cmdline, len, fdt); | ||||
| + | ||||
| +#else | ||||
| 		len = strlen(fdt_cmdline); | ||||
| 		if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { | ||||
| 			*ptr++ = ' '; | ||||
| 			memcpy(ptr, fdt_cmdline, len); | ||||
| 			ptr += len; | ||||
| 		} | ||||
| +#endif | ||||
| 	} | ||||
| 	*ptr = '\0'; | ||||
|  | ||||
| @@ -148,7 +212,9 @@ int atags_to_fdt(void *atag_list, void * | ||||
| 			else | ||||
| 				setprop_string(fdt, "/chosen", "bootargs", | ||||
| 					       atag->u.cmdline.cmdline); | ||||
| -		} else if (atag->hdr.tag == ATAG_MEM) { | ||||
| +		} | ||||
| +#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE | ||||
| +		else if (atag->hdr.tag == ATAG_MEM) { | ||||
| 			if (memcount >= sizeof(mem_reg_property)/4) | ||||
| 				continue; | ||||
| 			if (!atag->u.mem.size) | ||||
| @@ -187,6 +253,10 @@ int atags_to_fdt(void *atag_list, void * | ||||
| 		setprop(fdt, "/memory", "reg", mem_reg_property, | ||||
| 			4 * memcount * memsize); | ||||
| 	} | ||||
| +#else | ||||
| + | ||||
| +	} | ||||
| +#endif | ||||
|  | ||||
| 	return fdt_pack(fdt); | ||||
|  } | ||||
| --- a/init/main.c | ||||
| +++ b/init/main.c | ||||
| @@ -95,6 +95,10 @@ | ||||
|  #include <asm/sections.h> | ||||
|  #include <asm/cacheflush.h> | ||||
|  | ||||
| +#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) | ||||
| +#include <linux/of.h> | ||||
| +#endif | ||||
| + | ||||
|  static int kernel_init(void *); | ||||
|  | ||||
|  extern void init_IRQ(void); | ||||
| @@ -574,6 +578,18 @@ asmlinkage __visible void __init start_k | ||||
| 	page_alloc_init(); | ||||
|  | ||||
| 	pr_notice("Kernel command line: %s\n", boot_command_line); | ||||
| + | ||||
| +#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) | ||||
| +	//Show bootloader's original command line for reference | ||||
| +	if(of_chosen) { | ||||
| +		const char *prop = of_get_property(of_chosen, "bootloader-args", NULL); | ||||
| +		if(prop) | ||||
| +			pr_notice("Bootloader command line (ignored): %s\n", prop); | ||||
| +		else | ||||
| +			pr_notice("Bootloader command line not present\n"); | ||||
| +	} | ||||
| +#endif | ||||
| + | ||||
| 	parse_early_param(); | ||||
| 	after_dashes = parse_args("Booting kernel", | ||||
| 				  static_command_line, __start___param, | ||||
| @@ -0,0 +1,28 @@ | ||||
| From 8f68331e14dff9a101f2d0e1d6bec84a031f27ee Mon Sep 17 00:00:00 2001 | ||||
| From: John Crispin <john@phrozen.org> | ||||
| Date: Thu, 9 Mar 2017 11:03:18 +0100 | ||||
| Subject: [PATCH 69/69] arm: boot: add dts files | ||||
|  | ||||
| Signed-off-by: John Crispin <john@phrozen.org> | ||||
| --- | ||||
|  arch/arm/boot/dts/Makefile | 8 ++++++++ | ||||
|  1 file changed, 8 insertions(+) | ||||
|  | ||||
| --- a/arch/arm/boot/dts/Makefile | ||||
| +++ b/arch/arm/boot/dts/Makefile | ||||
| @@ -699,6 +699,15 @@ dtb-$(CONFIG_ARCH_QCOM) += \ | ||||
| 	qcom-apq8084-mtp.dtb \ | ||||
| 	qcom-ipq4019-ap.dk01.1-c1.dtb \ | ||||
| 	qcom-ipq8064-ap148.dtb \ | ||||
| +	qcom-ipq8064-c2600.dtb \ | ||||
| +	qcom-ipq8064-d7800.dtb \ | ||||
| +	qcom-ipq8064-db149.dtb \ | ||||
| +	qcom-ipq8064-ea8500.dtb \ | ||||
| +	qcom-ipq8064-r7500.dtb \ | ||||
| +	qcom-ipq8064-r7500v2.dtb \ | ||||
| +	qcom-ipq8064-wpq864.dtb \ | ||||
| +	qcom-ipq8065-nbg6817.dtb \ | ||||
| +	qcom-ipq8065-r7800.dtb \ | ||||
| 	qcom-msm8660-surf.dtb \ | ||||
| 	qcom-msm8960-cdp.dtb \ | ||||
| 	qcom-msm8974-lge-nexus5-hammerhead.dtb \ | ||||
| @@ -0,0 +1,16 @@ | ||||
| Check for SCM availability before attempting to use SPM | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
|  | ||||
| --- a/drivers/soc/qcom/spm.c | ||||
| +++ b/drivers/soc/qcom/spm.c | ||||
| @@ -219,6 +219,9 @@ static int __init qcom_cpuidle_init(stru | ||||
| 	cpumask_t mask; | ||||
| 	bool use_scm_power_down = false; | ||||
|  | ||||
| +	if (!qcom_scm_is_available()) | ||||
| +		return -EPROBE_DEFER; | ||||
| + | ||||
| 	for (i = 0; ; i++) { | ||||
| 		state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||||
| 		if (!state_node) | ||||
| @@ -0,0 +1,95 @@ | ||||
| From 86655aa14304ca88a8ce8847276147dbc1a83238 Mon Sep 17 00:00:00 2001 | ||||
| From: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| Date: Tue, 19 Jul 2016 18:44:49 +0530 | ||||
| Subject: PCI: qcom: Fixed IPQ806x specific clocks | ||||
|  | ||||
| Change-Id: I488e1bc707d6a22b37a338f41935e3922009ba5e | ||||
| Signed-off-by: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 38 +++++++++++++++++++++++++++++++++----- | ||||
|  1 file changed, 33 insertions(+), 5 deletions(-) | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -91,6 +91,8 @@ | ||||
| 	struct clk *iface_clk; | ||||
| 	struct clk *core_clk; | ||||
| 	struct clk *phy_clk; | ||||
| +	struct clk *aux_clk; | ||||
| +	struct clk *ref_clk; | ||||
| 	struct reset_control *pci_reset; | ||||
| 	struct reset_control *axi_reset; | ||||
| 	struct reset_control *ahb_reset; | ||||
| @@ -249,6 +251,14 @@ | ||||
| 	if (IS_ERR(res->phy_clk)) | ||||
| 		return PTR_ERR(res->phy_clk); | ||||
|  | ||||
| +	res->aux_clk = devm_clk_get(dev, "aux"); | ||||
| +	if (IS_ERR(res->aux_clk)) | ||||
| +		return PTR_ERR(res->aux_clk); | ||||
| + | ||||
| +	res->ref_clk = devm_clk_get(dev, "ref"); | ||||
| +	if (IS_ERR(res->ref_clk)) | ||||
| +		return PTR_ERR(res->ref_clk); | ||||
| + | ||||
| 	res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); | ||||
| 	if (IS_ERR(res->pci_reset)) | ||||
| 		return PTR_ERR(res->pci_reset); | ||||
| @@ -281,6 +291,8 @@ | ||||
| 	clk_disable_unprepare(res->iface_clk); | ||||
| 	clk_disable_unprepare(res->core_clk); | ||||
| 	clk_disable_unprepare(res->phy_clk); | ||||
| +	clk_disable_unprepare(res->aux_clk); | ||||
| +	clk_disable_unprepare(res->ref_clk); | ||||
| 	regulator_disable(res->vdda); | ||||
| 	regulator_disable(res->vdda_phy); | ||||
| 	regulator_disable(res->vdda_refclk); | ||||
| @@ -324,16 +336,28 @@ | ||||
| 		goto err_assert_ahb; | ||||
| 	} | ||||
|  | ||||
| +	ret = clk_prepare_enable(res->core_clk); | ||||
| +	if (ret) { | ||||
| +		dev_err(dev, "cannot prepare/enable core clock\n"); | ||||
| +		goto err_clk_core; | ||||
| +	} | ||||
| + | ||||
| 	ret = clk_prepare_enable(res->phy_clk); | ||||
| 	if (ret) { | ||||
| 		dev_err(dev, "cannot prepare/enable phy clock\n"); | ||||
| 		goto err_clk_phy; | ||||
| 	} | ||||
|  | ||||
| -	ret = clk_prepare_enable(res->core_clk); | ||||
| +	ret = clk_prepare_enable(res->aux_clk); | ||||
| 	if (ret) { | ||||
| -		dev_err(dev, "cannot prepare/enable core clock\n"); | ||||
| -		goto err_clk_core; | ||||
| +		dev_err(dev, "cannot prepare/enable aux clock\n"); | ||||
| +		goto err_clk_aux; | ||||
| +	} | ||||
| + | ||||
| +	ret = clk_prepare_enable(res->ref_clk); | ||||
| +	if (ret) { | ||||
| +		dev_err(dev, "cannot prepare/enable ref clock\n"); | ||||
| +		goto err_clk_ref; | ||||
| 	} | ||||
|  | ||||
| 	ret = reset_control_deassert(res->ahb_reset); | ||||
| @@ -389,10 +413,14 @@ | ||||
| 	return 0; | ||||
|  | ||||
|  err_deassert_ahb: | ||||
| -	clk_disable_unprepare(res->core_clk); | ||||
| -err_clk_core: | ||||
| +	clk_disable_unprepare(res->ref_clk); | ||||
| +err_clk_ref: | ||||
| +	clk_disable_unprepare(res->aux_clk); | ||||
| +err_clk_aux: | ||||
| 	clk_disable_unprepare(res->phy_clk); | ||||
|  err_clk_phy: | ||||
| +	clk_disable_unprepare(res->core_clk); | ||||
| +err_clk_core: | ||||
| 	clk_disable_unprepare(res->iface_clk); | ||||
|  err_assert_ahb: | ||||
| 	regulator_disable(res->vdda_phy); | ||||
| @@ -0,0 +1,85 @@ | ||||
| From 490d103232287eb51c92c49a4ef8865fd0a9d59e Mon Sep 17 00:00:00 2001 | ||||
| From: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| Date: Tue, 19 Jul 2016 18:58:18 +0530 | ||||
| Subject: PCI: qcom: Fixed IPQ806x PCIE reset changes | ||||
|  | ||||
| Change-Id: Ia6590e960b9754b1e8b7a51f318788cd63e9e321 | ||||
| Signed-off-by: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 24 +++++++++++++++++++----- | ||||
|  1 file changed, 19 insertions(+), 5 deletions(-) | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -98,6 +98,7 @@ | ||||
|  	struct reset_control *ahb_reset; | ||||
|  	struct reset_control *por_reset; | ||||
|  	struct reset_control *phy_reset; | ||||
| +	struct reset_control *ext_reset; | ||||
|  	struct regulator *vdda; | ||||
|  	struct regulator *vdda_phy; | ||||
|  	struct regulator *vdda_refclk; | ||||
| @@ -275,6 +276,10 @@ | ||||
|  	if (IS_ERR(res->por_reset)) | ||||
|  		return PTR_ERR(res->por_reset); | ||||
|  | ||||
| +	res->ext_reset = devm_reset_control_get(dev, "ext"); | ||||
| +	if (IS_ERR(res->ext_reset)) | ||||
| +		return PTR_ERR(res->ext_reset); | ||||
| + | ||||
|  	res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); | ||||
|  	return PTR_ERR_OR_ZERO(res->phy_reset); | ||||
|  } | ||||
| @@ -288,6 +293,7 @@ | ||||
|  	reset_control_assert(res->ahb_reset); | ||||
|  	reset_control_assert(res->por_reset); | ||||
|  	reset_control_assert(res->pci_reset); | ||||
| +	reset_control_assert(res->ext_reset); | ||||
|  	clk_disable_unprepare(res->iface_clk); | ||||
|  	clk_disable_unprepare(res->core_clk); | ||||
|  	clk_disable_unprepare(res->phy_clk); | ||||
| @@ -306,6 +312,12 @@ | ||||
|  	u32 val; | ||||
|  	int ret; | ||||
|  | ||||
| +	ret = reset_control_assert(res->ahb_reset); | ||||
| +	if (ret) { | ||||
| +		dev_err(dev, "cannot assert ahb reset\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
|  	ret = regulator_enable(res->vdda); | ||||
|  	if (ret) { | ||||
|  		dev_err(dev, "cannot enable vdda regulator\n"); | ||||
| @@ -324,16 +336,16 @@ | ||||
|  		goto err_vdda_phy; | ||||
|  	} | ||||
|  | ||||
| -	ret = reset_control_assert(res->ahb_reset); | ||||
| +	ret = reset_control_deassert(res->ext_reset); | ||||
|  	if (ret) { | ||||
| -		dev_err(dev, "cannot assert ahb reset\n"); | ||||
| -		goto err_assert_ahb; | ||||
| +		dev_err(dev, "cannot assert ext reset\n"); | ||||
| +		goto err_reset_ext; | ||||
|  	} | ||||
|  | ||||
|  	ret = clk_prepare_enable(res->iface_clk); | ||||
|  	if (ret) { | ||||
|  		dev_err(dev, "cannot prepare/enable iface clock\n"); | ||||
| -		goto err_assert_ahb; | ||||
| +		goto err_iface; | ||||
|  	} | ||||
|  | ||||
|  	ret = clk_prepare_enable(res->core_clk); | ||||
| @@ -422,7 +434,9 @@ | ||||
|  	clk_disable_unprepare(res->core_clk); | ||||
|  err_clk_core: | ||||
|  	clk_disable_unprepare(res->iface_clk); | ||||
| -err_assert_ahb: | ||||
| +err_iface: | ||||
| +	reset_control_assert(res->ext_reset); | ||||
| +err_reset_ext: | ||||
|  	regulator_disable(res->vdda_phy); | ||||
|  err_vdda_phy: | ||||
|  	regulator_disable(res->vdda_refclk); | ||||
| @@ -0,0 +1,126 @@ | ||||
| From eddd13215d0f2b549ebc5f0e8796d5b1231f90a0 Mon Sep 17 00:00:00 2001 | ||||
| From: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| Date: Tue, 19 Jul 2016 19:58:22 +0530 | ||||
| Subject: PCI: qcom: Fixed IPQ806x PCIE init changes | ||||
|  | ||||
| Change-Id: Ic319b1aec27a47809284759f8fcb6a8815b7cf7e | ||||
| Signed-off-by: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 62 +++++++++++++++++++++++++++++++++++++------- | ||||
|  1 file changed, 53 insertions(+), 9 deletions(-) | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -52,7 +52,13 @@ | ||||
|  #define PCIE_CAP_CPL_TIMEOUT_DISABLE		0x10 | ||||
|  | ||||
|  #define PCIE20_PARF_PHY_CTRL			0x40 | ||||
| +#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK	(0x1f << 16) | ||||
| +#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x)		(x << 16) | ||||
| + | ||||
|  #define PCIE20_PARF_PHY_REFCLK			0x4C | ||||
| +#define REF_SSP_EN				BIT(16) | ||||
| +#define REF_USE_PAD				BIT(12) | ||||
| + | ||||
|  #define PCIE20_PARF_DBI_BASE_ADDR		0x168 | ||||
|  #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE		0x16C | ||||
|  #define PCIE20_PARF_MHI_CLOCK_RESET_CTRL	0x174 | ||||
| @@ -83,6 +89,18 @@ | ||||
|  #define DBI_RO_WR_EN				1 | ||||
|  | ||||
|  #define PERST_DELAY_US				1000 | ||||
| +/* PARF registers */ | ||||
| +#define PCIE20_PARF_PCS_DEEMPH			0x34 | ||||
| +#define PCS_DEEMPH_TX_DEEMPH_GEN1(x)		(x << 16) | ||||
| +#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x)	(x << 8) | ||||
| +#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x)	(x << 0) | ||||
| + | ||||
| +#define PCIE20_PARF_PCS_SWING			0x38 | ||||
| +#define PCS_SWING_TX_SWING_FULL(x)		(x << 8) | ||||
| +#define PCS_SWING_TX_SWING_LOW(x)		(x << 0) | ||||
| + | ||||
| +#define PCIE20_PARF_CONFIG_BITS			0x50 | ||||
| +#define PHY_RX0_EQ(x)				(x << 24) | ||||
|  | ||||
|  #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE	0x358 | ||||
|  #define SLV_ADDR_SPACE_SZ			0x10000000 | ||||
| @@ -102,6 +120,7 @@ | ||||
|  	struct regulator *vdda; | ||||
|  	struct regulator *vdda_phy; | ||||
|  	struct regulator *vdda_refclk; | ||||
| +	uint8_t phy_tx0_term_offset; | ||||
|  }; | ||||
|  | ||||
|  struct qcom_pcie_resources_1_0_0 { | ||||
| @@ -179,6 +198,16 @@ | ||||
|  | ||||
|  #define to_qcom_pcie(x)		dev_get_drvdata((x)->dev) | ||||
|  | ||||
| +static inline void | ||||
| +writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask) | ||||
| +{ | ||||
| +	u32 val = readl(addr); | ||||
| + | ||||
| +	val &= ~clear_mask; | ||||
| +	val |= set_mask; | ||||
| +	writel(val, addr); | ||||
| +} | ||||
| + | ||||
|  static void qcom_ep_reset_assert(struct qcom_pcie *pcie) | ||||
|  { | ||||
|  	gpiod_set_value_cansleep(pcie->reset, 1); | ||||
| @@ -280,6 +309,10 @@ | ||||
|  	if (IS_ERR(res->ext_reset)) | ||||
|  		return PTR_ERR(res->ext_reset); | ||||
|  | ||||
| +	if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset", | ||||
| +				&res->phy_tx0_term_offset)) | ||||
| +		res->phy_tx0_term_offset = 0; | ||||
| + | ||||
|  	res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); | ||||
|  	return PTR_ERR_OR_ZERO(res->phy_reset); | ||||
|  } | ||||
| @@ -309,7 +342,6 @@ | ||||
|  	struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; | ||||
|  	struct dw_pcie *pci = pcie->pci; | ||||
|  	struct device *dev = pci->dev; | ||||
| -	u32 val; | ||||
|  	int ret; | ||||
|  | ||||
|  	ret = reset_control_assert(res->ahb_reset); | ||||
| @@ -378,15 +410,26 @@ | ||||
|  		goto err_deassert_ahb; | ||||
|  	} | ||||
|  | ||||
| -	/* enable PCIe clocks and resets */ | ||||
| -	val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); | ||||
| -	val &= ~BIT(0); | ||||
| -	writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); | ||||
| - | ||||
| -	/* enable external reference clock */ | ||||
| -	val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); | ||||
| -	val |= BIT(16); | ||||
| -	writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); | ||||
| +	writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); | ||||
| + | ||||
| +	/* Set Tx termination offset */ | ||||
| +	writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, | ||||
| +		      PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK, | ||||
| +		      PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset)); | ||||
| + | ||||
| +	/* PARF programming */ | ||||
| +	writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) | | ||||
| +	       PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) | | ||||
| +	       PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22), | ||||
| +	       pcie->parf + PCIE20_PARF_PCS_DEEMPH); | ||||
| +	writel(PCS_SWING_TX_SWING_FULL(0x78) | | ||||
| +	       PCS_SWING_TX_SWING_LOW(0x78), | ||||
| +	       pcie->parf + PCIE20_PARF_PCS_SWING); | ||||
| +	writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS); | ||||
| + | ||||
| +	/* Enable reference clock */ | ||||
| +	writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, | ||||
| +		      REF_USE_PAD, REF_SSP_EN); | ||||
|  | ||||
|  	ret = reset_control_deassert(res->phy_reset); | ||||
|  	if (ret) { | ||||
| @@ -0,0 +1,114 @@ | ||||
| From d27c303e828d7e42f339a459d2abfe30c51698e9 Mon Sep 17 00:00:00 2001 | ||||
| From: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| Date: Tue, 26 Jul 2016 12:28:31 +0530 | ||||
| Subject: PCI: qcom: Programming the PCIE iATU for IPQ806x | ||||
|  | ||||
| Resolved PCIE EP detection errors caused due to missing iATU programming. | ||||
|  | ||||
| Change-Id: Ie95c0f8cb940abc0192a8a3c4e825ddba54b72fe | ||||
| Signed-off-by: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 77 insertions(+) | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -83,6 +83,30 @@ | ||||
|  #define PCIE20_CAP_LINK_1			(PCIE20_CAP + 0x14) | ||||
|  #define PCIE_CAP_LINK1_VAL			0x2FD7F | ||||
|  | ||||
| +#define PCIE20_CAP_LINKCTRLSTATUS		(PCIE20_CAP + 0x10) | ||||
| + | ||||
| +#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0		0x818 | ||||
| +#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1		0x81c | ||||
| + | ||||
| +#define PCIE20_PLR_IATU_VIEWPORT		0x900 | ||||
| +#define PCIE20_PLR_IATU_REGION_OUTBOUND		(0x0 << 31) | ||||
| +#define PCIE20_PLR_IATU_REGION_INDEX(x)		(x << 0) | ||||
| + | ||||
| +#define PCIE20_PLR_IATU_CTRL1			0x904 | ||||
| +#define PCIE20_PLR_IATU_TYPE_CFG0		(0x4 << 0) | ||||
| +#define PCIE20_PLR_IATU_TYPE_MEM		(0x0 << 0) | ||||
| + | ||||
| +#define PCIE20_PLR_IATU_CTRL2			0x908 | ||||
| +#define PCIE20_PLR_IATU_ENABLE			BIT(31) | ||||
| + | ||||
| +#define PCIE20_PLR_IATU_LBAR			0x90C | ||||
| +#define PCIE20_PLR_IATU_UBAR			0x910 | ||||
| +#define PCIE20_PLR_IATU_LAR			0x914 | ||||
| +#define PCIE20_PLR_IATU_LTAR			0x918 | ||||
| +#define PCIE20_PLR_IATU_UTAR			0x91c | ||||
| + | ||||
| +#define MSM_PCIE_DEV_CFG_ADDR			0x01000000 | ||||
| + | ||||
|  #define PCIE20_PARF_Q2A_FLUSH			0x1AC | ||||
|  | ||||
|  #define PCIE20_MISC_CONTROL_1_REG		0x8BC | ||||
| @@ -251,6 +275,57 @@ | ||||
| 	writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); | ||||
|  } | ||||
|  | ||||
| +static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev) | ||||
| +{ | ||||
| +	struct pcie_port *pp = &pcie->pci->pp; | ||||
| + | ||||
| +	/* | ||||
| +	 * program and enable address translation region 0 (device config | ||||
| +	 * address space); region type config; | ||||
| +	 * axi config address range to device config address range | ||||
| +	 */ | ||||
| +	writel(PCIE20_PLR_IATU_REGION_OUTBOUND | | ||||
| +	       PCIE20_PLR_IATU_REGION_INDEX(0), | ||||
| +	       pcie->pci->dbi_base + PCIE20_PLR_IATU_VIEWPORT); | ||||
| + | ||||
| +	writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->pci->dbi_base + PCIE20_PLR_IATU_CTRL1); | ||||
| +	writel(PCIE20_PLR_IATU_ENABLE, pcie->pci->dbi_base + PCIE20_PLR_IATU_CTRL2); | ||||
| +	writel(pp->cfg0_base, pcie->pci->dbi_base + PCIE20_PLR_IATU_LBAR); | ||||
| +	writel((pp->cfg0_base >> 32), pcie->pci->dbi_base + PCIE20_PLR_IATU_UBAR); | ||||
| +	writel((pp->cfg0_base + pp->cfg0_size - 1), | ||||
| +	       pcie->pci->dbi_base + PCIE20_PLR_IATU_LAR); | ||||
| +	writel(busdev, pcie->pci->dbi_base + PCIE20_PLR_IATU_LTAR); | ||||
| +	writel(0, pcie->pci->dbi_base + PCIE20_PLR_IATU_UTAR); | ||||
| +} | ||||
| + | ||||
| +static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie) | ||||
| +{ | ||||
| +	struct pcie_port *pp = &pcie->pci->pp; | ||||
| + | ||||
| +	/* | ||||
| +	 * program and enable address translation region 2 (device resource | ||||
| +	 * address space); region type memory; | ||||
| +	 * axi device bar address range to device bar address range | ||||
| +	 */ | ||||
| +	writel(PCIE20_PLR_IATU_REGION_OUTBOUND | | ||||
| +	       PCIE20_PLR_IATU_REGION_INDEX(2), | ||||
| +	       pcie->pci->dbi_base + PCIE20_PLR_IATU_VIEWPORT); | ||||
| + | ||||
| +	writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->pci->dbi_base + PCIE20_PLR_IATU_CTRL1); | ||||
| +	writel(PCIE20_PLR_IATU_ENABLE, pcie->pci->dbi_base + PCIE20_PLR_IATU_CTRL2); | ||||
| +	writel(pp->mem_base, pcie->pci->dbi_base + PCIE20_PLR_IATU_LBAR); | ||||
| +	writel((pp->mem_base >> 32), pcie->pci->dbi_base + PCIE20_PLR_IATU_UBAR); | ||||
| +	writel(pp->mem_base + pp->mem_size - 1, | ||||
| +	       pcie->pci->dbi_base + PCIE20_PLR_IATU_LAR); | ||||
| +	writel(pp->mem_bus_addr, pcie->pci->dbi_base + PCIE20_PLR_IATU_LTAR); | ||||
| +	writel(upper_32_bits(pp->mem_bus_addr), | ||||
| +	       pcie->pci->dbi_base + PCIE20_PLR_IATU_UTAR); | ||||
| + | ||||
| +	/* 256B PCIE buffer setting */ | ||||
| +	writel(0x1, pcie->pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); | ||||
| +	writel(0x1, pcie->pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); | ||||
| +} | ||||
| + | ||||
|  static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) | ||||
|  { | ||||
| 	struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; | ||||
| @@ -465,6 +538,9 @@ | ||||
| 	writel(CFG_BRIDGE_SB_INIT, | ||||
| 	       pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); | ||||
|  | ||||
| +	qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); | ||||
| +	qcom_pcie_prog_viewport_mem2_outbound(pcie); | ||||
| + | ||||
| 	return 0; | ||||
|  | ||||
|  err_deassert_ahb: | ||||
| @@ -0,0 +1,61 @@ | ||||
| From 4910cfd150342ec7b038892262923c725a9c4001 Mon Sep 17 00:00:00 2001 | ||||
| From: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| Date: Wed, 7 Sep 2016 16:44:28 +0530 | ||||
| Subject: PCI: qcom: Force GEN1 support | ||||
|  | ||||
| Change-Id: Ica54ddb737d7b851469deab1745f54bf431bd3f0 | ||||
| Signed-off-by: Sham Muthayyan <smuthayy@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 13 +++++++++++++ | ||||
|  1 file changed, 13 insertions(+) | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -129,6 +129,8 @@ | ||||
|  #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE	0x358 | ||||
|  #define SLV_ADDR_SPACE_SZ			0x10000000 | ||||
|  | ||||
| +#define PCIE20_LNK_CONTROL2_LINK_STATUS2        0xA0 | ||||
| + | ||||
|  struct qcom_pcie_resources_2_1_0 { | ||||
| 	struct clk *iface_clk; | ||||
| 	struct clk *core_clk; | ||||
| @@ -218,6 +220,7 @@ | ||||
| 	struct phy *phy; | ||||
| 	struct gpio_desc *reset; | ||||
| 	struct qcom_pcie_ops *ops; | ||||
| +	uint32_t force_gen1; | ||||
|  }; | ||||
|  | ||||
|  #define to_qcom_pcie(x)		dev_get_drvdata((x)->dev) | ||||
| @@ -532,6 +535,11 @@ | ||||
|  | ||||
| 	/* wait for clock acquisition */ | ||||
| 	usleep_range(1000, 1500); | ||||
| +	if (pcie->force_gen1) { | ||||
| +		writel_relaxed((readl_relaxed( | ||||
| +			pcie->pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2) | 1), | ||||
| +			pcie->pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2); | ||||
| +	} | ||||
|  | ||||
|  | ||||
| 	/* Set the Max TLP size to 2K, instead of using default of 4K */ | ||||
| @@ -1382,6 +1390,8 @@ | ||||
| 	struct dw_pcie *pci; | ||||
| 	struct qcom_pcie *pcie; | ||||
| 	int ret; | ||||
| +	uint32_t force_gen1 = 0; | ||||
| +	struct device_node *np = pdev->dev.of_node; | ||||
|  | ||||
| 	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); | ||||
| 	if (!pcie) | ||||
| @@ -1403,6 +1413,9 @@ | ||||
| 	if (IS_ERR(pcie->reset)) | ||||
| 		return PTR_ERR(pcie->reset); | ||||
|  | ||||
| +	of_property_read_u32(np, "force_gen1", &force_gen1); | ||||
| +	pcie->force_gen1 = force_gen1; | ||||
| + | ||||
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); | ||||
| 	pcie->parf = devm_ioremap_resource(dev, res); | ||||
| 	if (IS_ERR(pcie->parf)) | ||||
| @@ -0,0 +1,69 @@ | ||||
| From edff8f777c6321ca89bb950a382f409c4a126e28 Mon Sep 17 00:00:00 2001 | ||||
| From: Gokul Sriram Palanisamy <gpalan@codeaurora.org> | ||||
| Date: Thu, 15 Dec 2016 17:38:18 +0530 | ||||
| Subject: pcie: Set PCIE MRRS and MPS to 256B | ||||
|  | ||||
| Set Max Read Request Size and Max Payload Size to 256 bytes, | ||||
| per chip team recommendation. | ||||
|  | ||||
| Change-Id: I097004be2ced1b3096ffc10c318aae0b2bb155e8 | ||||
| Signed-off-by: Gokul Sriram Palanisamy <gpalan@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 37 +++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 37 insertions(+) | ||||
|  | ||||
| (limited to 'drivers/pci/host/pcie-qcom.c') | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -131,6 +131,14 @@ | ||||
|  | ||||
|  #define PCIE20_LNK_CONTROL2_LINK_STATUS2        0xA0 | ||||
|  | ||||
| +#define __set(v, a, b)	(((v) << (b)) & GENMASK(a, b)) | ||||
| +#define __mask(a, b)	(((1 << ((a) + 1)) - 1) & ~((1 << (b)) - 1)) | ||||
| +#define PCIE20_DEV_CAS			0x78 | ||||
| +#define PCIE20_MRRS_MASK		__mask(14, 12) | ||||
| +#define PCIE20_MRRS(x)			__set(x, 14, 12) | ||||
| +#define PCIE20_MPS_MASK			__mask(7, 5) | ||||
| +#define PCIE20_MPS(x)			__set(x, 7, 5) | ||||
| + | ||||
|  struct qcom_pcie_resources_2_1_0 { | ||||
| 	struct clk *iface_clk; | ||||
| 	struct clk *core_clk; | ||||
| @@ -1472,6 +1480,35 @@ | ||||
| 	return 0; | ||||
|  } | ||||
|  | ||||
| +static void qcom_pcie_fixup_final(struct pci_dev *dev) | ||||
| +{ | ||||
| +	int cap, err; | ||||
| +	u16 ctl, reg_val; | ||||
| + | ||||
| +	cap = pci_pcie_cap(dev); | ||||
| +	if (!cap) | ||||
| +		return; | ||||
| + | ||||
| +	err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl); | ||||
| + | ||||
| +	if (err) | ||||
| +		return; | ||||
| + | ||||
| +	reg_val = ctl; | ||||
| + | ||||
| +	if (((reg_val & PCIE20_MRRS_MASK) >> 12) > 1) | ||||
| +		reg_val = (reg_val & ~(PCIE20_MRRS_MASK)) | PCIE20_MRRS(0x1); | ||||
| + | ||||
| +	if (((ctl & PCIE20_MPS_MASK) >> 5) > 1) | ||||
| +		reg_val = (reg_val & ~(PCIE20_MPS_MASK)) | PCIE20_MPS(0x1); | ||||
| + | ||||
| +	err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, reg_val); | ||||
| + | ||||
| +	if (err) | ||||
| +		pr_err("pcie config write failed %d\n", err); | ||||
| +} | ||||
| +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, qcom_pcie_fixup_final); | ||||
| + | ||||
|  static const struct of_device_id qcom_pcie_match[] = { | ||||
| 	{ .compatible = "qcom,pcie-apq8084", .data = &ops_1_0_0 }, | ||||
| 	{ .compatible = "qcom,pcie-ipq8064", .data = &ops_2_1_0 }, | ||||
| @@ -0,0 +1,91 @@ | ||||
| From b74bab6186131eea09459eedf5d737645a3559c9 Mon Sep 17 00:00:00 2001 | ||||
| From: Abhishek Sahu <absahu@codeaurora.org> | ||||
| Date: Thu, 22 Dec 2016 11:18:45 +0530 | ||||
| Subject: pcie: qcom: Fixed pcie_phy_clk branch issue | ||||
|  | ||||
| Following backtraces are observed in PCIe deinit operation. | ||||
|  | ||||
|  Hardware name: Qualcomm (Flattened Device Tree) | ||||
|  (unwind_backtrace) from [] (show_stack+0x10/0x14) | ||||
|  (show_stack) from [] (dump_stack+0x84/0x98) | ||||
|  (dump_stack) from [] (warn_slowpath_common+0x9c/0xb8) | ||||
|  (warn_slowpath_common) from [] (warn_slowpath_fmt+0x30/0x40) | ||||
|  (warn_slowpath_fmt) from [] (clk_branch_wait+0x114/0x120) | ||||
|  (clk_branch_wait) from [] (clk_core_disable+0xd0/0x1f4) | ||||
|  (clk_core_disable) from [] (clk_disable+0x24/0x30) | ||||
|  (clk_disable) from [] (qcom_pcie_deinit_v0+0x6c/0xb8) | ||||
|  (qcom_pcie_deinit_v0) from [] (qcom_pcie_host_init+0xe0/0xe8) | ||||
|  (qcom_pcie_host_init) from [] (dw_pcie_host_init+0x3b0/0x538) | ||||
|  (dw_pcie_host_init) from [] (qcom_pcie_probe+0x20c/0x2e4) | ||||
|  | ||||
| pcie_phy_clk is generated for PCIe controller itself and the | ||||
| GCC controls its branch operation. This error is coming since | ||||
| the assert operations turn off the parent clock before branch | ||||
| clock. Now this patch moves clk_disable_unprepare before assert | ||||
| operations. | ||||
|  | ||||
| Similarly, during probe function, the clock branch operation | ||||
| should be done after dessert operation. Currently, it does not | ||||
| generate any error since bootloader enables the pcie_phy_clk | ||||
| but the same error is coming during probe, if bootloader | ||||
| disables pcie_phy_clk. | ||||
|  | ||||
| Change-Id: Ib29c154d10eb64363d9cc982ce5fd8107af5627d | ||||
| Signed-off-by: Abhishek Sahu <absahu@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 16 +++++++--------- | ||||
|  1 file changed, 7 insertions(+), 9 deletions(-) | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -407,6 +407,7 @@ | ||||
|  { | ||||
| 	struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; | ||||
|  | ||||
| +	clk_disable_unprepare(res->phy_clk); | ||||
| 	reset_control_assert(res->pci_reset); | ||||
| 	reset_control_assert(res->axi_reset); | ||||
| 	reset_control_assert(res->ahb_reset); | ||||
| @@ -415,7 +415,6 @@ | ||||
| 	reset_control_assert(res->ext_reset); | ||||
| 	clk_disable_unprepare(res->iface_clk); | ||||
| 	clk_disable_unprepare(res->core_clk); | ||||
| -	clk_disable_unprepare(res->phy_clk); | ||||
| 	clk_disable_unprepare(res->aux_clk); | ||||
| 	clk_disable_unprepare(res->ref_clk); | ||||
| 	regulator_disable(res->vdda); | ||||
| @@ -472,12 +472,6 @@ | ||||
| 		goto err_clk_core; | ||||
| 	} | ||||
|  | ||||
| -	ret = clk_prepare_enable(res->phy_clk); | ||||
| -	if (ret) { | ||||
| -		dev_err(dev, "cannot prepare/enable phy clock\n"); | ||||
| -		goto err_clk_phy; | ||||
| -	} | ||||
| - | ||||
| 	ret = clk_prepare_enable(res->aux_clk); | ||||
| 	if (ret) { | ||||
| 		dev_err(dev, "cannot prepare/enable aux clock\n"); | ||||
| @@ -541,6 +535,12 @@ | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| +	ret = clk_prepare_enable(res->phy_clk); | ||||
| +	if (ret) { | ||||
| +		dev_err(dev, "cannot prepare/enable phy clock\n"); | ||||
| +		goto err_deassert_ahb; | ||||
| +	} | ||||
| + | ||||
| 	/* wait for clock acquisition */ | ||||
| 	usleep_range(1000, 1500); | ||||
| 	if (pcie->force_gen1) { | ||||
| @@ -566,8 +566,6 @@ | ||||
|  err_clk_ref: | ||||
| 	clk_disable_unprepare(res->aux_clk); | ||||
|  err_clk_aux: | ||||
| -	clk_disable_unprepare(res->phy_clk); | ||||
| -err_clk_phy: | ||||
| 	clk_disable_unprepare(res->core_clk); | ||||
|  err_clk_core: | ||||
| 	clk_disable_unprepare(res->iface_clk); | ||||
| @@ -0,0 +1,25 @@ | ||||
| From 1a9c48123bd09f75562b6a2ee0f0a7b2d533cd45 Mon Sep 17 00:00:00 2001 | ||||
| From: Abhishek Sahu <absahu@codeaurora.org> | ||||
| Date: Thu, 22 Dec 2016 11:50:49 +0530 | ||||
| Subject: pcie: qcom: change duplicate pci reset to phy reset | ||||
|  | ||||
| The deinit issues reset_control_assert for pci twice and | ||||
| does not contain phy reset. | ||||
|  | ||||
| Change-Id: Iba849963c7e5f9a2a1063f0e2e89635df70b8a99 | ||||
| Signed-off-by: Abhishek Sahu <absahu@codeaurora.org> | ||||
| --- | ||||
|  drivers/pci/host/pcie-qcom.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/pci/dwc/pcie-qcom.c | ||||
| +++ b/drivers/pci/dwc/pcie-qcom.c | ||||
| @@ -408,7 +408,7 @@ | ||||
| 	struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; | ||||
|  | ||||
| 	clk_disable_unprepare(res->phy_clk); | ||||
| -	reset_control_assert(res->pci_reset); | ||||
| +	reset_control_assert(res->phy_reset); | ||||
| 	reset_control_assert(res->axi_reset); | ||||
| 	reset_control_assert(res->ahb_reset); | ||||
| 	reset_control_assert(res->por_reset); | ||||
| @@ -0,0 +1,12 @@ | ||||
| --- a/drivers/firmware/qcom_scm.c | ||||
| +++ b/drivers/firmware/qcom_scm.c | ||||
| @@ -422,6 +422,9 @@ | ||||
| 	{ .compatible = "qcom,scm-msm8996", | ||||
| 	  .data = NULL, /* no clocks */ | ||||
| 	}, | ||||
| +	{ .compatible = "qcom,scm-ipq806x", | ||||
| +	  .data = NULL, /* no clocks */ | ||||
| +	}, | ||||
| 	{ .compatible = "qcom,scm", | ||||
| 	  .data = (void *)(SCM_HAS_CORE_CLK | ||||
| 			   | SCM_HAS_IFACE_CLK | ||||
| @@ -0,0 +1,183 @@ | ||||
| From 2034addc7e193dc81d7ca60d8884832751b76758 Mon Sep 17 00:00:00 2001 | ||||
| From: Ajay Kishore <akisho@codeaurora.org> | ||||
| Date: Tue, 24 Jan 2017 14:14:16 +0530 | ||||
| Subject: pinctrl: qcom: use scm_call to route GPIO irq to Apps | ||||
|  | ||||
| For IPQ806x targets, TZ protects the registers that are used to | ||||
| configure the routing of interrupts to a target processor. | ||||
| To resolve this, this patch uses scm call to route GPIO interrupts | ||||
| to application processor. Also the scm call interface is changed. | ||||
|  | ||||
| Change-Id: Ib6c06829d04bc8c20483c36e63da92e26cdef9ce | ||||
| Signed-off-by: Ajay Kishore <akisho@codeaurora.org> | ||||
| --- | ||||
|  drivers/firmware/qcom_scm-32.c     | 17 +++++++++++++++++ | ||||
|  drivers/firmware/qcom_scm-64.c     |  9 +++++++++ | ||||
|  drivers/firmware/qcom_scm.c        | 13 +++++++++++++ | ||||
|  drivers/firmware/qcom_scm.h        |  8 ++++++++ | ||||
|  drivers/pinctrl/qcom/pinctrl-msm.c | 34 ++++++++++++++++++++++++++++------ | ||||
|  include/linux/qcom_scm.h           |  3 ++- | ||||
|  6 files changed, 77 insertions(+), 7 deletions(-) | ||||
|  | ||||
| --- a/drivers/firmware/qcom_scm-32.c | ||||
| +++ b/drivers/firmware/qcom_scm-32.c | ||||
| @@ -561,6 +561,24 @@ | ||||
| 	return ret ? : le32_to_cpu(out); | ||||
|  } | ||||
|  | ||||
| +int __qcom_scm_pinmux_read(u32 svc_id, u32 cmd_id, u32 arg1) | ||||
| +{ | ||||
| +	s32 ret; | ||||
| + | ||||
| +	ret = qcom_scm_call_atomic1(svc_id, cmd_id, arg1); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +int __qcom_scm_pinmux_write(u32 svc_id, u32 cmd_id, u32 arg1, u32 arg2) | ||||
| +{ | ||||
| +	s32 ret; | ||||
| + | ||||
| +	ret = qcom_scm_call_atomic2(svc_id, cmd_id, arg1, arg2); | ||||
| + | ||||
| + return ret; | ||||
| +} | ||||
| + | ||||
|  int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) | ||||
|  { | ||||
| 	struct { | ||||
| --- a/drivers/firmware/qcom_scm-64.c | ||||
| +++ b/drivers/firmware/qcom_scm-64.c | ||||
| @@ -366,6 +366,16 @@ | ||||
| 	return ret ? : res.a1; | ||||
|  } | ||||
|  | ||||
| +int __qcom_scm_pinmux_read(u32 svc_id, u32 cmd_id, u32 arg1) | ||||
| +{ | ||||
| +	return -ENOTSUPP; | ||||
| +} | ||||
| + | ||||
| +int __qcom_scm_pinmux_write(u32 svc_id, u32 cmd_id, u32 arg1, u32 arg2) | ||||
| +{ | ||||
| +	return -ENOTSUPP; | ||||
| +} | ||||
| + | ||||
|  int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) | ||||
|  { | ||||
| 	struct qcom_scm_desc desc = {0}; | ||||
| --- a/drivers/firmware/qcom_scm.c | ||||
| +++ b/drivers/firmware/qcom_scm.c | ||||
| @@ -470,3 +470,16 @@ static int __init qcom_scm_init(void) | ||||
| 	return platform_driver_register(&qcom_scm_driver); | ||||
|  } | ||||
|  subsys_initcall(qcom_scm_init); | ||||
| + | ||||
| +int qcom_scm_pinmux_read(u32 arg1) | ||||
| +{ | ||||
| +	return __qcom_scm_pinmux_read(SCM_SVC_IO_ACCESS, SCM_IO_READ, arg1); | ||||
| +} | ||||
| +EXPORT_SYMBOL(qcom_scm_pinmux_read); | ||||
| + | ||||
| +int qcom_scm_pinmux_write(u32 arg1, u32 arg2) | ||||
| +{ | ||||
| +	return __qcom_scm_pinmux_write(SCM_SVC_IO_ACCESS, SCM_IO_WRITE, | ||||
| +					arg1, arg2); | ||||
| +} | ||||
| +EXPORT_SYMBOL(qcom_scm_pinmux_write); | ||||
| --- a/drivers/firmware/qcom_scm.h | ||||
| +++ b/drivers/firmware/qcom_scm.h | ||||
| @@ -58,6 +58,13 @@ extern int  __qcom_scm_pas_auth_and_rese | ||||
|  extern int  __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral); | ||||
|  extern int  __qcom_scm_pas_mss_reset(struct device *dev, bool reset); | ||||
|  | ||||
| +#define SCM_IO_READ	1 | ||||
| +#define SCM_IO_WRITE	2 | ||||
| +#define SCM_SVC_IO_ACCESS	0x5 | ||||
| + | ||||
| +s32 __qcom_scm_pinmux_read(u32 svc_id, u32 cmd_id, u32 arg1); | ||||
| +s32 __qcom_scm_pinmux_write(u32 svc_id, u32 cmd_id, u32 arg1, u32 arg2); | ||||
| + | ||||
|  /* common error codes */ | ||||
|  #define QCOM_SCM_V2_EBUSY	-12 | ||||
|  #define QCOM_SCM_ENOMEM		-5 | ||||
| --- a/drivers/pinctrl/qcom/pinctrl-msm.c | ||||
| +++ b/drivers/pinctrl/qcom/pinctrl-msm.c | ||||
| @@ -30,7 +30,8 @@ | ||||
|  #include <linux/reboot.h> | ||||
|  #include <linux/pm.h> | ||||
|  #include <linux/log2.h> | ||||
| - | ||||
| +#include <linux/qcom_scm.h> | ||||
| +#include <linux/io.h> | ||||
|  #include "../core.h" | ||||
|  #include "../pinconf.h" | ||||
|  #include "pinctrl-msm.h" | ||||
| @@ -638,6 +639,9 @@ static int msm_gpio_irq_set_type(struct | ||||
| 	const struct msm_pingroup *g; | ||||
| 	unsigned long flags; | ||||
| 	u32 val; | ||||
| +	u32 addr; | ||||
| +	int ret; | ||||
| +	const __be32 *reg; | ||||
|  | ||||
| 	g = &pctrl->soc->groups[d->hwirq]; | ||||
|  | ||||
| @@ -676,11 +680,30 @@ static int msm_gpio_irq_set_type(struct | ||||
| 	else | ||||
| 		clear_bit(d->hwirq, pctrl->dual_edge_irqs); | ||||
|  | ||||
| +	int ret = of_device_is_compatible(pctrl->dev->of_node, | ||||
| +					"qcom,ipq8064-pinctrl"); | ||||
| 	/* Route interrupts to application cpu */ | ||||
| -	val = readl(pctrl->regs + g->intr_target_reg); | ||||
| -	val &= ~(7 << g->intr_target_bit); | ||||
| -	val |= g->intr_target_kpss_val << g->intr_target_bit; | ||||
| -	writel(val, pctrl->regs + g->intr_target_reg); | ||||
| +	if (!ret) { | ||||
| +		val = readl(pctrl->regs + g->intr_target_reg); | ||||
| +		val &= ~(7 << g->intr_target_bit); | ||||
| +		val |= g->intr_target_kpss_val << g->intr_target_bit; | ||||
| +		writel(val, pctrl->regs + g->intr_target_reg); | ||||
| +	} else { | ||||
| +		const __be32 *reg = of_get_property(pctrl->dev->of_node, "reg", NULL); | ||||
| +		if (reg) { | ||||
| +			u32 addr = be32_to_cpup(reg) + g->intr_target_reg; | ||||
| +			val = qcom_scm_pinmux_read(addr); | ||||
| +			__iormb(); | ||||
| + | ||||
| +			val &= ~(7 << g->intr_target_bit); | ||||
| +			val |= g->intr_target_kpss_val << g->intr_target_bit; | ||||
| + | ||||
| +			__iowmb(); | ||||
| +			ret = qcom_scm_pinmux_write(addr, val); | ||||
| +			if (ret) | ||||
| +				pr_err("\n Routing interrupts to Apps proc failed"); | ||||
| +		} | ||||
| +	} | ||||
|  | ||||
| 	/* Update configuration for gpio. | ||||
| 	 * RAW_STATUS_EN is left on for all gpio irqs. Due to the | ||||
| @@ -954,4 +977,3 @@ int msm_pinctrl_remove(struct platform_d | ||||
| 	return 0; | ||||
|  } | ||||
|  EXPORT_SYMBOL(msm_pinctrl_remove); | ||||
| - | ||||
| --- a/include/linux/qcom_scm.h | ||||
| +++ b/include/linux/qcom_scm.h | ||||
| @@ -43,6 +43,8 @@ | ||||
|  extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); | ||||
|  extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); | ||||
|  extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); | ||||
| +extern s32 qcom_scm_pinmux_read(u32 arg1); | ||||
| +extern s32 qcom_scm_pinmux_write(u32 arg1, u32 arg2); | ||||
|  #else | ||||
|  static inline | ||||
|  int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | ||||
| @@ -73,5 +75,7 @@ | ||||
|  static inline int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) { return -ENODEV; } | ||||
|  static inline int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) { return -ENODEV; } | ||||
|  static inline int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) { return -ENODEV; } | ||||
| +extern s32 qcom_scm_pinmux_read(u32 arg1) { return -ENODEV; } | ||||
| +extern s32 qcom_scm_pinmux_write(u32 arg1, u32 arg2) { return -ENODEV; } | ||||
|  #endif | ||||
|  #endif | ||||
| @@ -0,0 +1,71 @@ | ||||
| From a86bda9f8a7965f0cedd347a9c04800eb9f41ea3 Mon Sep 17 00:00:00 2001 | ||||
| From: Vasudevan Murugesan <vmuruges@codeaurora.org> | ||||
| Date: Tue, 21 Jul 2015 10:22:38 +0530 | ||||
| Subject: ipq806x: usb: Control USB master reset | ||||
|  | ||||
| During removal of the glue layer(dwc3-of-simple), | ||||
| USB master reset is set to active and during insertion | ||||
| it is de-activated. | ||||
|  | ||||
| Change-Id: I537dc810f6cb2a46664ee674840145066432b957 | ||||
| Signed-off-by: Vasudevan Murugesan <vmuruges@codeaurora.org> | ||||
| (cherry picked from commit 4611e13580a216812f85f0801b95442d02eeb836) | ||||
| --- | ||||
|  drivers/usb/dwc3/dwc3-of-simple.c | 22 ++++++++++++++++++++++ | ||||
|  1 file changed, 12 insertions(+) | ||||
|  | ||||
| (limited to 'drivers/usb/dwc3/dwc3-of-simple.c') | ||||
|  | ||||
| --- a/drivers/usb/dwc3/dwc3-of-simple.c | ||||
| +++ b/drivers/usb/dwc3/dwc3-of-simple.c | ||||
| @@ -25,6 +25,7 @@ | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/dma-mapping.h> | ||||
|  #include <linux/clk.h> | ||||
| +#include <linux/reset.h> | ||||
|  #include <linux/of.h> | ||||
|  #include <linux/of_platform.h> | ||||
|  #include <linux/pm_runtime.h> | ||||
| @@ -33,6 +34,8 @@ | ||||
| 	struct device		*dev; | ||||
| 	struct clk		**clks; | ||||
| 	int			num_clocks; | ||||
| +	struct reset_control	*mstr_rst_30_0; | ||||
| +	struct reset_control	*mstr_rst_30_1; | ||||
|  }; | ||||
|  | ||||
|  static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) | ||||
| @@ -102,6 +105,20 @@ | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| +	simple->mstr_rst_30_0 = devm_reset_control_get(dev, "usb30_0_mstr_rst"); | ||||
| + | ||||
| +	if (!IS_ERR(simple->mstr_rst_30_0)) | ||||
| +		reset_control_deassert(simple->mstr_rst_30_0); | ||||
| +	else | ||||
| +		dev_dbg(simple->dev, "cannot get handle for USB PHY 0 master reset control\n"); | ||||
| + | ||||
| +	simple->mstr_rst_30_1 = devm_reset_control_get(dev, "usb30_1_mstr_rst"); | ||||
| + | ||||
| +	if (!IS_ERR(simple->mstr_rst_30_1)) | ||||
| +		reset_control_deassert(simple->mstr_rst_30_1); | ||||
| +	else | ||||
| +		dev_dbg(simple->dev, "cannot get handle for USB PHY 1 master reset control\n"); | ||||
| + | ||||
| 	ret = of_platform_populate(np, NULL, NULL, dev); | ||||
| 	if (ret) { | ||||
| 		for (i = 0; i < simple->num_clocks; i++) { | ||||
| @@ -130,6 +147,12 @@ | ||||
| 		clk_put(simple->clks[i]); | ||||
| 	} | ||||
|  | ||||
| +	if (!IS_ERR(simple->mstr_rst_30_0)) | ||||
| +		reset_control_assert(simple->mstr_rst_30_0); | ||||
| + | ||||
| +	if (!IS_ERR(simple->mstr_rst_30_1)) | ||||
| +		reset_control_assert(simple->mstr_rst_30_1); | ||||
| + | ||||
| 	of_platform_depopulate(dev); | ||||
|  | ||||
| 	pm_runtime_put_sync(dev); | ||||
| @@ -0,0 +1,38 @@ | ||||
| From 07b6d0cdbbda8c917480eceaec668f09e4cf24a5 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Lamparter <chunkeey@gmail.com> | ||||
| Date: Mon, 14 Nov 2016 23:49:22 +0100 | ||||
| Subject: [PATCH] mtd: nand: add Winbond manufacturer and chip | ||||
|  | ||||
| This patch adds the W25N01GV NAND to the table of | ||||
| known devices. Without this patch the device gets detected: | ||||
|  | ||||
| nand: device found, Manufacturer ID: 0xef, Chip ID: 0xaa | ||||
| nand: Unknown NAND 256MiB 1,8V 8-bit | ||||
| nand: 256 MiB, SLC, erase size: 64 KiB, page size: 1024, OOB size : 16 | ||||
|  | ||||
| Whereas the u-boot identifies it as: | ||||
| spi_nand: spi_nand_flash_probe SF NAND ID 00:ef:aa:21 | ||||
| SF: Detected W25N01GV with page size 2 KiB, total 128 MiB | ||||
|  | ||||
| Due to the page size discrepancy, it's impossible to attach | ||||
| ubi volumes on the device. | ||||
|  | ||||
| Signed-off-by: Christian Lamparter <chunkeey@gmail.com> | ||||
| --- | ||||
|  drivers/mtd/nand/nand_ids.c | 4 ++++ | ||||
|  include/linux/mtd/nand.h    | 1 + | ||||
|  2 files changed, 5 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/nand/nand_ids.c | ||||
| +++ b/drivers/mtd/nand/nand_ids.c | ||||
| @@ -54,6 +54,10 @@ struct nand_flash_dev nand_flash_ids[] = | ||||
| 		{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, | ||||
| 		  SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, | ||||
| 		  NAND_ECC_INFO(40, SZ_1K), 4 }, | ||||
| +	{"W25N01GV 1G 3.3V 8-bit", | ||||
| +		{ .id = {0xef, 0xaa} }, | ||||
| +		  SZ_2K, SZ_128, SZ_128K, NAND_NO_SUBPAGE_WRITE, | ||||
| +		  2, 64, NAND_ECC_INFO(1, SZ_512) }, | ||||
|  | ||||
| 	LEGACY_ID_NAND("NAND 4MiB 5V 8-bit",   0x6B, 4, SZ_8K, SP_OPTIONS), | ||||
| 	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), | ||||
| @@ -0,0 +1,22 @@ | ||||
| Subject: mtd: spi-nor: add mx25l25635f with SECT_4K | ||||
|  | ||||
| This patch fixes an issue with the creation of the | ||||
| ubi volume on the AVM FRITZ!Box 4040. The mx25l25635f | ||||
| and mx25l25635e support SECT_4K which will set the | ||||
| erase size to 4K. This is used by ubi to calculate | ||||
| VID header offsets. Without this, uboot and linux | ||||
| disagrees about the layout and refuse to attach | ||||
| the ubi volume created by the other. | ||||
|  | ||||
| --- | ||||
| --- a/drivers/mtd/spi-nor/spi-nor.c | ||||
| +++ b/drivers/mtd/spi-nor/spi-nor.c | ||||
| @@ -1023,7 +1023,7 @@ | ||||
| 	{ "mx25u6435f",  INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, | ||||
| 	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, | ||||
| 	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, | ||||
| -	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
| +	{ "mx25l25635f", INFO(0xc22019, 0, 64 * 1024, 512, SECT_4K) }, | ||||
| 	{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, | ||||
| 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, | ||||
| 	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
							
								
								
									
										11026
									
								
								target/linux/ipq806x/patches-4.14/310-msm-adhoc-bus-support.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11026
									
								
								target/linux/ipq806x/patches-4.14/310-msm-adhoc-bus-support.patch
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,177 @@ | ||||
| From: Christian Lamparter <chunkeey@googlemail.com> | ||||
| Subject: SoC: add qualcomm syscon | ||||
| --- a/drivers/soc/qcom/Makefile | ||||
| +++ b/drivers/soc/qcom/Makefile | ||||
| @@ -9,3 +9,4 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_st | ||||
|  obj-$(CONFIG_QCOM_SMP2P)	+= smp2p.o | ||||
|  obj-$(CONFIG_QCOM_SMSM)	+= smsm.o | ||||
|  obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o | ||||
| +obj-$(CONFIG_QCOM_TCSR)	 += qcom_tcsr.o | ||||
| --- a/drivers/soc/qcom/Kconfig | ||||
| +++ b/drivers/soc/qcom/Kconfig | ||||
| @@ -78,6 +78,13 @@ config QCOM_SMSM | ||||
| 	  Say yes here to support the Qualcomm Shared Memory State Machine. | ||||
| 	  The state machine is represented by bits in shared memory. | ||||
|  | ||||
| +config QCOM_TCSR | ||||
| +	tristate "QCOM Top Control and Status Registers" | ||||
| +	depends on ARCH_QCOM | ||||
| +	help | ||||
| +	  Say y here to enable TCSR support.  The TCSR provides control | ||||
| +	  functions for various peripherals. | ||||
| + | ||||
|  config QCOM_WCNSS_CTRL | ||||
| 	tristate "Qualcomm WCNSS control driver" | ||||
| 	depends on ARCH_QCOM | ||||
| --- /dev/null | ||||
| +++ b/drivers/soc/qcom/qcom_tcsr.c | ||||
| @@ -0,0 +1,98 @@ | ||||
| +/* | ||||
| + * Copyright (c) 2014, The Linux foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License rev 2 and | ||||
| + * only rev 2 as published by the free Software foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/err.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/of_platform.h> | ||||
| +#include <linux/platform_device.h> | ||||
| + | ||||
| +#define TCSR_USB_PORT_SEL	0xb0 | ||||
| +#define TCSR_USB_HSPHY_CONFIG	0xC | ||||
| + | ||||
| +#define TCSR_ESS_INTERFACE_SEL_OFFSET   0x0 | ||||
| +#define TCSR_ESS_INTERFACE_SEL_MASK     0xf | ||||
| + | ||||
| +#define TCSR_WIFI0_GLB_CFG_OFFSET	0x0 | ||||
| +#define TCSR_WIFI1_GLB_CFG_OFFSET	0x4 | ||||
| +#define TCSR_PNOC_SNOC_MEMTYPE_M0_M2	0x4 | ||||
| + | ||||
| +static int tcsr_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct resource *res; | ||||
| +	const struct device_node *node = pdev->dev.of_node; | ||||
| +	void __iomem *base; | ||||
| +	u32 val; | ||||
| + | ||||
| +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| +	base = devm_ioremap_resource(&pdev->dev, res); | ||||
| +	if (IS_ERR(base)) | ||||
| +		return PTR_ERR(base); | ||||
| + | ||||
| +	if (!of_property_read_u32(node, "qcom,usb-ctrl-select", &val)) { | ||||
| +		dev_err(&pdev->dev, "setting usb port select = %d\n", val); | ||||
| +		writel(val, base + TCSR_USB_PORT_SEL); | ||||
| +	} | ||||
| + | ||||
| +	if (!of_property_read_u32(node, "qcom,usb-hsphy-mode-select", &val)) { | ||||
| +		dev_info(&pdev->dev, "setting usb hs phy mode select = %x\n", val); | ||||
| +		writel(val, base + TCSR_USB_HSPHY_CONFIG); | ||||
| +	} | ||||
| + | ||||
| +	if (!of_property_read_u32(node, "qcom,ess-interface-select", &val)) { | ||||
| +		u32 tmp = 0; | ||||
| +		dev_info(&pdev->dev, "setting ess interface select = %x\n", val); | ||||
| +		tmp = readl(base + TCSR_ESS_INTERFACE_SEL_OFFSET); | ||||
| +		tmp = tmp & (~TCSR_ESS_INTERFACE_SEL_MASK); | ||||
| +		tmp = tmp | (val&TCSR_ESS_INTERFACE_SEL_MASK); | ||||
| +		writel(tmp, base + TCSR_ESS_INTERFACE_SEL_OFFSET); | ||||
| +        } | ||||
| + | ||||
| +	if (!of_property_read_u32(node, "qcom,wifi_glb_cfg", &val)) { | ||||
| +		dev_info(&pdev->dev, "setting wifi_glb_cfg = %x\n", val); | ||||
| +		writel(val, base + TCSR_WIFI0_GLB_CFG_OFFSET); | ||||
| +		writel(val, base + TCSR_WIFI1_GLB_CFG_OFFSET); | ||||
| +	} | ||||
| + | ||||
| +	if (!of_property_read_u32(node, "qcom,wifi_noc_memtype_m0_m2", &val)) { | ||||
| +		dev_info(&pdev->dev, | ||||
| +			"setting wifi_noc_memtype_m0_m2 = %x\n", val); | ||||
| +		writel(val, base + TCSR_PNOC_SNOC_MEMTYPE_M0_M2); | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static const struct of_device_id tcsr_dt_match[] = { | ||||
| +	{ .compatible = "qcom,tcsr", }, | ||||
| +	{ }, | ||||
| +}; | ||||
| + | ||||
| +MODULE_DEVICE_TABLE(of, tcsr_dt_match); | ||||
| + | ||||
| +static struct platform_driver tcsr_driver = { | ||||
| +	.driver = { | ||||
| +		.name		= "tcsr", | ||||
| +		.owner		= THIS_MODULE, | ||||
| +		.of_match_table	= tcsr_dt_match, | ||||
| +	}, | ||||
| +	.probe = tcsr_probe, | ||||
| +}; | ||||
| + | ||||
| +module_platform_driver(tcsr_driver); | ||||
| + | ||||
| +MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); | ||||
| +MODULE_DESCRIPTION("QCOM TCSR driver"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| --- /dev/null | ||||
| +++ b/include/dt-bindings/soc/qcom,tcsr.h | ||||
| @@ -0,0 +1,48 @@ | ||||
| +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 and | ||||
| + * only version 2 as published by the Free Software Foundation. | ||||
| + * | ||||
| + * 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. | ||||
| + */ | ||||
| +#ifndef __DT_BINDINGS_QCOM_TCSR_H | ||||
| +#define __DT_BINDINGS_QCOM_TCSR_H | ||||
| + | ||||
| +#define TCSR_USB_SELECT_USB3_P0		0x1 | ||||
| +#define TCSR_USB_SELECT_USB3_P1		0x2 | ||||
| +#define TCSR_USB_SELECT_USB3_DUAL	0x3 | ||||
| + | ||||
| +/* IPQ40xx HS PHY Mode Select */ | ||||
| +#define TCSR_USB_HSPHY_HOST_MODE	0x00E700E7 | ||||
| +#define TCSR_USB_HSPHY_DEVICE_MODE	0x00C700E7 | ||||
| + | ||||
| +/* IPQ40xx ess interface mode select */ | ||||
| +#define TCSR_ESS_PSGMII              0 | ||||
| +#define TCSR_ESS_PSGMII_RGMII5       1 | ||||
| +#define TCSR_ESS_PSGMII_RMII0        2 | ||||
| +#define TCSR_ESS_PSGMII_RMII1        4 | ||||
| +#define TCSR_ESS_PSGMII_RMII0_RMII1  6 | ||||
| +#define TCSR_ESS_PSGMII_RGMII4       9 | ||||
| + | ||||
| +/* | ||||
| + * IPQ40xx WiFi Global Config | ||||
| + * Bit 30:AXID_EN | ||||
| + * Enable AXI master bus Axid translating to confirm all txn submitted by order | ||||
| + * Bit 24: Use locally generated socslv_wxi_bvalid | ||||
| + * 1:  use locally generate socslv_wxi_bvalid for performance. | ||||
| + * 0:  use SNOC socslv_wxi_bvalid. | ||||
| + */ | ||||
| +#define TCSR_WIFI_GLB_CFG		0x41000000 | ||||
| + | ||||
| +/* IPQ40xx MEM_TYPE_SEL_M0_M2 Select Bit 26:24 - 2 NORMAL */ | ||||
| +#define TCSR_WIFI_NOC_MEMTYPE_M0_M2	0x02222222 | ||||
| + | ||||
| +/* TCSR A/B REG */ | ||||
| +#define IPQ806X_TCSR_REG_A_ADM_CRCI_MUX_SEL     0 | ||||
| +#define IPQ806X_TCSR_REG_B_ADM_CRCI_MUX_SEL     1 | ||||
| + | ||||
| +#endif | ||||
		Reference in New Issue
	
	Block a user