From: Ram Chandra Jangir Date: Sat, 21 May 2016 09:18:31 +0000 (+0530) Subject: ipq806x: Add support for linux-4.4 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=5e49c5795697868f6317f17fda5a2b04f299e116;p=librecmc%2Flibrecmc.git ipq806x: Add support for linux-4.4 1)Changes - Rebased the patches for linux-4.4.7 - Added patch to fix spi nor fifo and dma support - Added patch to configure watchdog barktime 2)Testing Tested on IPQ AP148 Board: a. NOR boot and NAND boot b. ethernet network and ath10k wifi c. ubi sysupgrade UnTested dwc3 usb has not been validated on IPQ board(AP148) 3)Known Issues: Once we flash ubi image on AP148, and if we reset the board, uboot on first boot creates PEB and LEB for dynamic sized partitions, which is incorrect and not what linux expects which causes errors when trying to mount rootfs. In order to test this, we can use the below steps: a. Flash the ubi image on board and don't reset the board b. load the kernel fit image in RAM and boot from there. Signed-off-by: Ram Chandra Jangir --- diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile index 512f9cade5..50e9467c5d 100644 --- a/target/linux/ipq806x/Makefile +++ b/target/linux/ipq806x/Makefile @@ -17,7 +17,9 @@ KERNELNAME:=zImage Image dtbs include $(INCLUDE_DIR)/target.mk DEFAULT_PACKAGES += \ kmod-leds-gpio kmod-gpio-button-hotplug swconfig \ - kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform - + kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform \ + kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev \ + kmod-usb3 kmod-usb-dwc3-qcom kmod-usb-phy-qcom-dwc3 \ + kmod-ath10k ath10k-firmware-qca99x0 wpad-mini $(eval $(call BuildTarget)) diff --git a/target/linux/ipq806x/config-3.18 b/target/linux/ipq806x/config-3.18 deleted file mode 100644 index 40e3a75f8c..0000000000 --- a/target/linux/ipq806x/config-3.18 +++ /dev/null @@ -1,450 +0,0 @@ -CONFIG_ALIGNMENT_TRAP=y -# CONFIG_AMBA_PL08X is not set -# CONFIG_APM_EMULATION is not set -CONFIG_APQ_GCC_8084=y -CONFIG_APQ_MMCC_8084=y -CONFIG_AR8216_PHY=y -CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y -CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=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_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_REQUIRE_GPIOLIB=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_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_ARCH_WANT_OPTIONAL_GPIOLIB=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 is not set -CONFIG_ARM_CPU_SUSPEND=y -CONFIG_ARM_GIC=y -CONFIG_ARM_HAS_SG_CHAIN=y -# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set -CONFIG_ARM_L1_CACHE_SHIFT=6 -CONFIG_ARM_L1_CACHE_SHIFT_6=y -# CONFIG_ARM_LPAE is not set -CONFIG_ARM_PATCH_PHYS_VIRT=y -CONFIG_ARM_QCOM_CPUFREQ=y -# 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_AT803X_PHY=y -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -CONFIG_BOUNCE=y -CONFIG_BUILD_BIN2C=y -# CONFIG_CACHE_L2X0 is not set -CONFIG_CLEANCACHE=y -CONFIG_CLKDEV_LOOKUP=y -CONFIG_CLKSRC_OF=y -CONFIG_CLKSRC_QCOM=y -CONFIG_CLONE_BACKWARDS=y -CONFIG_COMMON_CLK=y -CONFIG_COMMON_CLK_QCOM=y -CONFIG_COMPACTION=y -CONFIG_COREDUMP=y -# CONFIG_CPUFREQ_DT is not set -CONFIG_CPU_32v6K=y -CONFIG_CPU_32v7=y -CONFIG_CPU_ABRT_EV7=y -# 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_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set -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_FREQ_STAT_DETAILS is not set -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 is not set -CONFIG_CPU_TLB_V7=y -CONFIG_CPU_V7=y -CONFIG_CRC16=y -# CONFIG_CRC32_SARWATE is not set -CONFIG_CRC32_SLICEBY8=y -CONFIG_CROSS_MEMORY_ATTACH=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_LZO=y -CONFIG_CRYPTO_RNG2=y -# CONFIG_CRYPTO_SHA1_ARM_NEON is not set -# CONFIG_CRYPTO_SHA512_ARM_NEON is not set -CONFIG_CRYPTO_WORKQUEUE=y -CONFIG_CRYPTO_XZ=y -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_DEBUG_BUGVERBOSE=y -CONFIG_DEBUG_GPIO=y -CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" -CONFIG_DEBUG_PREEMPT=y -# CONFIG_DEBUG_UART_8250 is not set -# CONFIG_DEBUG_UART_PL01X is not set -# CONFIG_DEBUG_USER is not set -CONFIG_DECOMPRESS_GZIP=y -CONFIG_DMADEVICES=y -CONFIG_DMA_ENGINE=y -CONFIG_DMA_OF=y -CONFIG_DMA_VIRTUAL_CHANNELS=y -CONFIG_DTC=y -# CONFIG_DWMAC_GENERIC is not set -CONFIG_DWMAC_IPQ806X=y -# CONFIG_DWMAC_LPC18XX is not set -# CONFIG_DWMAC_MESON is not set -# CONFIG_DWMAC_ROCKCHIP is not set -# CONFIG_DWMAC_SOCFPGA is not set -# CONFIG_DWMAC_STI is not set -# CONFIG_DWMAC_SUNXI is not set -# CONFIG_DW_DMAC_CORE is not set -# CONFIG_DW_DMAC_PCI is not set -CONFIG_DYNAMIC_DEBUG=y -CONFIG_ETHERNET_PACKET_MANGLE=y -CONFIG_FIXED_PHY=y -CONFIG_FREEZER=y -CONFIG_GENERIC_ALLOCATOR=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_GENERIC_CLOCKEVENTS_BUILD=y -CONFIG_GENERIC_CPUFREQ_KRAIT=y -CONFIG_GENERIC_IDLE_POLL_SETUP=y -CONFIG_GENERIC_IO=y -CONFIG_GENERIC_IRQ_SHOW=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_GPIOLIB=y -CONFIG_GPIOLIB_IRQCHIP=y -CONFIG_GPIO_DEVRES=y -# CONFIG_GPIO_MSM_V2 is not set -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_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_BOOTMEM_INFO_NODE is not set -CONFIG_HAVE_BPF_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_ATTRS=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_HW_BREAKPOINT=y -CONFIG_HAVE_IDE=y -CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -CONFIG_HAVE_KERNEL_GZIP=y -CONFIG_HAVE_KERNEL_LZ4=y -CONFIG_HAVE_KERNEL_LZMA=y -CONFIG_HAVE_KERNEL_LZO=y -CONFIG_HAVE_KERNEL_XZ=y -CONFIG_HAVE_MEMBLOCK=y -CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -CONFIG_HAVE_NET_DSA=y -CONFIG_HAVE_OPROFILE=y -CONFIG_HAVE_PERF_EVENTS=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_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=y -CONFIG_HOTPLUG_CPU=y -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_COMPAT=y -CONFIG_I2C_HELPER_AUTO=y -CONFIG_I2C_QUP=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_INITRAMFS_SOURCE="" -CONFIG_IOMMU_API=y -CONFIG_IOMMU_HELPER=y -CONFIG_IOMMU_PGTABLES_L2=y -CONFIG_IOMMU_SUPPORT=y -CONFIG_IPQ_GCC_806X=y -CONFIG_IRQCHIP=y -CONFIG_IRQ_DOMAIN=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_LEDS_REGULATOR is not set -CONFIG_LIBFDT=y -CONFIG_LOCKUP_DETECTOR=y -CONFIG_LZO_COMPRESS=y -CONFIG_LZO_DECOMPRESS=y -CONFIG_MDIO_BITBANG=y -CONFIG_MDIO_BOARDINFO=y -CONFIG_MDIO_GPIO=y -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_MIGRATION=y -CONFIG_MODULES_USE_ELF_REL=y -CONFIG_MSM_GCC_8660=y -CONFIG_MSM_GCC_8960=y -CONFIG_MSM_GCC_8974=y -CONFIG_MSM_IOMMU=y -CONFIG_MSM_MMCC_8960=y -CONFIG_MSM_MMCC_8974=y -CONFIG_MTD_CMDLINE_PARTS=y -# CONFIG_MTD_IMPA7 is not set -CONFIG_MTD_JEDECPROBE=y -CONFIG_MTD_M25P80=y -CONFIG_MTD_NAND=y -CONFIG_MTD_NAND_ECC=y -CONFIG_MTD_NAND_QCOM=y -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_FLOW_LIMIT=y -CONFIG_NET_PTP_CLASSIFY=y -CONFIG_NET_VENDOR_WIZNET=y -CONFIG_NO_BOOTMEM=y -CONFIG_NO_HZ=y -CONFIG_NO_HZ_COMMON=y -CONFIG_NO_HZ_IDLE=y -CONFIG_NR_CPUS=4 -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_IOMMU=y -CONFIG_OF_IRQ=y -CONFIG_OF_MDIO=y -CONFIG_OF_MTD=y -CONFIG_OF_NET=y -CONFIG_OF_PCI=y -CONFIG_OF_PCI_IRQ=y -CONFIG_OF_RESERVED_MEM=y -CONFIG_OLD_SIGACTION=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_PAGEFLAGS_EXTENDED=y -CONFIG_PAGE_OFFSET=0xC0000000 -CONFIG_PCI=y -CONFIG_PCIEAER=y -CONFIG_PCIEPORTBUS=y -CONFIG_PCIE_DW=y -CONFIG_PCIE_QCOM=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCI_MSI=y -CONFIG_PERF_EVENTS=y -CONFIG_PERF_USE_VMALLOC=y -CONFIG_PHYLIB=y -# CONFIG_PHY_QCOM_APQ8064_SATA is not set -CONFIG_PHY_QCOM_IPQ806X_SATA=y -CONFIG_PINCTRL=y -CONFIG_PINCTRL_APQ8064=y -# CONFIG_PINCTRL_APQ8084 is not set -CONFIG_PINCTRL_IPQ8064=y -CONFIG_PINCTRL_MSM=y -# CONFIG_PINCTRL_MSM8960 is not set -CONFIG_PINCTRL_MSM8X74=y -# CONFIG_PL330_DMA is not set -CONFIG_PM=y -CONFIG_PM_CLK=y -# CONFIG_PM_DEBUG is not set -CONFIG_PM_OPP=y -CONFIG_PM_SLEEP=y -CONFIG_PM_SLEEP_SMP=y -CONFIG_POWER_RESET=y -# CONFIG_POWER_RESET_BRCMSTB is not set -# CONFIG_POWER_RESET_GPIO is not set -# CONFIG_POWER_RESET_GPIO_RESTART is not set -# CONFIG_POWER_RESET_LTC2952 is not set -CONFIG_POWER_RESET_MSM=y -# CONFIG_POWER_RESET_SYSCON is not set -CONFIG_POWER_SUPPLY=y -CONFIG_PPS=y -CONFIG_PREEMPT=y -CONFIG_PREEMPT_COUNT=y -# CONFIG_PREEMPT_NONE is not set -CONFIG_PREEMPT_RCU=y -CONFIG_PRINTK_TIME=y -CONFIG_PROC_PAGE_MONITOR=y -CONFIG_PTP_1588_CLOCK=y -CONFIG_QCOM_ADM=y -CONFIG_QCOM_BAM_DMA=y -CONFIG_QCOM_GSBI=y -CONFIG_QCOM_HFPLL=y -CONFIG_QCOM_SCM=y -CONFIG_QCOM_SMEM=y -CONFIG_QCOM_WDT=y -CONFIG_RAS=y -# CONFIG_RCU_BOOST is not set -CONFIG_RCU_CPU_STALL_TIMEOUT=21 -CONFIG_RCU_CPU_STALL_VERBOSE=y -CONFIG_RCU_STALL_COMMON=y -CONFIG_RD_GZIP=y -CONFIG_REGMAP=y -CONFIG_REGMAP_MMIO=y -CONFIG_REGULATOR=y -# CONFIG_REGULATOR_DEBUG is not set -CONFIG_REGULATOR_QCOM_RPM=y -# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set -CONFIG_RESET_CONTROLLER=y -CONFIG_RFS_ACCEL=y -CONFIG_RPS=y -CONFIG_RTC_CLASS=y -# CONFIG_RTC_DRV_CMOS is not set -CONFIG_RWSEM_SPIN_ON_OWNER=y -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_SCHED_HRTICK=y -# CONFIG_SCSI_DMA is not set -# CONFIG_SERIAL_AMBA_PL010 is not set -# CONFIG_SERIAL_AMBA_PL011 is not set -CONFIG_SERIAL_MSM=y -CONFIG_SERIAL_MSM_CONSOLE=y -# CONFIG_SLAB is not set -CONFIG_SLUB=y -CONFIG_SLUB_CPU_PARTIAL=y -CONFIG_SMP=y -CONFIG_SMP_ON_UP=y -CONFIG_SPARSE_IRQ=y -CONFIG_SPI=y -CONFIG_SPI_MASTER=y -CONFIG_SPI_QUP=y -CONFIG_SPMI=y -CONFIG_SPMI_MSM_PMIC_ARB=y -CONFIG_STMMAC_ETH=y -CONFIG_STMMAC_PLATFORM=y -CONFIG_STOP_MACHINE=y -# CONFIG_STRIP_ASM_SYMS is not set -CONFIG_SUSPEND=y -CONFIG_SUSPEND_FREEZER=y -CONFIG_SWCONFIG=y -CONFIG_SWIOTLB=y -CONFIG_SWP_EMULATE=y -CONFIG_SYS_SUPPORTS_APM_EMULATION=y -CONFIG_THERMAL=y -# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set -CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y -# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set -# CONFIG_THERMAL_EMULATION is not set -# CONFIG_THERMAL_GOV_FAIR_SHARE is not set -CONFIG_THERMAL_GOV_STEP_WISE=y -# CONFIG_THERMAL_GOV_USER_SPACE is not set -CONFIG_THERMAL_HWMON=y -CONFIG_THERMAL_OF=y -# CONFIG_THUMB2_KERNEL is not set -CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_TIMER_STATS=y -CONFIG_TREE_PREEMPT_RCU=y -CONFIG_UBIFS_FS=y -# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set -CONFIG_UBIFS_FS_LZO=y -CONFIG_UBIFS_FS_XZ=y -CONFIG_UBIFS_FS_ZLIB=y -CONFIG_UEVENT_HELPER_PATH="" -CONFIG_UID16=y -CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" -CONFIG_UNINLINE_SPIN_UNLOCK=y -CONFIG_USB_SUPPORT=y -CONFIG_USE_OF=y -CONFIG_VECTORS_BASE=0xffff0000 -# CONFIG_VFIO is not set -CONFIG_VFP=y -CONFIG_VFPv3=y -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_WATCHDOG_CORE=y -# CONFIG_WIZNET_W5100 is not set -# CONFIG_WIZNET_W5300 is not set -# CONFIG_WL_TI is not set -# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set -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 -CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ipq806x/config-4.1 b/target/linux/ipq806x/config-4.1 deleted file mode 100644 index b9ce6e79fd..0000000000 --- a/target/linux/ipq806x/config-4.1 +++ /dev/null @@ -1,458 +0,0 @@ -CONFIG_ALIGNMENT_TRAP=y -# CONFIG_AMBA_PL08X is not set -# CONFIG_APM_EMULATION is not set -CONFIG_APQ_GCC_8084=y -CONFIG_APQ_MMCC_8084=y -CONFIG_AR8216_PHY=y -CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=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_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_ARCH_WANT_OPTIONAL_GPIOLIB=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 is not set -CONFIG_ARM_CCI=y -CONFIG_ARM_CCI400_COMMON=y -CONFIG_ARM_CCI400_PMU=y -# CONFIG_ARM_CPUIDLE is not set -CONFIG_ARM_CPU_SUSPEND=y -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_PHYS_VIRT=y -CONFIG_ARM_QCOM_CPUFREQ=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_AT803X_PHY=y -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -CONFIG_BOUNCE=y -CONFIG_BUILD_BIN2C=y -# CONFIG_CACHE_L2X0 is not set -CONFIG_CLEANCACHE=y -CONFIG_CLKDEV_LOOKUP=y -CONFIG_CLKSRC_OF=y -CONFIG_CLKSRC_QCOM=y -CONFIG_CLONE_BACKWARDS=y -CONFIG_COMMON_CLK=y -CONFIG_COMMON_CLK_QCOM=y -CONFIG_COMPACTION=y -CONFIG_COREDUMP=y -# CONFIG_CPUFREQ_DT is not set -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_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set -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_FREQ_STAT_DETAILS is not set -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 is not set -CONFIG_CPU_TLB_V7=y -CONFIG_CPU_V7=y -CONFIG_CRC16=y -# CONFIG_CRC32_SARWATE is not set -CONFIG_CRC32_SLICEBY8=y -CONFIG_CROSS_MEMORY_ATTACH=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_LZO=y -CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_WORKQUEUE=y -CONFIG_CRYPTO_XZ=y -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_DEBUG_BUGVERBOSE=y -CONFIG_DEBUG_GPIO=y -CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" -CONFIG_DEBUG_PREEMPT=y -# CONFIG_DEBUG_UART_8250 is not set -# CONFIG_DEBUG_USER is not set -CONFIG_DECOMPRESS_GZIP=y -CONFIG_DEVMEM=y -CONFIG_DMADEVICES=y -CONFIG_DMA_ENGINE=y -CONFIG_DMA_OF=y -CONFIG_DMA_VIRTUAL_CHANNELS=y -CONFIG_DTC=y -# CONFIG_DWMAC_GENERIC is not set -CONFIG_DWMAC_IPQ806X=y -# CONFIG_DWMAC_LPC18XX is not set -# CONFIG_DWMAC_MESON is not set -# CONFIG_DWMAC_ROCKCHIP is not set -# CONFIG_DWMAC_SOCFPGA is not set -# CONFIG_DWMAC_STI is not set -# CONFIG_DWMAC_SUNXI is not set -# CONFIG_DW_DMAC_PCI is not set -CONFIG_DYNAMIC_DEBUG=y -CONFIG_ETHERNET_PACKET_MANGLE=y -CONFIG_FIXED_PHY=y -CONFIG_FREEZER=y -CONFIG_GENERIC_ALLOCATOR=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_GENERIC_CPUFREQ_KRAIT=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_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_DEVRES=y -# CONFIG_GPIO_MSM_V2 is not set -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_BOOTMEM_INFO_NODE is not set -CONFIG_HAVE_BPF_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_ATTRS=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_HW_BREAKPOINT=y -CONFIG_HAVE_IDE=y -CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -CONFIG_HAVE_KERNEL_GZIP=y -CONFIG_HAVE_KERNEL_LZ4=y -CONFIG_HAVE_KERNEL_LZMA=y -CONFIG_HAVE_KERNEL_LZO=y -CONFIG_HAVE_KERNEL_XZ=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=y -CONFIG_HOTPLUG_CPU=y -# CONFIG_HSU_DMA_PCI 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_COMPAT=y -CONFIG_I2C_HELPER_AUTO=y -CONFIG_I2C_QUP=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_INITRAMFS_SOURCE="" -CONFIG_IOMMU_HELPER=y -# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set -CONFIG_IOMMU_SUPPORT=y -CONFIG_IPQ_GCC_806X=y -# 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_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_MIGRATION=y -CONFIG_MODULES_USE_ELF_REL=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_LCC_8960 is not set -CONFIG_MSM_MMCC_8960=y -CONFIG_MSM_MMCC_8974=y -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_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_FLOW_LIMIT=y -CONFIG_NET_PTP_CLASSIFY=y -CONFIG_NET_VENDOR_WIZNET=y -CONFIG_NO_BOOTMEM=y -CONFIG_NO_HZ=y -CONFIG_NO_HZ_COMMON=y -CONFIG_NO_HZ_IDLE=y -CONFIG_NR_CPUS=4 -CONFIG_OF=y -CONFIG_OF_ADDRESS=y -CONFIG_OF_ADDRESS_PCI=y -CONFIG_OF_EARLY_FLATTREE=y -CONFIG_OF_FLATTREE=y -CONFIG_OF_GPIO=y -CONFIG_OF_IRQ=y -CONFIG_OF_MDIO=y -CONFIG_OF_MTD=y -CONFIG_OF_NET=y -CONFIG_OF_PCI=y -CONFIG_OF_PCI_IRQ=y -CONFIG_OF_RESERVED_MEM=y -CONFIG_OLD_SIGACTION=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_PAGEFLAGS_EXTENDED=y -CONFIG_PAGE_OFFSET=0xC0000000 -CONFIG_PCI=y -CONFIG_PCIEAER=y -CONFIG_PCIEPORTBUS=y -CONFIG_PCIE_DW=y -# CONFIG_PCIE_IPROC is not set -CONFIG_PCIE_PME=y -CONFIG_PCIE_QCOM=y -CONFIG_PCI_DEBUG=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCI_DOMAINS_GENERIC=y -CONFIG_PCI_MSI=y -CONFIG_PERF_EVENTS=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_UFS is not set -CONFIG_PINCTRL=y -CONFIG_PINCTRL_APQ8064=y -# CONFIG_PINCTRL_APQ8084 is not set -CONFIG_PINCTRL_IPQ8064=y -CONFIG_PINCTRL_MSM=y -# CONFIG_PINCTRL_MSM8916 is not set -# CONFIG_PINCTRL_MSM8960 is not set -CONFIG_PINCTRL_MSM8X74=y -# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set -# CONFIG_PL330_DMA is not set -CONFIG_PM=y -CONFIG_PM_CLK=y -# CONFIG_PM_DEBUG is not set -CONFIG_PM_OPP=y -CONFIG_PM_SLEEP=y -CONFIG_PM_SLEEP_SMP=y -CONFIG_POWER_RESET=y -# CONFIG_POWER_RESET_BRCMSTB is not set -CONFIG_POWER_RESET_MSM=y -CONFIG_POWER_SUPPLY=y -CONFIG_PPS=y -CONFIG_PREEMPT=y -CONFIG_PREEMPT_COUNT=y -# CONFIG_PREEMPT_NONE is not set -CONFIG_PREEMPT_RCU=y -CONFIG_PRINTK_TIME=y -CONFIG_PROC_PAGE_MONITOR=y -CONFIG_PTP_1588_CLOCK=y -CONFIG_QCOM_ADM=y -CONFIG_QCOM_BAM_DMA=y -CONFIG_QCOM_GSBI=y -CONFIG_QCOM_HFPLL=y -CONFIG_QCOM_SCM=y -CONFIG_QCOM_SMEM=y -CONFIG_QCOM_WDT=y -CONFIG_RAS=y -# CONFIG_RCU_BOOST is not set -CONFIG_RCU_CPU_STALL_TIMEOUT=21 -CONFIG_RCU_STALL_COMMON=y -CONFIG_RD_GZIP=y -CONFIG_REGMAP=y -CONFIG_REGMAP_MMIO=y -CONFIG_REGULATOR=y -CONFIG_REGULATOR_QCOM_RPM=y -CONFIG_RESET_CONTROLLER=y -CONFIG_RFS_ACCEL=y -CONFIG_RPS=y -CONFIG_RTC_CLASS=y -# CONFIG_RTC_DRV_CMOS is not set -CONFIG_RWSEM_SPIN_ON_OWNER=y -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_SCHED_HRTICK=y -# CONFIG_SCSI_DMA is not set -# CONFIG_SERIAL_AMBA_PL010 is not set -# CONFIG_SERIAL_AMBA_PL011 is not set -CONFIG_SERIAL_MSM=y -CONFIG_SERIAL_MSM_CONSOLE=y -# CONFIG_SLAB is not set -CONFIG_SLUB=y -CONFIG_SLUB_CPU_PARTIAL=y -CONFIG_SMP=y -CONFIG_SMP_ON_UP=y -CONFIG_SPARSE_IRQ=y -CONFIG_SPI=y -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_STOP_MACHINE=y -# CONFIG_STRIP_ASM_SYMS is not set -CONFIG_SUSPEND=y -CONFIG_SUSPEND_FREEZER=y -CONFIG_SWCONFIG=y -CONFIG_SWIOTLB=y -CONFIG_SWP_EMULATE=y -CONFIG_SYS_SUPPORTS_APM_EMULATION=y -CONFIG_THERMAL=y -# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set -CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y -# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set -# CONFIG_THERMAL_EMULATION is not set -# CONFIG_THERMAL_GOV_FAIR_SHARE is not set -CONFIG_THERMAL_GOV_STEP_WISE=y -# CONFIG_THERMAL_GOV_USER_SPACE is not set -CONFIG_THERMAL_HWMON=y -CONFIG_THERMAL_OF=y -# CONFIG_THUMB2_KERNEL is not set -CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_TIMER_STATS=y -CONFIG_UBIFS_FS=y -# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set -CONFIG_UBIFS_FS_LZO=y -CONFIG_UBIFS_FS_XZ=y -CONFIG_UBIFS_FS_ZLIB=y -CONFIG_UEVENT_HELPER_PATH="" -CONFIG_UID16=y -CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" -CONFIG_UNINLINE_SPIN_UNLOCK=y -CONFIG_USB_SUPPORT=y -CONFIG_USE_OF=y -CONFIG_VDSO=y -CONFIG_VECTORS_BASE=0xffff0000 -CONFIG_VFP=y -CONFIG_VFPv3=y -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_WATCHDOG_CORE=y -# CONFIG_WIZNET_W5100 is not set -# CONFIG_WIZNET_W5300 is not set -# CONFIG_WL_TI is not set -# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set -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 -CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4 new file mode 100644 index 0000000000..408fc6d3e9 --- /dev/null +++ b/target/linux/ipq806x/config-4.4 @@ -0,0 +1,453 @@ +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_AMBA_PL08X is not set +# CONFIG_APM_EMULATION is not set +CONFIG_APQ_GCC_8084=y +CONFIG_APQ_MMCC_8084=y +CONFIG_AR8216_PHY=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=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_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_ARCH_WANT_OPTIONAL_GPIOLIB=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 is not set +CONFIG_ARM_CCI=y +CONFIG_ARM_CCI400_COMMON=y +CONFIG_ARM_CCI400_PMU=y +CONFIG_ARM_CCI_PMU=y +CONFIG_ARM_CPU_SUSPEND=y +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_PHYS_VIRT=y +CONFIG_ARM_PMU=y +CONFIG_ARM_QCOM_CPUFREQ=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_AT803X_PHY=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_BOUNCE=y +CONFIG_BUILD_BIN2C=y +# CONFIG_CACHE_L2X0 is not set +CONFIG_CLEANCACHE=y +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_COMPACTION=y +CONFIG_COREDUMP=y +# CONFIG_CPUFREQ_DT is not set +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_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +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 is not set +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_WORKQUEUE=y +CONFIG_CRYPTO_XZ=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_GPIO=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_DEBUG_PREEMPT=y +# CONFIG_DEBUG_UART_8250 is not set +# CONFIG_DEBUG_USER is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DEVMEM=y +CONFIG_DMADEVICES=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DTC=y +# CONFIG_DWMAC_GENERIC is not set +CONFIG_DWMAC_IPQ806X=y +# CONFIG_DWMAC_SUNXI is not set +# CONFIG_DW_DMAC_PCI is not set +CONFIG_DYNAMIC_DEBUG=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_FREEZER=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPUFREQ_KRAIT=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_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_DEVRES=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_BOOTMEM_INFO_NODE is not set +CONFIG_HAVE_BPF_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_ATTRS=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_HW_BREAKPOINT=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZ4=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_XZ=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=y +CONFIG_HOTPLUG_CPU=y +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_COMPAT=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_QUP=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IOMMU_HELPER=y +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IPQ_GCC_806X=y +# 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_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_MIGRATION=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_REL=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_LCC_8960 is not set +CONFIG_MSM_MMCC_8960=y +CONFIG_MSM_MMCC_8974=y +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_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_AR8XXX is not set +CONFIG_NET_DSA_HWMON=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_BOOTMEM=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_ADDRESS_PCI=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_NET=y +CONFIG_OF_PCI=y +CONFIG_OF_PCI_IRQ=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_PME=y +CONFIG_PCIE_QCOM=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PERF_EVENTS=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_UFS is not set +CONFIG_PINCTRL=y +CONFIG_PINCTRL_APQ8064=y +# CONFIG_PINCTRL_APQ8084 is not set +CONFIG_PINCTRL_IPQ8064=y +CONFIG_PINCTRL_MSM=y +# CONFIG_PINCTRL_MSM8660 is not set +# CONFIG_PINCTRL_MSM8916 is not set +# CONFIG_PINCTRL_MSM8960 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=y +CONFIG_PM_CLK=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_GENERIC_DOMAINS_SLEEP=y +CONFIG_PM_OPP=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_MSM=y +CONFIG_POWER_SUPPLY=y +CONFIG_PPS=y +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_RCU=y +CONFIG_PRINTK_TIME=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_QCOM_ADM=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_GDSC=y +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_HFPLL=y +# CONFIG_QCOM_PM is not set +CONFIG_QCOM_SCM=y +CONFIG_QCOM_SCM_32=y +# CONFIG_QCOM_SMD is not set +CONFIG_QCOM_SMEM=y +CONFIG_QCOM_WDT=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +# CONFIG_RCU_BOOST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_STALL_COMMON=y +CONFIG_RD_GZIP=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_QCOM_RPM=y +# CONFIG_REGULATOR_QCOM_SPMI is not set +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_CMOS is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_SCHED_HRTICK=y +# CONFIG_SCHED_INFO is not set +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_FSL=y +# CONFIG_SERIAL_AMBA_PL010 is not set +# 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_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_STRIP_ASM_SYMS is not set +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_SWCONFIG=y +CONFIG_SWIOTLB=y +CONFIG_SWP_EMULATE=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +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_TIMER_STATS=y +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_XZ=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VDSO=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WL_TI is not set +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +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 +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile index ff8b2c7d9b..ab3c846f1f 100644 --- a/target/linux/ipq806x/image/Makefile +++ b/target/linux/ipq806x/image/Makefile @@ -93,6 +93,7 @@ define Device/AP148 BLOCKSIZE := 128KiB PAGESIZE := 2048 BOARD_NAME := ap148 + DEVICE_TITLE := Qualcom AP148 endef define Device/AP148-legacy @@ -102,6 +103,7 @@ define Device/AP148-legacy BLOCKSIZE := 128KiB PAGESIZE := 2048 BOARD_NAME := ap148 + DEVICE_TITLE := Qualcom AP148 (legacy) endef define Device/D7800 @@ -113,6 +115,7 @@ define Device/D7800 BLOCKSIZE := 128KiB PAGESIZE := 2048 BOARD_NAME := d7800 + DEVICE_TITLE := Netgear Nighthawk X4 D7800 endef define Device/DB149 @@ -120,6 +123,7 @@ define Device/DB149 DEVICE_DTS := qcom-ipq8064-db149 KERNEL_INSTALL := 1 BOARD_NAME := db149 + DEVICE_TITLE := Qualcom DB149 endef define Device/R7500 @@ -131,6 +135,7 @@ define Device/R7500 BLOCKSIZE := 128KiB PAGESIZE := 2048 BOARD_NAME := r7500 + DEVICE_TITLE := Netgear Nighthawk X4 R7500 endef TARGET_DEVICES += AP148 AP148-legacy D7800 DB149 R7500 diff --git a/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch deleted file mode 100644 index e110bf726c..0000000000 --- a/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch +++ /dev/null @@ -1,522 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: spi: qup: Add DMA capabilities -From: Andy Gross -X-Patchwork-Id: 4432401 -Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org> -To: Mark Brown -Cc: linux-spi@vger.kernel.org, Sagar Dharia , - Daniel Sneddon , - Bjorn Andersson , - "Ivan T. Ivanov" , - linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - linux-arm-msm@vger.kernel.org, Andy Gross -Date: Thu, 26 Jun 2014 16:06:21 -0500 - -This patch adds DMA capabilities to the spi-qup driver. If DMA channels are -present, the QUP will use DMA instead of block mode for transfers to/from SPI -peripherals for transactions larger than the length of a block. - -Signed-off-by: Andy Gross - ---- -.../devicetree/bindings/spi/qcom,spi-qup.txt | 10 + - drivers/spi/spi-qup.c | 361 ++++++++++++++++++-- - 2 files changed, 350 insertions(+), 21 deletions(-) - ---- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt -+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt -@@ -27,6 +27,11 @@ Optional properties: - - spi-max-frequency: Specifies maximum SPI clock frequency, - Units - Hz. Definition as per - Documentation/devicetree/bindings/spi/spi-bus.txt -+- dmas : Two DMA channel specifiers following the convention outlined -+ in bindings/dma/dma.txt -+- dma-names: Names for the dma channels, if present. There must be at -+ least one channel named "tx" for transmit and named "rx" for -+ receive. - - num-cs: total number of chipselects - - cs-gpios: should specify GPIOs used for chipselects. - The gpios will be referred to as reg = in the SPI child -@@ -51,6 +56,10 @@ Example: - clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>; - clock-names = "core", "iface"; - -+ dmas = <&blsp2_bam 2>, -+ <&blsp2_bam 3>; -+ dma-names = "rx", "tx"; -+ - pinctrl-names = "default"; - pinctrl-0 = <&spi8_default>; - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -22,6 +22,8 @@ - #include - #include - #include -+#include -+#include - - #define QUP_CONFIG 0x0000 - #define QUP_STATE 0x0004 -@@ -116,6 +118,8 @@ - - #define SPI_NUM_CHIPSELECTS 4 - -+#define SPI_MAX_XFER (SZ_64K - 64) -+ - /* high speed mode is when bus rate is greater then 26MHz */ - #define SPI_HS_MIN_RATE 26000000 - #define SPI_MAX_RATE 50000000 -@@ -143,6 +147,17 @@ struct spi_qup { - int tx_bytes; - int rx_bytes; - int qup_v1; -+ -+ int use_dma; -+ -+ struct dma_chan *rx_chan; -+ struct dma_slave_config rx_conf; -+ struct dma_chan *tx_chan; -+ struct dma_slave_config tx_conf; -+ dma_addr_t rx_dma; -+ dma_addr_t tx_dma; -+ void *dummy; -+ atomic_t dma_outstanding; - }; - - -@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp - } - } - -+static void qup_dma_callback(void *data) -+{ -+ struct spi_qup *controller = data; -+ -+ if (atomic_dec_and_test(&controller->dma_outstanding)) -+ complete(&controller->done); -+} -+ -+static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer) -+{ -+ struct dma_async_tx_descriptor *rxd, *txd; -+ dma_cookie_t rx_cookie, tx_cookie; -+ u32 xfer_len, rx_align = 0, tx_align = 0, n_words; -+ struct scatterlist tx_sg[2], rx_sg[2]; -+ int ret = 0; -+ u32 bytes_to_xfer = xfer->len; -+ u32 offset = 0; -+ u32 rx_nents = 0, tx_nents = 0; -+ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0; -+ -+ -+ if (xfer->rx_buf) { -+ rx_dma = dma_map_single(controller->dev, xfer->rx_buf, -+ xfer->len, DMA_FROM_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, rx_dma)) { -+ ret = -ENOMEM; -+ return ret; -+ } -+ -+ /* check to see if we need dummy buffer for leftover bytes */ -+ rx_align = xfer->len % controller->in_blk_sz; -+ if (rx_align) { -+ rx_dummy_dma = dma_map_single(controller->dev, -+ controller->dummy, controller->in_fifo_sz, -+ DMA_FROM_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, rx_dummy_dma)) { -+ ret = -ENOMEM; -+ goto err_map_rx_dummy; -+ } -+ } -+ } -+ -+ if (xfer->tx_buf) { -+ tx_dma = dma_map_single(controller->dev, -+ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, tx_dma)) { -+ ret = -ENOMEM; -+ goto err_map_tx; -+ } -+ -+ /* check to see if we need dummy buffer for leftover bytes */ -+ tx_align = xfer->len % controller->out_blk_sz; -+ if (tx_align) { -+ memcpy(controller->dummy + SZ_1K, -+ xfer->tx_buf + xfer->len - tx_align, -+ tx_align); -+ memset(controller->dummy + SZ_1K + tx_align, 0, -+ controller->out_blk_sz - tx_align); -+ -+ tx_dummy_dma = dma_map_single(controller->dev, -+ controller->dummy + SZ_1K, -+ controller->out_blk_sz, DMA_TO_DEVICE); -+ -+ if (dma_mapping_error(controller->dev, tx_dummy_dma)) { -+ ret = -ENOMEM; -+ goto err_map_tx_dummy; -+ } -+ } -+ } -+ -+ atomic_set(&controller->dma_outstanding, 0); -+ -+ while (bytes_to_xfer > 0) { -+ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER); -+ n_words = DIV_ROUND_UP(xfer_len, controller->w_size); -+ -+ /* write out current word count to controller */ -+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); -+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); -+ -+ reinit_completion(&controller->done); -+ -+ if (xfer->tx_buf) { -+ /* recalc align for each transaction */ -+ tx_align = xfer_len % controller->out_blk_sz; -+ -+ if (tx_align) -+ tx_nents = 2; -+ else -+ tx_nents = 1; -+ -+ /* initialize scatterlists */ -+ sg_init_table(tx_sg, tx_nents); -+ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align; -+ sg_dma_address(&tx_sg[0]) = tx_dma + offset; -+ -+ /* account for non block size transfer */ -+ if (tx_align) { -+ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz; -+ sg_dma_address(&tx_sg[1]) = tx_dummy_dma; -+ } -+ -+ txd = dmaengine_prep_slave_sg(controller->tx_chan, -+ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0); -+ if (!txd) { -+ ret = -ENOMEM; -+ goto err_unmap; -+ } -+ -+ atomic_inc(&controller->dma_outstanding); -+ -+ txd->callback = qup_dma_callback; -+ txd->callback_param = controller; -+ -+ tx_cookie = dmaengine_submit(txd); -+ -+ dma_async_issue_pending(controller->tx_chan); -+ } -+ -+ if (xfer->rx_buf) { -+ /* recalc align for each transaction */ -+ rx_align = xfer_len % controller->in_blk_sz; -+ -+ if (rx_align) -+ rx_nents = 2; -+ else -+ rx_nents = 1; -+ -+ /* initialize scatterlists */ -+ sg_init_table(rx_sg, rx_nents); -+ sg_dma_address(&rx_sg[0]) = rx_dma + offset; -+ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align; -+ -+ /* account for non block size transfer */ -+ if (rx_align) { -+ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz; -+ sg_dma_address(&rx_sg[1]) = rx_dummy_dma; -+ } -+ -+ rxd = dmaengine_prep_slave_sg(controller->rx_chan, -+ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0); -+ if (!rxd) { -+ ret = -ENOMEM; -+ goto err_unmap; -+ } -+ -+ atomic_inc(&controller->dma_outstanding); -+ -+ rxd->callback = qup_dma_callback; -+ rxd->callback_param = controller; -+ -+ rx_cookie = dmaengine_submit(rxd); -+ -+ dma_async_issue_pending(controller->rx_chan); -+ } -+ -+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -+ dev_warn(controller->dev, "cannot set EXECUTE state\n"); -+ goto err_unmap; -+ } -+ -+ if (!wait_for_completion_timeout(&controller->done, -+ msecs_to_jiffies(1000))) { -+ ret = -ETIMEDOUT; -+ -+ /* clear out all the DMA transactions */ -+ if (xfer->tx_buf) -+ dmaengine_terminate_all(controller->tx_chan); -+ if (xfer->rx_buf) -+ dmaengine_terminate_all(controller->rx_chan); -+ -+ goto err_unmap; -+ } -+ -+ if (rx_align) -+ memcpy(xfer->rx_buf + offset + xfer->len - rx_align, -+ controller->dummy, rx_align); -+ -+ /* adjust remaining bytes to transfer */ -+ bytes_to_xfer -= xfer_len; -+ offset += xfer_len; -+ -+ -+ /* reset mini-core state so we can program next transaction */ -+ if (spi_qup_set_state(controller, QUP_STATE_RESET)) { -+ dev_err(controller->dev, "cannot set RESET state\n"); -+ goto err_unmap; -+ } -+ } -+ -+ ret = 0; -+ -+err_unmap: -+ if (tx_align) -+ dma_unmap_single(controller->dev, tx_dummy_dma, -+ controller->out_fifo_sz, DMA_TO_DEVICE); -+err_map_tx_dummy: -+ if (xfer->tx_buf) -+ dma_unmap_single(controller->dev, tx_dma, xfer->len, -+ DMA_TO_DEVICE); -+err_map_tx: -+ if (rx_align) -+ dma_unmap_single(controller->dev, rx_dummy_dma, -+ controller->in_fifo_sz, DMA_FROM_DEVICE); -+err_map_rx_dummy: -+ if (xfer->rx_buf) -+ dma_unmap_single(controller->dev, rx_dma, xfer->len, -+ DMA_FROM_DEVICE); -+ -+ return ret; -+} -+ - static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) - { - struct spi_qup *controller = dev_id; -@@ -315,11 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i - error = -EIO; - } - -- if (opflags & QUP_OP_IN_SERVICE_FLAG) -- spi_qup_fifo_read(controller, xfer); -+ if (!controller->use_dma) { -+ if (opflags & QUP_OP_IN_SERVICE_FLAG) -+ spi_qup_fifo_read(controller, xfer); - -- if (opflags & QUP_OP_OUT_SERVICE_FLAG) -- spi_qup_fifo_write(controller, xfer); -+ if (opflags & QUP_OP_OUT_SERVICE_FLAG) -+ spi_qup_fifo_write(controller, xfer); -+ } - - spin_lock_irqsave(&controller->lock, flags); - controller->error = error; -@@ -339,6 +571,8 @@ static int spi_qup_io_config(struct spi_ - struct spi_qup *controller = spi_master_get_devdata(spi->master); - u32 config, iomode, mode; - int ret, n_words, w_size; -+ size_t dma_align = dma_get_cache_alignment(); -+ u32 dma_available = 0; - - if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { - dev_err(controller->dev, "too big size for loopback %d > %d\n", -@@ -367,6 +601,11 @@ static int spi_qup_io_config(struct spi_ - n_words = xfer->len / w_size; - controller->w_size = w_size; - -+ if (controller->rx_chan && -+ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && -+ IS_ALIGNED((size_t)xfer->rx_buf, dma_align)) -+ dma_available = 1; -+ - if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { - mode = QUP_IO_M_MODE_FIFO; - writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); -@@ -374,19 +613,31 @@ static int spi_qup_io_config(struct spi_ - /* must be zero for FIFO */ - writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); -- } else { -+ controller->use_dma = 0; -+ } else if (!dma_available) { - mode = QUP_IO_M_MODE_BLOCK; - writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); - /* must be zero for BLOCK and BAM */ - writel_relaxed(0, controller->base + QUP_MX_READ_CNT); - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); -+ controller->use_dma = 0; -+ } else { -+ mode = QUP_IO_M_MODE_DMOV; -+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); -+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); -+ controller->use_dma = 1; - } - - iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); - /* Set input and output transfer mode */ - iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); -- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); -+ -+ if (!controller->use_dma) -+ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); -+ else -+ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; -+ - iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); - iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); - -@@ -419,6 +670,14 @@ static int spi_qup_io_config(struct spi_ - config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); - config |= xfer->bits_per_word - 1; - config |= QUP_CONFIG_SPI_MODE; -+ -+ if (controller->use_dma) { -+ if (!xfer->tx_buf) -+ config |= QUP_CONFIG_NO_OUTPUT; -+ if (!xfer->rx_buf) -+ config |= QUP_CONFIG_NO_INPUT; -+ } -+ - writel_relaxed(config, controller->base + QUP_CONFIG); - - /* only write to OPERATIONAL_MASK when register is present */ -@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s - controller->tx_bytes = 0; - spin_unlock_irqrestore(&controller->lock, flags); - -- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -- dev_warn(controller->dev, "cannot set RUN state\n"); -- goto exit; -- } -+ if (controller->use_dma) { -+ ret = spi_qup_do_dma(controller, xfer); -+ } else { -+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -+ dev_warn(controller->dev, "cannot set RUN state\n"); -+ goto exit; -+ } - -- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { -- dev_warn(controller->dev, "cannot set PAUSE state\n"); -- goto exit; -- } -+ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { -+ dev_warn(controller->dev, "cannot set PAUSE state\n"); -+ goto exit; -+ } - -- spi_qup_fifo_write(controller, xfer); -+ spi_qup_fifo_write(controller, xfer); - -- if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -- dev_warn(controller->dev, "cannot set EXECUTE state\n"); -- goto exit; -- } -+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) { -+ dev_warn(controller->dev, "cannot set EXECUTE state\n"); -+ goto exit; -+ } - -- if (!wait_for_completion_timeout(&controller->done, timeout)) -- ret = -ETIMEDOUT; -+ if (!wait_for_completion_timeout(&controller->done, timeout)) -+ ret = -ETIMEDOUT; -+ } - exit: - spi_qup_set_state(controller, QUP_STATE_RESET); - spin_lock_irqsave(&controller->lock, flags); -@@ -554,6 +817,7 @@ static int spi_qup_probe(struct platform - master->transfer_one = spi_qup_transfer_one; - master->dev.of_node = pdev->dev.of_node; - master->auto_runtime_pm = true; -+ master->dma_alignment = dma_get_cache_alignment(); - - platform_set_drvdata(pdev, master); - -@@ -619,6 +883,56 @@ static int spi_qup_probe(struct platform - QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN, - base + QUP_ERROR_FLAGS_EN); - -+ /* allocate dma resources, if available */ -+ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx"); -+ if (controller->rx_chan) { -+ controller->tx_chan = -+ dma_request_slave_channel(&pdev->dev, "tx"); -+ -+ if (!controller->tx_chan) { -+ dev_err(&pdev->dev, "Failed to allocate dma tx chan"); -+ dma_release_channel(controller->rx_chan); -+ } -+ -+ /* set DMA parameters */ -+ controller->rx_conf.device_fc = 1; -+ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO; -+ controller->rx_conf.src_maxburst = controller->in_blk_sz; -+ -+ controller->tx_conf.device_fc = 1; -+ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO; -+ controller->tx_conf.dst_maxburst = controller->out_blk_sz; -+ -+ if (dmaengine_slave_config(controller->rx_chan, -+ &controller->rx_conf)) { -+ dev_err(&pdev->dev, "failed to configure RX channel\n"); -+ -+ dma_release_channel(controller->rx_chan); -+ dma_release_channel(controller->tx_chan); -+ controller->tx_chan = NULL; -+ controller->rx_chan = NULL; -+ } else if (dmaengine_slave_config(controller->tx_chan, -+ &controller->tx_conf)) { -+ dev_err(&pdev->dev, "failed to configure TX channel\n"); -+ -+ dma_release_channel(controller->rx_chan); -+ dma_release_channel(controller->tx_chan); -+ controller->tx_chan = NULL; -+ controller->rx_chan = NULL; -+ } -+ -+ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE, -+ GFP_KERNEL); -+ -+ if (!controller->dummy) { -+ dma_release_channel(controller->rx_chan); -+ dma_release_channel(controller->tx_chan); -+ controller->tx_chan = NULL; -+ controller->rx_chan = NULL; -+ } -+ } -+ -+ - writel_relaxed(0, base + SPI_CONFIG); - writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); - -@@ -731,6 +1045,11 @@ static int spi_qup_remove(struct platfor - if (ret) - return ret; - -+ if (controller->rx_chan) -+ dma_release_channel(controller->rx_chan); -+ if (controller->tx_chan) -+ dma_release_channel(controller->tx_chan); -+ - clk_disable_unprepare(controller->cclk); - clk_disable_unprepare(controller->iclk); - diff --git a/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch b/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch deleted file mode 100644 index 62ee5b4fad..0000000000 --- a/target/linux/ipq806x/patches-3.18/002-v3-spi-qup-Fix-incorrect-block-transfers.patch +++ /dev/null @@ -1,376 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3] spi: qup: Fix incorrect block transfers -From: Andy Gross -X-Patchwork-Id: 5007321 -Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org> -To: Mark Brown -Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org, - "Ivan T. Ivanov" , - Bjorn Andersson , - Kumar Gala , Andy Gross -Date: Tue, 30 Sep 2014 16:21:28 -0500 - -This patch fixes a number of errors with the QUP block transfer mode. Errors -manifested themselves as input underruns, output overruns, and timed out -transactions. - -The block mode does not require the priming that occurs in FIFO mode. At the -moment that the QUP is placed into the RUN state, the QUP will immediately raise -an interrupt if the request is a write. Therefore, there is no need to prime -the pump. - -In addition, the block transfers require that whole blocks of data are -read/written at a time. The last block of data that completes a transaction may -contain less than a full blocks worth of data. - -Each block of data results in an input/output service interrupt accompanied with -a input/output block flag set. Additional block reads/writes require clearing -of the service flag. It is ok to check for additional blocks of data in the -ISR, but you have to ack every block you transfer. Imbalanced acks result in -early return from complete transactions with pending interrupts that still have -to be ack'd. The next transaction can be affected by these interrupts. -Transactions are deemed complete when the MAX_INPUT or MAX_OUTPUT flag are set. - -Changes from v2: -- Added in additional completion check so that transaction done is not - prematurely signaled. -- Fixed various review comments. - -Changes from v1: -- Split out read/write block function. -- Removed extraneous checks for transfer length - -Signed-off-by: Andy Gross - ---- -drivers/spi/spi-qup.c | 201 ++++++++++++++++++++++++++++++++++++------------- - 1 file changed, 148 insertions(+), 53 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -82,6 +82,8 @@ - #define QUP_IO_M_MODE_BAM 3 - - /* QUP_OPERATIONAL fields */ -+#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) -+#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) - #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) - #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) - #define QUP_OP_IN_SERVICE_FLAG BIT(9) -@@ -147,6 +149,7 @@ struct spi_qup { - int tx_bytes; - int rx_bytes; - int qup_v1; -+ int mode; - - int use_dma; - -@@ -213,30 +216,14 @@ static int spi_qup_set_state(struct spi_ - return 0; - } - -- --static void spi_qup_fifo_read(struct spi_qup *controller, -- struct spi_transfer *xfer) -+static void spi_qup_fill_read_buffer(struct spi_qup *controller, -+ struct spi_transfer *xfer, u32 data) - { - u8 *rx_buf = xfer->rx_buf; -- u32 word, state; -- int idx, shift, w_size; -- -- w_size = controller->w_size; -- -- while (controller->rx_bytes < xfer->len) { -- -- state = readl_relaxed(controller->base + QUP_OPERATIONAL); -- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) -- break; -- -- word = readl_relaxed(controller->base + QUP_INPUT_FIFO); -- -- if (!rx_buf) { -- controller->rx_bytes += w_size; -- continue; -- } -+ int idx, shift; - -- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { -+ if (rx_buf) -+ for (idx = 0; idx < controller->w_size; idx++) { - /* - * The data format depends on bytes per SPI word: - * 4 bytes: 0x12345678 -@@ -244,41 +231,139 @@ static void spi_qup_fifo_read(struct spi - * 1 byte : 0x00000012 - */ - shift = BITS_PER_BYTE; -- shift *= (w_size - idx - 1); -- rx_buf[controller->rx_bytes] = word >> shift; -+ shift *= (controller->w_size - idx - 1); -+ rx_buf[controller->rx_bytes + idx] = data >> shift; -+ } -+ -+ controller->rx_bytes += controller->w_size; -+} -+ -+static void spi_qup_prepare_write_data(struct spi_qup *controller, -+ struct spi_transfer *xfer, u32 *data) -+{ -+ const u8 *tx_buf = xfer->tx_buf; -+ u32 val; -+ int idx; -+ -+ *data = 0; -+ -+ if (tx_buf) -+ for (idx = 0; idx < controller->w_size; idx++) { -+ val = tx_buf[controller->tx_bytes + idx]; -+ *data |= val << (BITS_PER_BYTE * (3 - idx)); - } -+ -+ controller->tx_bytes += controller->w_size; -+} -+ -+static void spi_qup_fifo_read(struct spi_qup *controller, -+ struct spi_transfer *xfer) -+{ -+ u32 data; -+ -+ /* clear service request */ -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ while (controller->rx_bytes < xfer->len) { -+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_IN_FIFO_NOT_EMPTY)) -+ break; -+ -+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO); -+ -+ spi_qup_fill_read_buffer(controller, xfer, data); - } - } - - static void spi_qup_fifo_write(struct spi_qup *controller, -- struct spi_transfer *xfer) -+ struct spi_transfer *xfer) - { -- const u8 *tx_buf = xfer->tx_buf; -- u32 word, state, data; -- int idx, w_size; -+ u32 data; - -- w_size = controller->w_size; -+ /* clear service request */ -+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); - - while (controller->tx_bytes < xfer->len) { - -- state = readl_relaxed(controller->base + QUP_OPERATIONAL); -- if (state & QUP_OP_OUT_FIFO_FULL) -+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_OUT_FIFO_FULL) - break; - -- word = 0; -- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { -+ spi_qup_prepare_write_data(controller, xfer, &data); -+ writel_relaxed(data, controller->base + QUP_OUTPUT_FIFO); - -- if (!tx_buf) { -- controller->tx_bytes += w_size; -- break; -- } -+ } -+} - -- data = tx_buf[controller->tx_bytes]; -- word |= data << (BITS_PER_BYTE * (3 - idx)); -- } -+static void spi_qup_block_read(struct spi_qup *controller, -+ struct spi_transfer *xfer) -+{ -+ u32 data; -+ u32 reads_per_blk = controller->in_blk_sz >> 2; -+ u32 num_words = (xfer->len - controller->rx_bytes) / controller->w_size; -+ int i; -+ -+ do { -+ /* ACK by clearing service flag */ -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ /* transfer up to a block size of data in a single pass */ -+ for (i = 0; num_words && i < reads_per_blk; i++, num_words--) { -+ -+ /* read data and fill up rx buffer */ -+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO); -+ spi_qup_fill_read_buffer(controller, xfer, data); -+ } -+ -+ /* check to see if next block is ready */ -+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_IN_BLOCK_READ_REQ)) -+ break; - -- writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); -- } -+ } while (num_words); -+ -+ /* -+ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block -+ * reads, it has to be cleared again at the very end -+ */ -+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_MAX_INPUT_DONE_FLAG) -+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+} -+ -+static void spi_qup_block_write(struct spi_qup *controller, -+ struct spi_transfer *xfer) -+{ -+ u32 data; -+ u32 writes_per_blk = controller->out_blk_sz >> 2; -+ u32 num_words = (xfer->len - controller->tx_bytes) / controller->w_size; -+ int i; -+ -+ do { -+ /* ACK by clearing service flag */ -+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, -+ controller->base + QUP_OPERATIONAL); -+ -+ /* transfer up to a block size of data in a single pass */ -+ for (i = 0; num_words && i < writes_per_blk; i++, num_words--) { -+ -+ /* swizzle the bytes for output and write out */ -+ spi_qup_prepare_write_data(controller, xfer, &data); -+ writel_relaxed(data, -+ controller->base + QUP_OUTPUT_FIFO); -+ } -+ -+ /* check to see if next block is ready */ -+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) & -+ QUP_OP_OUT_BLOCK_WRITE_REQ)) -+ break; -+ -+ } while (num_words); - } - - static void qup_dma_callback(void *data) -@@ -515,9 +600,9 @@ static irqreturn_t spi_qup_qup_irq(int i - - writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); - writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); -- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); - - if (!xfer) { -+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); - dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", - qup_err, spi_err, opflags); - return IRQ_HANDLED; -@@ -546,11 +631,19 @@ static irqreturn_t spi_qup_qup_irq(int i - } - - if (!controller->use_dma) { -- if (opflags & QUP_OP_IN_SERVICE_FLAG) -- spi_qup_fifo_read(controller, xfer); -+ if (opflags & QUP_OP_IN_SERVICE_FLAG) { -+ if (opflags & QUP_OP_IN_BLOCK_READ_REQ) -+ spi_qup_block_read(controller, xfer); -+ else -+ spi_qup_fifo_read(controller, xfer); -+ } - -- if (opflags & QUP_OP_OUT_SERVICE_FLAG) -- spi_qup_fifo_write(controller, xfer); -+ if (opflags & QUP_OP_OUT_SERVICE_FLAG) { -+ if (opflags & QUP_OP_OUT_BLOCK_WRITE_REQ) -+ spi_qup_block_write(controller, xfer); -+ else -+ spi_qup_fifo_write(controller, xfer); -+ } - } - - spin_lock_irqsave(&controller->lock, flags); -@@ -558,7 +651,8 @@ static irqreturn_t spi_qup_qup_irq(int i - controller->xfer = xfer; - spin_unlock_irqrestore(&controller->lock, flags); - -- if (controller->rx_bytes == xfer->len || error) -+ if ((controller->rx_bytes == xfer->len && -+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) - complete(&controller->done); - - return IRQ_HANDLED; -@@ -569,7 +663,7 @@ static irqreturn_t spi_qup_qup_irq(int i - static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) - { - struct spi_qup *controller = spi_master_get_devdata(spi->master); -- u32 config, iomode, mode; -+ u32 config, iomode; - int ret, n_words, w_size; - size_t dma_align = dma_get_cache_alignment(); - u32 dma_available = 0; -@@ -607,7 +701,7 @@ static int spi_qup_io_config(struct spi_ - dma_available = 1; - - if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { -- mode = QUP_IO_M_MODE_FIFO; -+ controller->mode = QUP_IO_M_MODE_FIFO; - writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); - /* must be zero for FIFO */ -@@ -615,7 +709,7 @@ static int spi_qup_io_config(struct spi_ - writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); - controller->use_dma = 0; - } else if (!dma_available) { -- mode = QUP_IO_M_MODE_BLOCK; -+ controller->mode = QUP_IO_M_MODE_BLOCK; - writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); - writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); - /* must be zero for BLOCK and BAM */ -@@ -623,7 +717,7 @@ static int spi_qup_io_config(struct spi_ - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); - controller->use_dma = 0; - } else { -- mode = QUP_IO_M_MODE_DMOV; -+ controller->mode = QUP_IO_M_MODE_DMOV; - writel_relaxed(0, controller->base + QUP_MX_READ_CNT); - writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); - controller->use_dma = 1; -@@ -638,8 +732,8 @@ static int spi_qup_io_config(struct spi_ - else - iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; - -- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); -- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); -+ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); -+ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); - - writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); - -@@ -724,7 +818,8 @@ static int spi_qup_transfer_one(struct s - goto exit; - } - -- spi_qup_fifo_write(controller, xfer); -+ if (controller->mode == QUP_IO_M_MODE_FIFO) -+ spi_qup_fifo_write(controller, xfer); - - if (spi_qup_set_state(controller, QUP_STATE_RUN)) { - dev_warn(controller->dev, "cannot set EXECUTE state\n"); -@@ -741,6 +836,7 @@ exit: - if (!ret) - ret = controller->error; - spin_unlock_irqrestore(&controller->lock, flags); -+ - return ret; - } - diff --git a/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch b/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch deleted file mode 100644 index 7052227810..0000000000 --- a/target/linux/ipq806x/patches-3.18/003-spi-qup-Ensure-done-detection.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Thu, 15 Jan 2015 17:56:02 -0800 -Subject: [PATCH] spi: qup: Ensure done detection - -This patch fixes an issue where a SPI transaction has completed, but the done -condition is missed. This occurs because at the time of interrupt the -MAX_INPUT_DONE_FLAG is not asserted. However, in the process of reading blocks -of data from the FIFO, the last portion of data comes in. - -The opflags read at the beginning of the irq handler no longer matches the -current opflag state. To get around this condition, the block read function -should update the opflags so that done detection is correct after the return. - -Change-Id: If109e0eeb432f96000d765c4b34dbb2269f8093f -Signed-off-by: Andy Gross ---- - drivers/spi/spi-qup.c | 12 +++++++----- - 1 file changed, 7 insertions(+), 5 deletions(-) - ---- a/drivers/spi/spi-qup.c -+++ b/drivers/spi/spi-qup.c -@@ -298,7 +298,7 @@ static void spi_qup_fifo_write(struct sp - } - - static void spi_qup_block_read(struct spi_qup *controller, -- struct spi_transfer *xfer) -+ struct spi_transfer *xfer, u32 *opflags) - { - u32 data; - u32 reads_per_blk = controller->in_blk_sz >> 2; -@@ -327,10 +327,12 @@ static void spi_qup_block_read(struct sp - - /* - * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block -- * reads, it has to be cleared again at the very end -+ * reads, it has to be cleared again at the very end. However, be sure -+ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be -+ * present and this is used to determine if transaction is complete - */ -- if (readl_relaxed(controller->base + QUP_OPERATIONAL) & -- QUP_OP_MAX_INPUT_DONE_FLAG) -+ *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); -+ if (*opflags & QUP_OP_MAX_INPUT_DONE_FLAG) - writel_relaxed(QUP_OP_IN_SERVICE_FLAG, - controller->base + QUP_OPERATIONAL); - -@@ -633,7 +635,7 @@ static irqreturn_t spi_qup_qup_irq(int i - if (!controller->use_dma) { - if (opflags & QUP_OP_IN_SERVICE_FLAG) { - if (opflags & QUP_OP_IN_BLOCK_READ_REQ) -- spi_qup_block_read(controller, xfer); -+ spi_qup_block_read(controller, xfer, &opflags); - else - spi_qup_fifo_read(controller, xfer); - } diff --git a/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch b/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch deleted file mode 100644 index 68489a83b5..0000000000 --- a/target/linux/ipq806x/patches-3.18/011-watchdog-qcom-use-timer-devicetree-binding.patch +++ /dev/null @@ -1,67 +0,0 @@ -From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 19 Feb 2015 20:19:30 -0800 -Subject: [PATCH 1/3] watchdog: qcom: use timer devicetree binding - -MSM watchdog configuration happens in the same register block as the -timer, so we'll use the same binding as the existing timer. - -The qcom-wdt will now be probed when devicetree has an entry compatible -with "qcom,kpss-timer" or "qcom-scss-timer". - -Signed-off-by: Mathieu Olivari ---- - drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------ - 1 file changed, 15 insertions(+), 6 deletions(-) - ---- a/drivers/watchdog/qcom-wdt.c -+++ b/drivers/watchdog/qcom-wdt.c -@@ -20,9 +20,9 @@ - #include - #include - --#define WDT_RST 0x0 --#define WDT_EN 0x8 --#define WDT_BITE_TIME 0x24 -+#define WDT_RST 0x38 -+#define WDT_EN 0x40 -+#define WDT_BITE_TIME 0x5C - - struct qcom_wdt { - struct watchdog_device wdd; -@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platfor - { - struct qcom_wdt *wdt; - struct resource *res; -+ struct device_node *np = pdev->dev.of_node; -+ u32 percpu_offset; - int ret; - - wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); -@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platfor - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ /* We use CPU0's DGT for the watchdog */ -+ if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) -+ percpu_offset = 0; -+ -+ res->start += percpu_offset; -+ res->end += percpu_offset; -+ - wdt->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(wdt->base)) - return PTR_ERR(wdt->base); -@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platfo - } - - static const struct of_device_id qcom_wdt_of_table[] = { -- { .compatible = "qcom,kpss-wdt-msm8960", }, -- { .compatible = "qcom,kpss-wdt-apq8064", }, -- { .compatible = "qcom,kpss-wdt-ipq8064", }, -+ { .compatible = "qcom,kpss-timer" }, -+ { .compatible = "qcom,scss-timer" }, - { }, - }; - MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); diff --git a/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch b/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch deleted file mode 100644 index ae9677656e..0000000000 --- a/target/linux/ipq806x/patches-3.18/012-ARM-qcom-add-description-of-KPSS-WDT-for-IPQ8064.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 19 Feb 2015 20:27:39 -0800 -Subject: [PATCH 2/3] ARM: qcom: add description of KPSS WDT for IPQ8064 - -Add the watchdog related entries to the Krait Processor Sub-system -(KPSS) timer IPQ8064 devicetree section. Also, add a fixed-clock -description of SLEEP_CLK, which will do for now. - -Signed-off-by: Josh Cartwright -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++- - 1 file changed, 13 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -60,6 +60,14 @@ - }; - }; - -+ clocks { -+ sleep_clk: sleep_clk { -+ compatible = "fixed-clock"; -+ clock-frequency = <32768>; -+ #clock-cells = <0>; -+ }; -+ }; -+ - soc: soc { - #address-cells = <1>; - #size-cells = <1>; -@@ -89,10 +97,14 @@ - compatible = "qcom,kpss-timer", "qcom,msm-timer"; - interrupts = <1 1 0x301>, - <1 2 0x301>, -- <1 3 0x301>; -+ <1 3 0x301>, -+ <1 4 0x301>, -+ <1 5 0x301>; - reg = <0x0200a000 0x100>; - clock-frequency = <25000000>, - <32768>; -+ clocks = <&sleep_clk>; -+ clock-names = "sleep"; - cpu-offset = <0x80000>; - }; - diff --git a/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch b/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch deleted file mode 100644 index 6876768962..0000000000 --- a/target/linux/ipq806x/patches-3.18/013-ARM-msm-add-watchdog-entries-to-DT-timer-binding-doc.patch +++ /dev/null @@ -1,50 +0,0 @@ -From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 19 Feb 2015 20:36:27 -0800 -Subject: [PATCH 3/3] ARM: msm: add watchdog entries to DT timer binding doc - -The watchdog has been reworked to use the same DT node as the timer. -This change is updating the device tree doc accordingly. - -Signed-off-by: Mathieu Olivari ---- - Documentation/devicetree/bindings/arm/msm/timer.txt | 16 +++++++++++++--- - 1 file changed, 13 insertions(+), 3 deletions(-) - ---- a/Documentation/devicetree/bindings/arm/msm/timer.txt -+++ b/Documentation/devicetree/bindings/arm/msm/timer.txt -@@ -9,11 +9,17 @@ Properties: - "qcom,scss-timer" - scorpion subsystem - - - interrupts : Interrupts for the the debug timer, the first general purpose -- timer, and optionally a second general purpose timer in that -- order. -+ timer, and optionally a second general purpose timer, and -+ optionally as well, 2 watchdog interrupts, in that order. - - - reg : Specifies the base address of the timer registers. - -+- clocks: Reference to the parent clocks, one per output clock. The parents -+ must appear in the same order as the clock names. -+ -+- clock-names: The name of the clocks as free-form strings. They should be in -+ the same order as the clocks. -+ - - clock-frequency : The frequency of the debug timer and the general purpose - timer(s) in Hz in that order. - -@@ -29,9 +35,13 @@ Example: - compatible = "qcom,scss-timer", "qcom,msm-timer"; - interrupts = <1 1 0x301>, - <1 2 0x301>, -- <1 3 0x301>; -+ <1 3 0x301>, -+ <1 4 0x301>, -+ <1 5 0x301>; - reg = <0x0200a000 0x100>; - clock-frequency = <19200000>, - <32768>; -+ clocks = <&sleep_clk>; -+ clock-names = "sleep"; - cpu-offset = <0x40000>; - }; diff --git a/target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch deleted file mode 100644 index a61481e652..0000000000 --- a/target/linux/ipq806x/patches-3.18/020-add-ap148-bootargs.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -14,6 +14,14 @@ - }; - }; - -+ alias { -+ serial0 = &uart4; -+ }; -+ -+ chosen { -+ linux,stdout-path = "serial0:115200n8"; -+ }; -+ - soc { - pinmux@800000 { - i2c4_pins: i2c4_pinmux { ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -140,7 +140,7 @@ - ranges; - status = "disabled"; - -- serial@12490000 { -+ uart2: serial@12490000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x12490000 0x1000>, - <0x12480000 0x1000>; -@@ -175,7 +175,7 @@ - ranges; - status = "disabled"; - -- serial@16340000 { -+ uart4: serial@16340000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x16340000 0x1000>, - <0x16300000 0x1000>; -@@ -209,7 +209,7 @@ - ranges; - status = "disabled"; - -- serial@1a240000 { -+ uart5: serial@1a240000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x1a240000 0x1000>, - <0x1a200000 0x1000>; diff --git a/target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch deleted file mode 100644 index bfdb30fe14..0000000000 --- a/target/linux/ipq806x/patches-3.18/021-add-ap148-partitions.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -77,15 +77,7 @@ - spi-max-frequency = <50000000>; - reg = <0>; - -- partition@0 { -- label = "rootfs"; -- reg = <0x0 0x1000000>; -- }; -- -- partition@1 { -- label = "scratch"; -- reg = <0x1000000 0x1000000>; -- }; -+ linux,part-probe = "qcom-smem"; - }; - }; - }; diff --git a/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch b/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch deleted file mode 100644 index bd6ec1e94f..0000000000 --- a/target/linux/ipq806x/patches-3.18/022-add-db149-dts.patch +++ /dev/null @@ -1,160 +0,0 @@ -From a32d6e7c8fca6371a2614924b89981bc912b6378 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 7 Apr 2015 19:58:58 -0700 -Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree - -Add basic DB149 (IPQ806x based platform) device-tree. It supports UART, -SATA, USB2, USB3 and NOR flash. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 257 +++++++++++++++++++++++++++++++ - 2 files changed, 258 insertions(+) - create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -360,6 +360,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-apq8084-ifc6540.dtb \ - qcom-apq8084-mtp.dtb \ - qcom-ipq8064-ap148.dtb \ -+ qcom-ipq8064-db149.dtb \ - qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb \ - qcom-msm8974-sony-xperia-honami.dtb ---- /dev/null -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -0,0 +1,132 @@ -+#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; -+ }; -+ -+ 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; -+ }; -+ }; -+ }; -+ -+ gsbi2: gsbi@12480000 { -+ qcom,mode = ; -+ status = "ok"; -+ uart2: serial@12490000 { -+ status = "ok"; -+ }; -+ }; -+ -+ gsbi5: gsbi@1a200000 { -+ qcom,mode = ; -+ 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"; -+ }; -+ }; -+}; diff --git a/target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch deleted file mode 100644 index 319859b109..0000000000 --- a/target/linux/ipq806x/patches-3.18/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch +++ /dev/null @@ -1,53 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -46,15 +46,12 @@ - serial@16340000 { - status = "ok"; - }; -- -- i2c4: i2c@16380000 { -- status = "ok"; -- -- clock-frequency = <200000>; -- -- pinctrl-0 = <&i2c4_pins>; -- pinctrl-names = "default"; -- }; -+ /* -+ * 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 { ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -794,7 +794,7 @@ static struct clk_rcg gsbi7_qup_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, - }, - }, - }; -@@ -810,7 +810,7 @@ static struct clk_branch gsbi7_qup_clk = - .parent_names = (const char *[]){ "gsbi7_qup_src" }, - .num_parents = 1, - .ops = &clk_branch_ops, -- .flags = CLK_SET_RATE_PARENT, -+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, - }, - }, - }; -@@ -858,7 +858,7 @@ static struct clk_branch gsbi4_h_clk = { - .hw.init = &(struct clk_init_data){ - .name = "gsbi4_h_clk", - .ops = &clk_branch_ops, -- .flags = CLK_IS_ROOT, -+ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, - }, - }, - }; diff --git a/target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch deleted file mode 100644 index f026ed9394..0000000000 --- a/target/linux/ipq806x/patches-3.18/024-ap148-add-memory-node.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -4,6 +4,11 @@ - 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>; diff --git a/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch b/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch deleted file mode 100644 index 04f35b7ec7..0000000000 --- a/target/linux/ipq806x/patches-3.18/030-hwspinlock-core-add-device-tree-support.patch +++ /dev/null @@ -1,167 +0,0 @@ -From fb7737e949e31d8a71acee6bbb670f32dbd2a2c0 Mon Sep 17 00:00:00 2001 -From: Suman Anna -Date: Wed, 4 Mar 2015 20:01:14 -0600 -Subject: [PATCH] hwspinlock/core: add device tree support - -This patch adds a new OF-friendly API of_hwspin_lock_get_id() -for hwspinlock clients to use/request locks from a hwspinlock -device instantiated through a device-tree blob. This new API -can be used by hwspinlock clients to get the id for a specific -lock using the phandle + args specifier, so that it can be -requested using the available hwspin_lock_request_specific() -API. - -Signed-off-by: Suman Anna -Reviewed-by: Bjorn Andersson -[small comment clarification] -Signed-off-by: Ohad Ben-Cohen ---- - Documentation/hwspinlock.txt | 10 +++++ - drivers/hwspinlock/hwspinlock_core.c | 79 ++++++++++++++++++++++++++++++++++++ - include/linux/hwspinlock.h | 7 ++++ - 3 files changed, 96 insertions(+) - ---- a/Documentation/hwspinlock.txt -+++ b/Documentation/hwspinlock.txt -@@ -48,6 +48,16 @@ independent, drivers. - ids for predefined purposes. - Should be called from a process context (might sleep). - -+ int of_hwspin_lock_get_id(struct device_node *np, int index); -+ - retrieve the global lock id for an OF phandle-based specific lock. -+ This function provides a means for DT users of a hwspinlock module -+ to get the global lock id of a specific hwspinlock, so that it can -+ be requested using the normal hwspin_lock_request_specific() API. -+ The function returns a lock id number on success, -EPROBE_DEFER if -+ the hwspinlock device is not yet registered with the core, or other -+ error values. -+ Should be called from a process context (might sleep). -+ - int hwspin_lock_free(struct hwspinlock *hwlock); - - free a previously-assigned hwspinlock; returns 0 on success, or an - appropriate error code on failure (e.g. -EINVAL if the hwspinlock ---- a/drivers/hwspinlock/hwspinlock_core.c -+++ b/drivers/hwspinlock/hwspinlock_core.c -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - - #include "hwspinlock_internal.h" - -@@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock * - } - EXPORT_SYMBOL_GPL(__hwspin_unlock); - -+/** -+ * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id -+ * @bank: the hwspinlock device bank -+ * @hwlock_spec: hwlock specifier as found in the device tree -+ * -+ * This is a simple translation function, suitable for hwspinlock platform -+ * drivers that only has a lock specifier length of 1. -+ * -+ * Returns a relative index of the lock within a specified bank on success, -+ * or -EINVAL on invalid specifier cell count. -+ */ -+static inline int -+of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec) -+{ -+ if (WARN_ON(hwlock_spec->args_count != 1)) -+ return -EINVAL; -+ -+ return hwlock_spec->args[0]; -+} -+ -+/** -+ * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock -+ * @np: device node from which to request the specific hwlock -+ * @index: index of the hwlock in the list of values -+ * -+ * This function provides a means for DT users of the hwspinlock module to -+ * get the global lock id of a specific hwspinlock using the phandle of the -+ * hwspinlock device, so that it can be requested using the normal -+ * hwspin_lock_request_specific() API. -+ * -+ * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock -+ * device is not yet registered, -EINVAL on invalid args specifier value or an -+ * appropriate error as returned from the OF parsing of the DT client node. -+ */ -+int of_hwspin_lock_get_id(struct device_node *np, int index) -+{ -+ struct of_phandle_args args; -+ struct hwspinlock *hwlock; -+ struct radix_tree_iter iter; -+ void **slot; -+ int id; -+ int ret; -+ -+ ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index, -+ &args); -+ if (ret) -+ return ret; -+ -+ /* Find the hwspinlock device: we need its base_id */ -+ ret = -EPROBE_DEFER; -+ rcu_read_lock(); -+ radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) { -+ hwlock = radix_tree_deref_slot(slot); -+ if (unlikely(!hwlock)) -+ continue; -+ -+ if (hwlock->bank->dev->of_node == args.np) { -+ ret = 0; -+ break; -+ } -+ } -+ rcu_read_unlock(); -+ if (ret < 0) -+ goto out; -+ -+ id = of_hwspin_lock_simple_xlate(&args); -+ if (id < 0 || id >= hwlock->bank->num_locks) { -+ ret = -EINVAL; -+ goto out; -+ } -+ id += hwlock->bank->base_id; -+ -+out: -+ of_node_put(args.np); -+ return ret ? ret : id; -+} -+EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id); -+ - static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) - { - struct hwspinlock *tmp; ---- a/include/linux/hwspinlock.h -+++ b/include/linux/hwspinlock.h -@@ -26,6 +26,7 @@ - #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ - - struct device; -+struct device_node; - struct hwspinlock; - struct hwspinlock_device; - struct hwspinlock_ops; -@@ -66,6 +67,7 @@ int hwspin_lock_unregister(struct hwspin - struct hwspinlock *hwspin_lock_request(void); - struct hwspinlock *hwspin_lock_request_specific(unsigned int id); - int hwspin_lock_free(struct hwspinlock *hwlock); -+int of_hwspin_lock_get_id(struct device_node *np, int index); - int hwspin_lock_get_id(struct hwspinlock *hwlock); - int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int, - unsigned long *); -@@ -120,6 +122,11 @@ void __hwspin_unlock(struct hwspinlock * - { - } - -+static inline int of_hwspin_lock_get_id(struct device_node *np, int index) -+{ -+ return 0; -+} -+ - static inline int hwspin_lock_get_id(struct hwspinlock *hwlock) - { - return 0; diff --git a/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch b/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch deleted file mode 100644 index 581b199d7f..0000000000 --- a/target/linux/ipq806x/patches-3.18/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch +++ /dev/null @@ -1,234 +0,0 @@ -From 19a0f61224d2d91860fa8291ab63cb104ee86bdd Mon Sep 17 00:00:00 2001 -From: Bjorn Andersson -Date: Tue, 24 Mar 2015 10:11:05 -0700 -Subject: [PATCH] hwspinlock: qcom: Add support for Qualcomm HW Mutex block - -Add driver for Qualcomm Hardware Mutex block found in many Qualcomm -SoCs. - -Based on initial effort by Kumar Gala - -Signed-off-by: Bjorn Andersson -Reviewed-by: Andy Gross -Reviewed-by: Jeffrey Hugo -Signed-off-by: Ohad Ben-Cohen ---- - drivers/hwspinlock/Kconfig | 12 +++ - drivers/hwspinlock/Makefile | 1 + - drivers/hwspinlock/qcom_hwspinlock.c | 181 +++++++++++++++++++++++++++++++++++ - 3 files changed, 194 insertions(+) - create mode 100644 drivers/hwspinlock/qcom_hwspinlock.c - ---- a/drivers/hwspinlock/Kconfig -+++ b/drivers/hwspinlock/Kconfig -@@ -18,6 +18,18 @@ config HWSPINLOCK_OMAP - - If unsure, say N. - -+config HWSPINLOCK_QCOM -+ tristate "Qualcomm Hardware Spinlock device" -+ depends on ARCH_QCOM -+ select HWSPINLOCK -+ select MFD_SYSCON -+ help -+ Say y here to support the Qualcomm Hardware Mutex functionality, which -+ provides a synchronisation mechanism for the various processors on -+ the SoC. -+ -+ If unsure, say N. -+ - config HSEM_U8500 - tristate "STE Hardware Semaphore functionality" - depends on ARCH_U8500 ---- a/drivers/hwspinlock/Makefile -+++ b/drivers/hwspinlock/Makefile -@@ -4,4 +4,5 @@ - - obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o - obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o -+obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o - obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o ---- /dev/null -+++ b/drivers/hwspinlock/qcom_hwspinlock.c -@@ -0,0 +1,181 @@ -+/* -+ * Copyright (c) 2013, The Linux Foundation. All rights reserved. -+ * Copyright (c) 2015, Sony Mobile Communications AB -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "hwspinlock_internal.h" -+ -+#define QCOM_MUTEX_APPS_PROC_ID 1 -+#define QCOM_MUTEX_NUM_LOCKS 32 -+ -+static int qcom_hwspinlock_trylock(struct hwspinlock *lock) -+{ -+ struct regmap_field *field = lock->priv; -+ u32 lock_owner; -+ int ret; -+ -+ ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID); -+ if (ret) -+ return ret; -+ -+ ret = regmap_field_read(field, &lock_owner); -+ if (ret) -+ return ret; -+ -+ return lock_owner == QCOM_MUTEX_APPS_PROC_ID; -+} -+ -+static void qcom_hwspinlock_unlock(struct hwspinlock *lock) -+{ -+ struct regmap_field *field = lock->priv; -+ u32 lock_owner; -+ int ret; -+ -+ ret = regmap_field_read(field, &lock_owner); -+ if (ret) { -+ pr_err("%s: unable to query spinlock owner\n", __func__); -+ return; -+ } -+ -+ if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) { -+ pr_err("%s: spinlock not owned by us (actual owner is %d)\n", -+ __func__, lock_owner); -+ } -+ -+ ret = regmap_field_write(field, 0); -+ if (ret) -+ pr_err("%s: failed to unlock spinlock\n", __func__); -+} -+ -+static const struct hwspinlock_ops qcom_hwspinlock_ops = { -+ .trylock = qcom_hwspinlock_trylock, -+ .unlock = qcom_hwspinlock_unlock, -+}; -+ -+static const struct of_device_id qcom_hwspinlock_of_match[] = { -+ { .compatible = "qcom,sfpb-mutex" }, -+ { .compatible = "qcom,tcsr-mutex" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); -+ -+static int qcom_hwspinlock_probe(struct platform_device *pdev) -+{ -+ struct hwspinlock_device *bank; -+ struct device_node *syscon; -+ struct reg_field field; -+ struct regmap *regmap; -+ size_t array_size; -+ u32 stride; -+ u32 base; -+ int ret; -+ int i; -+ -+ syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0); -+ if (!syscon) { -+ dev_err(&pdev->dev, "no syscon property\n"); -+ return -ENODEV; -+ } -+ -+ regmap = syscon_node_to_regmap(syscon); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); -+ -+ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "no offset in syscon\n"); -+ return -EINVAL; -+ } -+ -+ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "no stride syscon\n"); -+ return -EINVAL; -+ } -+ -+ array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); -+ bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL); -+ if (!bank) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, bank); -+ -+ for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { -+ field.reg = base + i * stride; -+ field.lsb = 0; -+ field.msb = 32; -+ -+ bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, -+ regmap, field); -+ } -+ -+ pm_runtime_enable(&pdev->dev); -+ -+ ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops, -+ 0, QCOM_MUTEX_NUM_LOCKS); -+ if (ret) -+ pm_runtime_disable(&pdev->dev); -+ -+ return ret; -+} -+ -+static int qcom_hwspinlock_remove(struct platform_device *pdev) -+{ -+ struct hwspinlock_device *bank = platform_get_drvdata(pdev); -+ int ret; -+ -+ ret = hwspin_lock_unregister(bank); -+ if (ret) { -+ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); -+ return ret; -+ } -+ -+ pm_runtime_disable(&pdev->dev); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_hwspinlock_driver = { -+ .probe = qcom_hwspinlock_probe, -+ .remove = qcom_hwspinlock_remove, -+ .driver = { -+ .name = "qcom_hwspinlock", -+ .of_match_table = qcom_hwspinlock_of_match, -+ }, -+}; -+ -+static int __init qcom_hwspinlock_init(void) -+{ -+ return platform_driver_register(&qcom_hwspinlock_driver); -+} -+/* board init code might need to reserve hwspinlocks for predefined purposes */ -+postcore_initcall(qcom_hwspinlock_init); -+ -+static void __exit qcom_hwspinlock_exit(void) -+{ -+ platform_driver_unregister(&qcom_hwspinlock_driver); -+} -+module_exit(qcom_hwspinlock_exit); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs"); diff --git a/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch b/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch deleted file mode 100644 index 70d5a58248..0000000000 --- a/target/linux/ipq806x/patches-3.18/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch +++ /dev/null @@ -1,26 +0,0 @@ -From bd5717a4632cdecafe82d03de7dcb3b1876e2828 Mon Sep 17 00:00:00 2001 -From: Bjorn Andersson -Date: Fri, 26 Jun 2015 14:47:21 -0700 -Subject: [PATCH] hwspinlock: qcom: Correct msb in regmap_field - -msb of the regmap_field was mistakenly given the value 32, to set all bits -in the regmap update mask; although incorrect this worked until 921cc294, -where the mask calculation was corrected. - -Signed-off-by: Bjorn Andersson -Signed-off-by: Ohad Ben-Cohen ---- - drivers/hwspinlock/qcom_hwspinlock.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/hwspinlock/qcom_hwspinlock.c -+++ b/drivers/hwspinlock/qcom_hwspinlock.c -@@ -123,7 +123,7 @@ static int qcom_hwspinlock_probe(struct - for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { - field.reg = base + i * stride; - field.lsb = 0; -- field.msb = 32; -+ field.msb = 31; - - bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, - regmap, field); diff --git a/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch deleted file mode 100644 index 4a353254bf..0000000000 --- a/target/linux/ipq806x/patches-3.18/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch +++ /dev/null @@ -1,34 +0,0 @@ -From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 13 Aug 2015 09:45:26 -0700 -Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts - -Add one new node to the ipq806x.dtsi file to declare & register the -hardware spinlock devices. This mechanism is required to be used by -other drivers such as SMEM. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++ - 1 file changed, 11 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -291,5 +291,17 @@ - #clock-cells = <1>; - #reset-cells = <1>; - }; -+ -+ sfpb_mutex_block: syscon@1200600 { -+ compatible = "syscon"; -+ reg = <0x01200600 0x100>; -+ }; - }; -+ -+ sfpb_mutex: sfpb-mutex { -+ compatible = "qcom,sfpb-mutex"; -+ syscon = <&sfpb_mutex_block 4 4>; -+ -+ #hwlock-cells = <1>; -+ }; - }; diff --git a/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch b/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch deleted file mode 100644 index d22db2226e..0000000000 --- a/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch +++ /dev/null @@ -1,82 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,1/2] soc: qcom: Add device tree binding for SMEM -From: Bjorn Andersson -X-Patchwork-Id: 6202201 -Message-Id: <1428795178-24312-1-git-send-email-bjorn.andersson@sonymobile.com> -To: Rob Herring , Pawel Moll , - Mark Rutland , - Ian Campbell , - Kumar Gala , Jeffrey Hugo , - Andry Gross -Cc: , - linux-arm-msm , - -Date: Sat, 11 Apr 2015 16:32:57 -0700 - -Add device tree binding documentation for the Qualcom Shared Memory -manager. - -Signed-off-by: Bjorn Andersson - ---- -Changes since v1: -- None - - .../devicetree/bindings/soc/qcom/qcom,smem.txt | 49 ++++++++++++++++++++++ - 1 file changed, 49 insertions(+) - create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt -@@ -0,0 +1,49 @@ -+Qualcomm Shared Memory binding -+ -+This binding describes the Qualcomm Shared Memory, used to share data between -+various subsystems and OSes in Qualcomm platforms. -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: must be: -+ "qcom,smem" -+ -+- memory-region: -+ Usage: required -+ Value type: -+ Definition: handle to memory reservation for main smem memory region. -+ -+- reg: -+ Usage: optional -+ Value type: -+ Definition: base address and size pair for any additional memory areas -+ of the shared memory. -+ -+- hwspinlocks: -+ Usage: required -+ Value type: -+ Definition: reference to a hwspinlock used to protect allocations from -+ the shared memory -+ -+= EXAMPLE -+ -+ reserved-memory { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges; -+ -+ smem_region: smem@fa00000 { -+ reg = <0xfa00000 0x200000>; -+ no-map; -+ }; -+ }; -+ -+ smem@fa00000 { -+ compatible = "qcom,smem"; -+ -+ memory-region = <&smem_region>; -+ reg = <0xfc428000 0x4000>; -+ -+ hwlocks = <&tcsr_mutex 3>; -+ }; diff --git a/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch b/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch deleted file mode 100644 index c8cff1a288..0000000000 --- a/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch +++ /dev/null @@ -1,841 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,2/2] soc: qcom: Add Shared Memory Manager driver -From: Bjorn Andersson -X-Patchwork-Id: 6202211 -Message-Id: <1428795178-24312-2-git-send-email-bjorn.andersson@sonymobile.com> -To: Kumar Gala , Andy Gross , - David Brown , Jeffrey Hugo -Cc: , , - -Date: Sat, 11 Apr 2015 16:32:58 -0700 - -The Shared Memory Manager driver implements an interface for allocating -and accessing items in the memory area shared among all of the -processors in a Qualcomm platform. - -Signed-off-by: Bjorn Andersson -Reviewed-by: Andy Gross -Tested-by: Andy Gross - ---- -Changes since v1: -- ioremapping the regions nocache -- improved documentation of the two regions of partitions -- corrected free space check in private allocator - - drivers/soc/qcom/Kconfig | 7 + - drivers/soc/qcom/Makefile | 1 + - drivers/soc/qcom/smem.c | 768 ++++++++++++++++++++++++++++++++++++++++++ - include/linux/soc/qcom/smem.h | 14 + - 4 files changed, 790 insertions(+) - create mode 100644 drivers/soc/qcom/smem.c - create mode 100644 include/linux/soc/qcom/smem.h - ---- a/drivers/soc/qcom/Kconfig -+++ b/drivers/soc/qcom/Kconfig -@@ -9,3 +9,10 @@ config QCOM_GSBI - functions for connecting the underlying serial UART, SPI, and I2C - devices to the output pins. - -+config QCOM_SMEM -+ tristate "Qualcomm Shared Memory Manager (SMEM)" -+ depends on ARCH_QCOM -+ help -+ Say y here to enable support for the Qualcomm Shared Memory Manager. -+ The driver provides an interface to items in a heap shared among all -+ processors in a Qualcomm platform. ---- a/drivers/soc/qcom/Makefile -+++ b/drivers/soc/qcom/Makefile -@@ -1 +1,2 @@ - obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o -+obj-$(CONFIG_QCOM_SMEM) += smem.o ---- /dev/null -+++ b/drivers/soc/qcom/smem.c -@@ -0,0 +1,768 @@ -+/* -+ * Copyright (c) 2015, Sony Mobile Communications AB. -+ * Copyright (c) 2012-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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * The Qualcomm shared memory system is a allocate only heap structure that -+ * consists of one of more memory areas that can be accessed by the processors -+ * in the SoC. -+ * -+ * All systems contains a global heap, accessible by all processors in the SoC, -+ * with a table of contents data structure (@smem_header) at the beginning of -+ * the main shared memory block. -+ * -+ * The global header contains metadata for allocations as well as a fixed list -+ * of 512 entries (@smem_global_entry) that can be initialized to reference -+ * parts of the shared memory space. -+ * -+ * -+ * In addition to this global heap a set of "private" heaps can be set up at -+ * boot time with access restrictions so that only certain processor pairs can -+ * access the data. -+ * -+ * These partitions are referenced from an optional partition table -+ * (@smem_ptable), that is found 4kB from the end of the main smem region. The -+ * partition table entries (@smem_ptable_entry) lists the involved processors -+ * (or hosts) and their location in the main shared memory region. -+ * -+ * Each partition starts with a header (@smem_partition_header) that identifies -+ * the partition and holds properties for the two internal memory regions. The -+ * two regions are cached and non-cached memory respectively. Each region -+ * contain a link list of allocation headers (@smem_private_entry) followed by -+ * their data. -+ * -+ * Items in the non-cached region are allocated from the start of the partition -+ * while items in the cached region are allocated from the end. The free area -+ * is hence the region between the cached and non-cached offsets. -+ * -+ * -+ * To synchronize allocations in the shared memory heaps a remote spinlock must -+ * be held - currently lock number 3 of the sfpb or tcsr is used for this on all -+ * platforms. -+ * -+ */ -+ -+/** -+ * struct smem_proc_comm - proc_comm communication struct (legacy) -+ * @command: current command to be executed -+ * @status: status of the currently requested command -+ * @params: parameters to the command -+ */ -+struct smem_proc_comm { -+ u32 command; -+ u32 status; -+ u32 params[2]; -+}; -+ -+/** -+ * struct smem_global_entry - entry to reference smem items on the heap -+ * @allocated: boolean to indicate if this entry is used -+ * @offset: offset to the allocated space -+ * @size: size of the allocated space, 8 byte aligned -+ * @aux_base: base address for the memory region used by this unit, or 0 for -+ * the default region. bits 0,1 are reserved -+ */ -+struct smem_global_entry { -+ u32 allocated; -+ u32 offset; -+ u32 size; -+ u32 aux_base; /* bits 1:0 reserved */ -+}; -+#define AUX_BASE_MASK 0xfffffffc -+ -+/** -+ * struct smem_header - header found in beginning of primary smem region -+ * @proc_comm: proc_comm communication interface (legacy) -+ * @version: array of versions for the various subsystems -+ * @initialized: boolean to indicate that smem is initialized -+ * @free_offset: index of the first unallocated byte in smem -+ * @available: number of bytes available for allocation -+ * @reserved: reserved field, must be 0 -+ * toc: array of references to items -+ */ -+struct smem_header { -+ struct smem_proc_comm proc_comm[4]; -+ u32 version[32]; -+ u32 initialized; -+ u32 free_offset; -+ u32 available; -+ u32 reserved; -+ struct smem_global_entry toc[]; -+}; -+ -+/** -+ * struct smem_ptable_entry - one entry in the @smem_ptable list -+ * @offset: offset, within the main shared memory region, of the partition -+ * @size: size of the partition -+ * @flags: flags for the partition (currently unused) -+ * @host0: first processor/host with access to this partition -+ * @host1: second processor/host with access to this partition -+ * @reserved: reserved entries for later use -+ */ -+struct smem_ptable_entry { -+ u32 offset; -+ u32 size; -+ u32 flags; -+ u16 host0; -+ u16 host1; -+ u32 reserved[8]; -+}; -+ -+/** -+ * struct smem_ptable - partition table for the private partitions -+ * @magic: magic number, must be SMEM_PTABLE_MAGIC -+ * @version: version of the partition table -+ * @num_entries: number of partitions in the table -+ * @reserved: for now reserved entries -+ * @entry: list of @smem_ptable_entry for the @num_entries partitions -+ */ -+struct smem_ptable { -+ u32 magic; -+ u32 version; -+ u32 num_entries; -+ u32 reserved[5]; -+ struct smem_ptable_entry entry[]; -+}; -+#define SMEM_PTABLE_MAGIC 0x434f5424 /* "$TOC" */ -+ -+/** -+ * struct smem_partition_header - header of the partitions -+ * @magic: magic number, must be SMEM_PART_MAGIC -+ * @host0: first processor/host with access to this partition -+ * @host1: second processor/host with access to this partition -+ * @size: size of the partition -+ * @offset_free_uncached: offset to the first free byte of uncached memory in -+ * this partition -+ * @offset_free_cached: offset to the first free byte of cached memory in this -+ * partition -+ * @reserved: for now reserved entries -+ */ -+struct smem_partition_header { -+ u32 magic; -+ u16 host0; -+ u16 host1; -+ u32 size; -+ u32 offset_free_uncached; -+ u32 offset_free_cached; -+ u32 reserved[3]; -+}; -+#define SMEM_PART_MAGIC 0x54525024 /* "$PRT" */ -+ -+/** -+ * struct smem_private_entry - header of each item in the private partition -+ * @canary: magic number, must be SMEM_PRIVATE_CANARY -+ * @item: identifying number of the smem item -+ * @size: size of the data, including padding bytes -+ * @padding_data: number of bytes of padding of data -+ * @padding_hdr: number of bytes of padding between the header and the data -+ * @reserved: for now reserved entry -+ */ -+struct smem_private_entry { -+ u16 canary; -+ u16 item; -+ u32 size; /* includes padding bytes */ -+ u16 padding_data; -+ u16 padding_hdr; -+ u32 reserved; -+}; -+#define SMEM_PRIVATE_CANARY 0xa5a5 -+ -+/* -+ * Item 3 of the global heap contains an array of versions for the various -+ * software components in the SoC. We verify that the boot loader version is -+ * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check. -+ */ -+#define SMEM_ITEM_VERSION 3 -+#define SMEM_MASTER_SBL_VERSION_INDEX 7 -+#define SMEM_EXPECTED_VERSION 11 -+ -+/* -+ * The first 8 items are only to be allocated by the boot loader while -+ * initializing the heap. -+ */ -+#define SMEM_ITEM_LAST_FIXED 8 -+ -+/* Highest accepted item number, for both global and private heaps */ -+#define SMEM_ITEM_LAST 512 -+ -+/* Processor/host identifier for the application processor */ -+#define SMEM_HOST_APPS 0 -+ -+/* Max number of processors/hosts in a system */ -+#define SMEM_HOST_COUNT 7 -+ -+/** -+ * struct smem_region - representation of a chunk of memory used for smem -+ * @aux_base: identifier of aux_mem base -+ * @virt_base: virtual base address of memory with this aux_mem identifier -+ * @size: size of the memory region -+ */ -+struct smem_region { -+ u32 aux_base; -+ void __iomem *virt_base; -+ size_t size; -+}; -+ -+/** -+ * struct qcom_smem - device data for the smem device -+ * @dev: device pointer -+ * @hwlock: reference to a hwspinlock -+ * @partitions: list of pointers to partitions affecting the current -+ * processor/host -+ * @num_regions: number of @regions -+ * @regions: list of the memory regions defining the shared memory -+ */ -+struct qcom_smem { -+ struct device *dev; -+ -+ struct hwspinlock *hwlock; -+ -+ struct smem_partition_header *partitions[SMEM_HOST_COUNT]; -+ -+ unsigned num_regions; -+ struct smem_region regions[0]; -+}; -+ -+/* Pointer to the one and only smem handle */ -+static struct qcom_smem *__smem; -+ -+/* Timeout (ms) for the trylock of remote spinlocks */ -+#define HWSPINLOCK_TIMEOUT 1000 -+ -+static int qcom_smem_alloc_private(struct qcom_smem *smem, -+ unsigned host, -+ unsigned item, -+ size_t size) -+{ -+ struct smem_partition_header *phdr; -+ struct smem_private_entry *hdr; -+ size_t alloc_size; -+ void *p; -+ -+ /* We're not going to find it if there's no matching partition */ -+ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) -+ return -ENOENT; -+ -+ phdr = smem->partitions[host]; -+ -+ p = (void *)phdr + sizeof(*phdr); -+ while (p < (void *)phdr + phdr->offset_free_uncached) { -+ hdr = p; -+ -+ if (hdr->canary != SMEM_PRIVATE_CANARY) { -+ dev_err(smem->dev, -+ "Found invalid canary in host %d partition\n", -+ host); -+ return -EINVAL; -+ } -+ -+ if (hdr->item == item) -+ return -EEXIST; -+ -+ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; -+ } -+ -+ /* Check that we don't grow into the cached region */ -+ alloc_size = sizeof(*hdr) + ALIGN(size, 8); -+ if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) { -+ dev_err(smem->dev, "Out of memory\n"); -+ return -ENOSPC; -+ } -+ -+ hdr = p; -+ hdr->canary = SMEM_PRIVATE_CANARY; -+ hdr->item = item; -+ hdr->size = ALIGN(size, 8); -+ hdr->padding_data = hdr->size - size; -+ hdr->padding_hdr = 0; -+ -+ /* -+ * Ensure the header is written before we advance the free offset, so -+ * that remote processors that does not take the remote spinlock still -+ * gets a consistent view of the linked list. -+ */ -+ wmb(); -+ phdr->offset_free_uncached += alloc_size; -+ -+ return 0; -+} -+ -+static int qcom_smem_alloc_global(struct qcom_smem *smem, -+ unsigned item, -+ size_t size) -+{ -+ struct smem_header *header; -+ struct smem_global_entry *entry; -+ -+ if (WARN_ON(item >= SMEM_ITEM_LAST)) -+ return -EINVAL; -+ -+ header = smem->regions[0].virt_base; -+ entry = &header->toc[item]; -+ if (entry->allocated) -+ return -EEXIST; -+ -+ size = ALIGN(size, 8); -+ if (WARN_ON(size > header->available)) -+ return -ENOMEM; -+ -+ entry->offset = header->free_offset; -+ entry->size = size; -+ -+ /* -+ * Ensure the header is consistent before we mark the item allocated, -+ * so that remote processors will get a consistent view of the item -+ * even though they do not take the spinlock on read. -+ */ -+ wmb(); -+ entry->allocated = 1; -+ -+ header->free_offset += size; -+ header->available -= size; -+ -+ return 0; -+} -+ -+/** -+ * qcom_smem_alloc - allocate space for a smem item -+ * @host: remote processor id, or -1 -+ * @item: smem item handle -+ * @size: number of bytes to be allocated -+ * -+ * Allocate space for a given smem item of size @size, given that the item is -+ * not yet allocated. -+ */ -+int qcom_smem_alloc(unsigned host, unsigned item, size_t size) -+{ -+ unsigned long flags; -+ int ret; -+ -+ if (!__smem) -+ return -EPROBE_DEFER; -+ -+ if (item < SMEM_ITEM_LAST_FIXED) { -+ dev_err(__smem->dev, -+ "Rejecting allocation of static entry %d\n", item); -+ return -EINVAL; -+ } -+ -+ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, -+ HWSPINLOCK_TIMEOUT, -+ &flags); -+ if (ret) -+ return ret; -+ -+ ret = qcom_smem_alloc_private(__smem, host, item, size); -+ if (ret == -ENOENT) -+ ret = qcom_smem_alloc_global(__smem, item, size); -+ -+ hwspin_unlock_irqrestore(__smem->hwlock, &flags); -+ -+ return ret; -+} -+EXPORT_SYMBOL(qcom_smem_alloc); -+ -+static int qcom_smem_get_global(struct qcom_smem *smem, -+ unsigned item, -+ void **ptr, -+ size_t *size) -+{ -+ struct smem_header *header; -+ struct smem_region *area; -+ struct smem_global_entry *entry; -+ u32 aux_base; -+ unsigned i; -+ -+ if (WARN_ON(item >= SMEM_ITEM_LAST)) -+ return -EINVAL; -+ -+ header = smem->regions[0].virt_base; -+ entry = &header->toc[item]; -+ if (!entry->allocated) -+ return -ENXIO; -+ -+ if (ptr != NULL) { -+ aux_base = entry->aux_base & AUX_BASE_MASK; -+ -+ for (i = 0; i < smem->num_regions; i++) { -+ area = &smem->regions[i]; -+ -+ if (area->aux_base == aux_base || !aux_base) { -+ *ptr = area->virt_base + entry->offset; -+ break; -+ } -+ } -+ } -+ if (size != NULL) -+ *size = entry->size; -+ -+ return 0; -+} -+ -+static int qcom_smem_get_private(struct qcom_smem *smem, -+ unsigned host, -+ unsigned item, -+ void **ptr, -+ size_t *size) -+{ -+ struct smem_partition_header *phdr; -+ struct smem_private_entry *hdr; -+ void *p; -+ -+ /* We're not going to find it if there's no matching partition */ -+ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) -+ return -ENOENT; -+ -+ phdr = smem->partitions[host]; -+ -+ p = (void *)phdr + sizeof(*phdr); -+ while (p < (void *)phdr + phdr->offset_free_uncached) { -+ hdr = p; -+ -+ if (hdr->canary != SMEM_PRIVATE_CANARY) { -+ dev_err(smem->dev, -+ "Found invalid canary in host %d partition\n", -+ host); -+ return -EINVAL; -+ } -+ -+ if (hdr->item == item) { -+ if (ptr != NULL) -+ *ptr = p + sizeof(*hdr) + hdr->padding_hdr; -+ -+ if (size != NULL) -+ *size = hdr->size - hdr->padding_data; -+ -+ return 0; -+ } -+ -+ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; -+ } -+ -+ return -ENOENT; -+} -+ -+/** -+ * qcom_smem_get - resolve ptr of size of a smem item -+ * @host: the remote processor, or -1 -+ * @item: smem item handle -+ * @ptr: pointer to be filled out with address of the item -+ * @size: pointer to be filled out with size of the item -+ * -+ * Looks up pointer and size of a smem item. -+ */ -+int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size) -+{ -+ unsigned long flags; -+ int ret; -+ -+ if (!__smem) -+ return -EPROBE_DEFER; -+ -+ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, -+ HWSPINLOCK_TIMEOUT, -+ &flags); -+ if (ret) -+ return ret; -+ -+ ret = qcom_smem_get_private(__smem, host, item, ptr, size); -+ if (ret == -ENOENT) -+ ret = qcom_smem_get_global(__smem, item, ptr, size); -+ -+ hwspin_unlock_irqrestore(__smem->hwlock, &flags); -+ return ret; -+ -+} -+EXPORT_SYMBOL(qcom_smem_get); -+ -+/** -+ * qcom_smem_get_free_space - retrieve amont of free space in a partition -+ * @host: the remote processor identifing a partition, or -1 -+ * -+ * To be used by smem clients as a quick way to determine if any new -+ * allocations has been made. -+ */ -+int qcom_smem_get_free_space(unsigned host) -+{ -+ struct smem_partition_header *phdr; -+ struct smem_header *header; -+ unsigned ret; -+ -+ if (!__smem) -+ return -EPROBE_DEFER; -+ -+ if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { -+ phdr = __smem->partitions[host]; -+ ret = phdr->offset_free_uncached; -+ } else { -+ header = __smem->regions[0].virt_base; -+ ret = header->available; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(qcom_smem_get_free_space); -+ -+static int qcom_smem_get_sbl_version(struct qcom_smem *smem) -+{ -+ unsigned *versions; -+ size_t size; -+ int ret; -+ -+ ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, -+ (void **)&versions, &size); -+ if (ret < 0) { -+ dev_err(smem->dev, "Unable to read the version item\n"); -+ return -ENOENT; -+ } -+ -+ if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) { -+ dev_err(smem->dev, "Version item is too small\n"); -+ return -EINVAL; -+ } -+ -+ return versions[SMEM_MASTER_SBL_VERSION_INDEX]; -+} -+ -+static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, -+ unsigned local_host) -+{ -+ struct smem_partition_header *header; -+ struct smem_ptable_entry *entry; -+ struct smem_ptable *ptable; -+ unsigned remote_host; -+ int i; -+ -+ ptable = smem->regions[0].virt_base + smem->regions[0].size - 4 * 1024; -+ if (ptable->magic != SMEM_PTABLE_MAGIC) -+ return 0; -+ -+ if (ptable->version != 1) { -+ dev_err(smem->dev, -+ "Unsupported partition header version %d\n", -+ ptable->version); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < ptable->num_entries; i++) { -+ entry = &ptable->entry[i]; -+ -+ if (entry->host0 != local_host && entry->host1 != local_host) -+ continue; -+ -+ if (!entry->offset) -+ continue; -+ -+ if (!entry->size) -+ continue; -+ -+ if (entry->host0 == local_host) -+ remote_host = entry->host1; -+ else -+ remote_host = entry->host0; -+ -+ if (smem->partitions[remote_host]) { -+ dev_err(smem->dev, -+ "Already found a partition for host %d\n", -+ remote_host); -+ return -EINVAL; -+ } -+ -+ header = smem->regions[0].virt_base + entry->offset; -+ -+ if (header->magic != SMEM_PART_MAGIC) { -+ dev_err(smem->dev, -+ "Partition %d has invalid magic\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->host0 != local_host && header->host1 != local_host) { -+ dev_err(smem->dev, -+ "Partition %d hosts are invalid\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->host0 != remote_host && header->host1 != remote_host) { -+ dev_err(smem->dev, -+ "Partition %d hosts are invalid\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->size != entry->size) { -+ dev_err(smem->dev, -+ "Partition %d has invalid size\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->offset_free_uncached > header->size) { -+ dev_err(smem->dev, -+ "Partition %d has invalid free pointer\n", i); -+ return -EINVAL; -+ } -+ -+ smem->partitions[remote_host] = header; -+ } -+ -+ return 0; -+} -+ -+static int qcom_smem_count_mem_regions(struct platform_device *pdev) -+{ -+ struct resource *res; -+ int num_regions = 0; -+ int i; -+ -+ for (i = 0; i < pdev->num_resources; i++) { -+ res = &pdev->resource[i]; -+ -+ if (resource_type(res) == IORESOURCE_MEM) -+ num_regions++; -+ } -+ -+ return num_regions; -+} -+ -+static int qcom_smem_probe(struct platform_device *pdev) -+{ -+ struct smem_header *header; -+ struct device_node *np; -+ struct qcom_smem *smem; -+ struct resource *res; -+ struct resource r; -+ size_t array_size; -+ int num_regions = 0; -+ int hwlock_id; -+ u32 version; -+ int ret; -+ int i; -+ -+ num_regions = qcom_smem_count_mem_regions(pdev) + 1; -+ -+ array_size = num_regions * sizeof(struct smem_region); -+ smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL); -+ if (!smem) -+ return -ENOMEM; -+ -+ smem->dev = &pdev->dev; -+ smem->num_regions = num_regions; -+ -+ np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); -+ if (!np) { -+ dev_err(&pdev->dev, "No memory-region specified\n"); -+ return -EINVAL; -+ } -+ -+ ret = of_address_to_resource(np, 0, &r); -+ of_node_put(np); -+ if (ret) -+ return ret; -+ -+ smem->regions[0].aux_base = (u32)r.start; -+ smem->regions[0].size = resource_size(&r); -+ smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev, -+ r.start, -+ resource_size(&r)); -+ if (!smem->regions[0].virt_base) -+ return -ENOMEM; -+ -+ for (i = 1; i < num_regions; i++) { -+ res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1); -+ -+ smem->regions[i].aux_base = (u32)res->start; -+ smem->regions[i].size = resource_size(res); -+ smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev, -+ res->start, -+ resource_size(res)); -+ if (!smem->regions[i].virt_base) -+ return -ENOMEM; -+ } -+ -+ header = smem->regions[0].virt_base; -+ if (header->initialized != 1 || header->reserved) { -+ dev_err(&pdev->dev, "SMEM is not initilized by SBL\n"); -+ return -EINVAL; -+ } -+ -+ version = qcom_smem_get_sbl_version(smem); -+ if (version >> 16 != SMEM_EXPECTED_VERSION) { -+ dev_err(&pdev->dev, "Unsupported smem version 0x%x\n", version); -+ return -EINVAL; -+ } -+ -+ ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); -+ if (ret < 0) -+ return ret; -+ -+ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); -+ if (hwlock_id < 0) { -+ dev_err(&pdev->dev, "failed to retrieve hwlock\n"); -+ return hwlock_id; -+ } -+ -+ smem->hwlock = hwspin_lock_request_specific(hwlock_id); -+ if (!smem->hwlock) -+ return -ENXIO; -+ -+ __smem = smem; -+ -+ return 0; -+} -+ -+static int qcom_smem_remove(struct platform_device *pdev) -+{ -+ hwspin_lock_free(__smem->hwlock); -+ __smem = NULL; -+ -+ return 0; -+} -+ -+static const struct of_device_id qcom_smem_of_match[] = { -+ { .compatible = "qcom,smem" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, qcom_smem_of_match); -+ -+static struct platform_driver qcom_smem_driver = { -+ .probe = qcom_smem_probe, -+ .remove = qcom_smem_remove, -+ .driver = { -+ .name = "qcom_smem", -+ .of_match_table = qcom_smem_of_match, -+ .suppress_bind_attrs = true, -+ }, -+}; -+ -+static int __init qcom_smem_init(void) -+{ -+ return platform_driver_register(&qcom_smem_driver); -+} -+arch_initcall(qcom_smem_init); -+ -+static void __exit qcom_smem_exit(void) -+{ -+ platform_driver_unregister(&qcom_smem_driver); -+} -+module_exit(qcom_smem_exit) -+ -+MODULE_AUTHOR("Bjorn Andersson "); -+MODULE_DESCRIPTION("Qualcomm Shared Memory Manager"); -+MODULE_LICENSE("GPLv2"); ---- /dev/null -+++ b/include/linux/soc/qcom/smem.h -@@ -0,0 +1,14 @@ -+#ifndef __QCOM_SMEM_H__ -+#define __QCOM_SMEM_H__ -+ -+struct device_node; -+struct qcom_smem; -+ -+#define QCOM_SMEM_HOST_ANY -1 -+ -+int qcom_smem_alloc(unsigned host, unsigned item, size_t size); -+int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size); -+ -+int qcom_smem_get_free_space(unsigned host); -+ -+#endif diff --git a/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch deleted file mode 100644 index b2b9a749c3..0000000000 --- a/target/linux/ipq806x/patches-3.18/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch +++ /dev/null @@ -1,36 +0,0 @@ -From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Fri, 14 Aug 2015 11:17:20 -0700 -Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts - -SMEM is used on IPQ806x to store various board related information such -as boot device and flash partition layout. We'll declare it as a device -so we can make use of it thanks to the new SMEM soc driver. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -54,7 +54,7 @@ - no-map; - }; - -- smem@41000000 { -+ smem: smem@41000000 { - reg = <0x41000000 0x200000>; - no-map; - }; -@@ -304,4 +304,10 @@ - - #hwlock-cells = <1>; - }; -+ -+ smem { -+ compatible = "qcom,smem"; -+ memory-region = <&smem>; -+ hwlocks = <&sfpb_mutex 3>; -+ }; - }; diff --git a/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch deleted file mode 100644 index b2c8cd5c53..0000000000 --- a/target/linux/ipq806x/patches-3.18/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch +++ /dev/null @@ -1,277 +0,0 @@ -From 0501f76b138cf1dc11a313bb7a094da524b79337 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 13 Aug 2015 09:53:14 -0700 -Subject: [PATCH 3/3] 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 ---- - drivers/mtd/Kconfig | 7 ++ - drivers/mtd/Makefile | 1 + - drivers/mtd/qcom_smem_part.c | 231 +++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 239 insertions(+) - create mode 100644 drivers/mtd/qcom_smem_part.c - ---- a/drivers/mtd/Kconfig -+++ b/drivers/mtd/Kconfig -@@ -200,6 +200,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,231 @@ -+/* -+ * 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 -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+/* 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) -+{ -+ int ret; -+ size_t size; -+ -+ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, -+ (void **) smem_blksz, &size); -+ -+ if (ret < 0) { -+ 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) -+{ -+ int ret; -+ size_t size; -+ -+ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, -+ (void **) smem_flash_type, &size); -+ -+ if (ret < 0) { -+ 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) -+{ -+ int ret; -+ size_t size; -+ -+ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, -+ (void **) pparts, &size); -+ -+ if (ret < 0) { -+ 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, -+ 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 = data->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 struct mtd_part_parser qcom_smem_parser = { -+ .owner = THIS_MODULE, -+ .parse_fn = parse_qcom_smem_partitions, -+ .name = "qcom-smem", -+}; -+ -+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 "); -+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); ---- a/drivers/mtd/Makefile -+++ b/drivers/mtd/Makefile -@@ -16,6 +16,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 - - # 'Users' - code which presents functionality to userspace. - obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o diff --git a/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch deleted file mode 100644 index 25803b88eb..0000000000 --- a/target/linux/ipq806x/patches-3.18/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch +++ /dev/null @@ -1,511 +0,0 @@ ---- a/drivers/phy/Kconfig -+++ b/drivers/phy/Kconfig -@@ -256,4 +256,15 @@ config PHY_STIH41X_USB - Enable this to support the USB transceiver that is part of - STMicroelectronics STiH41x SoC series. - -+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. -+ - endmenu ---- a/drivers/phy/Makefile -+++ b/drivers/phy/Makefile -@@ -31,3 +31,4 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += - obj-$(CONFIG_PHY_XGENE) += phy-xgene.o - obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o - obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o -+obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o ---- /dev/null -+++ b/drivers/phy/phy-qcom-dwc3.c -@@ -0,0 +1,483 @@ -+/* Copyright (c) 2013-2014, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * 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) -+ -+/* 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) -+ -+struct qcom_dwc3_usb_phy { -+ void __iomem *base; -+ struct device *dev; -+ struct phy *phy; -+ -+ int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3); -+ int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3); -+ -+ struct clk *xo_clk; -+ struct clk *ref_clk; -+}; -+ -+/** -+ * 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(void __iomem *base, u32 addr, u32 val) -+{ -+ int ret; -+ -+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); -+ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG); -+ -+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG); -+ if (ret) -+ goto err_wait; -+ -+ writel(val, base + CR_PROTOCOL_DATA_IN_REG); -+ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG); -+ -+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG); -+ if (ret) -+ goto err_wait; -+ -+ writel(0x1, base + CR_PROTOCOL_WRITE_REG); -+ -+ ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG); -+ -+err_wait: -+ 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; -+ bool first_read = true; -+ -+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); -+ writel(0x1, 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. -+ */ -+retry: -+ writel(0x1, base + CR_PROTOCOL_READ_REG); -+ -+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); -+ if (ret) -+ goto err_wait; -+ -+ if (first_read) { -+ readl(base + CR_PROTOCOL_DATA_OUT_REG); -+ first_read = false; -+ goto retry; -+ } -+ -+ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG); -+ -+err_wait: -+ return ret; -+} -+ -+static int qcom_dwc3_phy_power_on(struct phy *phy) -+{ -+ int ret; -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ 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; -+} -+ -+static int qcom_dwc3_phy_power_off(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_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3) -+{ -+ u32 val; -+ -+ /* -+ * 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_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3) -+{ -+ int ret; -+ u32 data = 0; -+ -+ /* reset phy */ -+ data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET, -+ phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ usleep_range(2000, 2200); -+ writel_relaxed(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_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ msleep(30); -+ -+ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; -+ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ -+ /* -+ * 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 to 3 -+ * 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 |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT; -+ data |= RX_OVRD_IN_HI_RX_EQ_OVRD; -+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base, -+ 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 to 22 -+ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127 -+ * 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 |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT; -+ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; -+ data |= 0x7f; -+ data |= TX_OVRD_DRV_LO_EN; -+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base, -+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); -+ if (ret) -+ goto err_phy_trans; -+ -+ /* -+ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows -+ * TX_FULL_SWING [26:20] amplitude to 127 -+ * TX_DEEMPH_3_5DB [13:8] to 22 -+ * LOS_BIAS [2:0] to 0x5 -+ */ -+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, -+ 0x07f03f07, 0x07f01605); -+ -+err_phy_trans: -+ return ret; -+} -+ -+static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3) -+{ -+ /* 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 -+ * 4. Disable SSPHY ref clk -+ */ -+ 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, -+ 0x0, SSUSB_CTRL_TEST_POWERDOWN); -+ -+ return 0; -+} -+ -+static int qcom_dwc3_phy_init(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ if (phy_dwc3->phy_init) -+ return phy_dwc3->phy_init(phy_dwc3); -+ -+ return 0; -+} -+ -+static int qcom_dwc3_phy_exit(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ if (phy_dwc3->phy_exit) -+ return qcom_dwc3_ss_phy_exit(phy_dwc3); -+ -+ return 0; -+} -+ -+static struct phy_ops qcom_dwc3_phy_ops = { -+ .init = qcom_dwc3_phy_init, -+ .exit = qcom_dwc3_phy_exit, -+ .power_on = qcom_dwc3_phy_power_on, -+ .power_off = qcom_dwc3_phy_power_off, -+ .owner = THIS_MODULE, -+}; -+ -+static const struct of_device_id qcom_dwc3_phy_table[] = { -+ { .compatible = "qcom,dwc3-hs-usb-phy", }, -+ { .compatible = "qcom,dwc3-ss-usb-phy", }, -+ { /* 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 resource *res; -+ -+ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); -+ if (!phy_dwc3) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, phy_dwc3); -+ -+ 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); -+ } -+ -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "qcom,dwc3-hs-usb-phy")) { -+ clk_set_rate(phy_dwc3->ref_clk, 60000000); -+ phy_dwc3->phy_init = qcom_dwc3_hs_phy_init; -+ } else if (of_device_is_compatible(pdev->dev.of_node, -+ "qcom,dwc3-ss-usb-phy")) { -+ phy_dwc3->phy_init = qcom_dwc3_ss_phy_init; -+ phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit; -+ clk_set_rate(phy_dwc3->ref_clk, 125000000); -+ } else { -+ dev_err(phy_dwc3->dev, "Unknown phy\n"); -+ return -EINVAL; -+ } -+ -+ 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; -+ } -+ -+ phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops, -+ NULL); -+ -+ if (IS_ERR(phy_dwc3->phy)) -+ return PTR_ERR(phy_dwc3->phy); -+ -+ phy_set_drvdata(phy_dwc3->phy, 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 "); -+MODULE_AUTHOR("Ivan T. Ivanov "); -+MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); diff --git a/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch deleted file mode 100644 index e2d03d4ffd..0000000000 --- a/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch +++ /dev/null @@ -1,126 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -91,5 +91,29 @@ - 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"; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -296,6 +296,91 @@ - compatible = "syscon"; - reg = <0x01200600 0x100>; - }; -+ -+ hs_phy_1: phy@100f8800 { -+ compatible = "qcom,dwc3-hs-usb-phy"; -+ reg = <0x100f8800 0x30>; -+ clocks = <&gcc USB30_1_UTMI_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ ss_phy_1: phy@100f8830 { -+ compatible = "qcom,dwc3-ss-usb-phy"; -+ reg = <0x100f8830 0x30>; -+ clocks = <&gcc USB30_1_MASTER_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ hs_phy_0: phy@110f8800 { -+ compatible = "qcom,dwc3-hs-usb-phy"; -+ reg = <0x110f8800 0x30>; -+ clocks = <&gcc USB30_0_UTMI_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ ss_phy_0: phy@110f8830 { -+ compatible = "qcom,dwc3-ss-usb-phy"; -+ reg = <0x110f8830 0x30>; -+ clocks = <&gcc USB30_0_MASTER_CLK>; -+ clock-names = "ref"; -+ #phy-cells = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ usb3_0: usb30@0 { -+ compatible = "qcom,dwc3"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ clocks = <&gcc USB30_0_MASTER_CLK>; -+ clock-names = "core"; -+ -+ ranges; -+ -+ status = "disabled"; -+ -+ dwc3@11000000 { -+ compatible = "snps,dwc3"; -+ reg = <0x11000000 0xcd00>; -+ interrupts = <0 110 0x4>; -+ phys = <&hs_phy_0>, <&ss_phy_0>; -+ phy-names = "usb2-phy", "usb3-phy"; -+ tx-fifo-resize; -+ dr_mode = "host"; -+ }; -+ }; -+ -+ usb3_1: usb30@1 { -+ compatible = "qcom,dwc3"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ clocks = <&gcc USB30_1_MASTER_CLK>; -+ clock-names = "core"; -+ -+ ranges; -+ -+ status = "disabled"; -+ -+ dwc3@10000000 { -+ compatible = "snps,dwc3"; -+ reg = <0x10000000 0xcd00>; -+ interrupts = <0 205 0x4>; -+ phys = <&hs_phy_1>, <&ss_phy_1>; -+ phy-names = "usb2-phy", "usb3-phy"; -+ tx-fifo-resize; -+ dr_mode = "host"; -+ }; -+ }; -+ - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch b/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch deleted file mode 100644 index 752f3f74d5..0000000000 --- a/target/linux/ipq806x/patches-3.18/102-soc-qcom-gsbi-Add-support-for-ADM-CRCI-muxing.patch +++ /dev/null @@ -1,249 +0,0 @@ ---- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt -+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt -@@ -6,7 +6,8 @@ configuration settings. The mode settin - the 4 GSBI IOs. - - Required properties: --- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064 -+- compatible: Should contain "qcom,gsbi-v1.0.0" -+- cell-index: Should contain the GSBI index - - reg: Address range for GSBI registers - - clocks: required clock - - clock-names: must contain "iface" entry -@@ -16,6 +17,8 @@ Required properties: - Optional properties: - - qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference - dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values. -+- syscon-tcsr: indicates phandle of TCSR syscon node. Required if child uses -+ dma. - - Required properties if child node exists: - - #address-cells: Must be 1 -@@ -39,6 +42,7 @@ Example for APQ8064: - - gsbi4@16300000 { - compatible = "qcom,gsbi-v1.0.0"; -+ cell-index = <4>; - reg = <0x16300000 0x100>; - clocks = <&gcc GSBI4_H_CLK>; - clock-names = "iface"; -@@ -48,6 +52,8 @@ Example for APQ8064: - qcom,mode = ; - qcom,crci = ; - -+ syscon-tcsr = <&tcsr>; -+ - /* child nodes go under here */ - - i2c_qup4: i2c@16380000 { -@@ -76,3 +82,9 @@ Example for APQ8064: - }; - }; - -+ tcsr: syscon@1a400000 { -+ compatible = "qcom,apq8064-tcsr", "syscon"; -+ reg = <0x1a400000 0x100>; -+ }; -+ -+ ---- a/drivers/soc/qcom/Kconfig -+++ b/drivers/soc/qcom/Kconfig -@@ -4,6 +4,7 @@ - config QCOM_GSBI - tristate "QCOM General Serial Bus Interface" - depends on ARCH_QCOM -+ select MFD_SYSCON - help - Say y here to enable GSBI support. The GSBI provides control - functions for connecting the underlying serial UART, SPI, and I2C ---- a/drivers/soc/qcom/qcom_gsbi.c -+++ b/drivers/soc/qcom/qcom_gsbi.c -@@ -18,22 +18,129 @@ - #include - #include - #include -+#include -+#include -+#include - - #define GSBI_CTRL_REG 0x0000 - #define GSBI_PROTOCOL_SHIFT 4 -+#define MAX_GSBI 12 -+ -+#define TCSR_ADM_CRCI_BASE 0x70 -+ -+struct crci_config { -+ u32 num_rows; -+ const u32 (*array)[MAX_GSBI]; -+}; -+ -+static const u32 crci_ipq8064[][MAX_GSBI] = { -+ { -+ 0x000003, 0x00000c, 0x000030, 0x0000c0, -+ 0x000300, 0x000c00, 0x003000, 0x00c000, -+ 0x030000, 0x0c0000, 0x300000, 0xc00000 -+ }, -+ { -+ 0x000003, 0x00000c, 0x000030, 0x0000c0, -+ 0x000300, 0x000c00, 0x003000, 0x00c000, -+ 0x030000, 0x0c0000, 0x300000, 0xc00000 -+ }, -+}; -+ -+static const struct crci_config config_ipq8064 = { -+ .num_rows = ARRAY_SIZE(crci_ipq8064), -+ .array = crci_ipq8064, -+}; -+ -+static const unsigned int crci_apq8064[][MAX_GSBI] = { -+ { -+ 0x001800, 0x006000, 0x000030, 0x0000c0, -+ 0x000300, 0x000400, 0x000000, 0x000000, -+ 0x000000, 0x000000, 0x000000, 0x000000 -+ }, -+ { -+ 0x000000, 0x000000, 0x000000, 0x000000, -+ 0x000000, 0x000020, 0x0000c0, 0x000000, -+ 0x000000, 0x000000, 0x000000, 0x000000 -+ }, -+}; -+ -+static const struct crci_config config_apq8064 = { -+ .num_rows = ARRAY_SIZE(crci_apq8064), -+ .array = crci_apq8064, -+}; -+ -+static const unsigned int crci_msm8960[][MAX_GSBI] = { -+ { -+ 0x000003, 0x00000c, 0x000030, 0x0000c0, -+ 0x000300, 0x000400, 0x000000, 0x000000, -+ 0x000000, 0x000000, 0x000000, 0x000000 -+ }, -+ { -+ 0x000000, 0x000000, 0x000000, 0x000000, -+ 0x000000, 0x000020, 0x0000c0, 0x000300, -+ 0x001800, 0x006000, 0x000000, 0x000000 -+ }, -+}; -+ -+static const struct crci_config config_msm8960 = { -+ .num_rows = ARRAY_SIZE(crci_msm8960), -+ .array = crci_msm8960, -+}; -+ -+static const unsigned int crci_msm8660[][MAX_GSBI] = { -+ { /* ADM 0 - B */ -+ 0x000003, 0x00000c, 0x000030, 0x0000c0, -+ 0x000300, 0x000c00, 0x003000, 0x00c000, -+ 0x030000, 0x0c0000, 0x300000, 0xc00000 -+ }, -+ { /* ADM 0 - B */ -+ 0x000003, 0x00000c, 0x000030, 0x0000c0, -+ 0x000300, 0x000c00, 0x003000, 0x00c000, -+ 0x030000, 0x0c0000, 0x300000, 0xc00000 -+ }, -+ { /* ADM 1 - A */ -+ 0x000003, 0x00000c, 0x000030, 0x0000c0, -+ 0x000300, 0x000c00, 0x003000, 0x00c000, -+ 0x030000, 0x0c0000, 0x300000, 0xc00000 -+ }, -+ { /* ADM 1 - B */ -+ 0x000003, 0x00000c, 0x000030, 0x0000c0, -+ 0x000300, 0x000c00, 0x003000, 0x00c000, -+ 0x030000, 0x0c0000, 0x300000, 0xc00000 -+ }, -+}; -+ -+static const struct crci_config config_msm8660 = { -+ .num_rows = ARRAY_SIZE(crci_msm8660), -+ .array = crci_msm8660, -+}; - - struct gsbi_info { - struct clk *hclk; - u32 mode; - u32 crci; -+ struct regmap *tcsr; -+}; -+ -+static const struct of_device_id tcsr_dt_match[] = { -+ { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064}, -+ { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064}, -+ { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960}, -+ { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660}, -+ { }, - }; - - static int gsbi_probe(struct platform_device *pdev) - { - struct device_node *node = pdev->dev.of_node; -+ struct device_node *tcsr_node; -+ const struct of_device_id *match; - struct resource *res; - void __iomem *base; - struct gsbi_info *gsbi; -+ int i; -+ u32 mask, gsbi_num; -+ const struct crci_config *config = NULL; - - gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL); - -@@ -45,6 +152,32 @@ static int gsbi_probe(struct platform_de - if (IS_ERR(base)) - return PTR_ERR(base); - -+ /* get the tcsr node and setup the config and regmap */ -+ gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr"); -+ -+ if (!IS_ERR(gsbi->tcsr)) { -+ tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0); -+ if (tcsr_node) { -+ match = of_match_node(tcsr_dt_match, tcsr_node); -+ if (match) -+ config = match->data; -+ else -+ dev_warn(&pdev->dev, "no matching TCSR\n"); -+ -+ of_node_put(tcsr_node); -+ } -+ } -+ -+ if (of_property_read_u32(node, "cell-index", &gsbi_num)) { -+ dev_err(&pdev->dev, "missing cell-index\n"); -+ return -EINVAL; -+ } -+ -+ if (gsbi_num < 1 || gsbi_num > MAX_GSBI) { -+ dev_err(&pdev->dev, "invalid cell-index\n"); -+ return -EINVAL; -+ } -+ - if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) { - dev_err(&pdev->dev, "missing mode configuration\n"); - return -EINVAL; -@@ -64,6 +197,25 @@ static int gsbi_probe(struct platform_de - writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci, - base + GSBI_CTRL_REG); - -+ /* -+ * modify tcsr to reflect mode and ADM CRCI mux -+ * Each gsbi contains a pair of bits, one for RX and one for TX -+ * SPI mode requires both bits cleared, otherwise they are set -+ */ -+ if (config) { -+ for (i = 0; i < config->num_rows; i++) { -+ mask = config->array[i][gsbi_num - 1]; -+ -+ if (gsbi->mode == GSBI_PROT_SPI) -+ regmap_update_bits(gsbi->tcsr, -+ TCSR_ADM_CRCI_BASE + 4 * i, mask, 0); -+ else -+ regmap_update_bits(gsbi->tcsr, -+ TCSR_ADM_CRCI_BASE + 4 * i, mask, mask); -+ -+ } -+ } -+ - /* make sure the gsbi control write is not reordered */ - wmb(); - diff --git a/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch b/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch deleted file mode 100644 index 322e1d9237..0000000000 --- a/target/linux/ipq806x/patches-3.18/103-ARM-DT-ipq8064-Add-TCSR-support.patch +++ /dev/null @@ -1,65 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -132,6 +132,7 @@ - - gsbi2: gsbi@12480000 { - compatible = "qcom,gsbi-v1.0.0"; -+ cell-index = <2>; - reg = <0x12480000 0x100>; - clocks = <&gcc GSBI2_H_CLK>; - clock-names = "iface"; -@@ -140,6 +141,8 @@ - ranges; - status = "disabled"; - -+ syscon-tcsr = <&tcsr>; -+ - uart2: serial@12490000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x12490000 0x1000>, -@@ -167,6 +170,7 @@ - - gsbi4: gsbi@16300000 { - compatible = "qcom,gsbi-v1.0.0"; -+ cell-index = <4>; - reg = <0x16300000 0x100>; - clocks = <&gcc GSBI4_H_CLK>; - clock-names = "iface"; -@@ -175,6 +179,8 @@ - ranges; - status = "disabled"; - -+ syscon-tcsr = <&tcsr>; -+ - uart4: serial@16340000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x16340000 0x1000>, -@@ -201,6 +207,7 @@ - - gsbi5: gsbi@1a200000 { - compatible = "qcom,gsbi-v1.0.0"; -+ cell-index = <5>; - reg = <0x1a200000 0x100>; - clocks = <&gcc GSBI5_H_CLK>; - clock-names = "iface"; -@@ -209,6 +216,8 @@ - ranges; - status = "disabled"; - -+ syscon-tcsr = <&tcsr>; -+ - uart5: serial@1a240000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x1a240000 0x1000>, -@@ -279,6 +288,11 @@ - status = "disabled"; - }; - -+ tcsr: syscon@1a400000 { -+ compatible = "qcom,tcsr-ipq8064", "syscon"; -+ reg = <0x1a400000 0x100>; -+ }; -+ - qcom,ssbi@500000 { - compatible = "qcom,ssbi"; - reg = <0x00500000 0x1000>; diff --git a/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch deleted file mode 100644 index 41f91fae7c..0000000000 --- a/target/linux/ipq806x/patches-3.18/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch +++ /dev/null @@ -1,263 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings -From: Stanimir Varbanov -X-Patchwork-Id: 6326181 -Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com> -To: Rob Herring , Kumar Gala , - Mark Rutland , - Grant Likely , - Bjorn Helgaas , - Kishon Vijay Abraham I , - Russell King , Arnd Bergmann -Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, - linux-pci@vger.kernel.org, Mathieu Olivari , - Srinivas Kandagatla , - Stanimir Varbanov -Date: Mon, 4 May 2015 15:42:16 +0300 - -Document Qualcomm PCIe driver devicetree bindings. - -Signed-off-by: Stanimir Varbanov - ---- -.../devicetree/bindings/pci/qcom,pcie.txt | 231 ++++++++++++++++++++ - 1 files changed, 231 insertions(+), 0 deletions(-) - create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt -@@ -0,0 +1,231 @@ -+* Qualcomm PCI express root complex -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: Value shall include -+ - "qcom,pcie-v0" for apq/ipq8064 -+ - "qcom,pcie-v1" for apq8084 -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: Register ranges as listed in the reg-names property -+ -+- reg-names: -+ Usage: required -+ Value type: -+ Definition: Must include the following entries -+ - "parf" Qualcomm specific registers -+ - "dbi" Designware PCIe registers -+ - "elbi" External local bus interface registers -+ - "config" PCIe configuration space -+ -+- device_type: -+ Usage: required -+ Value type: -+ Definition: Should be "pci". As specified in designware-pcie.txt -+ -+- #address-cells: -+ Usage: required -+ Value type: -+ Definition: Should be set to 3. As specified in designware-pcie.txt -+ -+- #size-cells: -+ Usage: required -+ Value type: -+ Definition: Should be set 2. As specified in designware-pcie.txt -+ -+- ranges: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- interrupts: -+ Usage: required -+ Value type: -+ Definition: MSI interrupt -+ -+- interrupt-names: -+ Usage: required -+ Value type: -+ Definition: Should contain "msi" -+ -+- #interrupt-cells: -+ Usage: required -+ Value type: -+ Definition: Should be 1. As specified in designware-pcie.txt -+ -+- interrupt-map-mask: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- interrupt-map: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- clocks: -+ Usage: required -+ Value type: -+ Definition: List of phandle and clock specifier pairs as listed -+ in clock-names property -+ -+- clock-names: -+ Usage: required -+ Value type: -+ Definition: Should contain the following entries -+ * should be populated for v0 and v1 -+ - "iface" Configuration AHB clock -+ -+ * should be populated for v0 -+ - "core" Clocks the pcie hw block -+ - "phy" Clocks the pcie PHY block -+ -+ * should be populated for v1 -+ - "aux" Auxiliary (AUX) clock -+ - "bus_master" Master AXI clock -+ - "bus_slave" Slave AXI clock -+ -+- resets: -+ Usage: required -+ Value type: -+ Definition: List of phandle and reset specifier pairs as listed -+ in reset-names property -+ -+- reset-names: -+ Usage: required -+ Value type: -+ Definition: Should contain the following entries -+ * should be populated for v0 -+ - "axi" AXI reset -+ - "ahb" AHB reset -+ - "por" POR reset -+ - "pci" PCI reset -+ - "phy" PHY reset -+ -+ * should be populated for v1 -+ - "core" Core reset -+ -+- power-domains: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: A phandle and power domain specifier pair to the -+ power domain which is responsible for collapsing -+ and restoring power to the peripheral -+ -+- -supply: -+ Usage: required -+ Value type: -+ Definition: List of phandles to the power supply regulator(s) -+ * should be populated for v0 and v1 -+ - "vdda" core analog power supply -+ -+ * should be populated for v0 -+ - "vdda_phy" analog power supply for PHY -+ - "vdda_refclk" analog power supply for IC which generate -+ reference clock -+ -+- phys: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: List of phandle(s) as listed in phy-names property -+ -+- phy-names: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: Should contain "pciephy" -+ -+- -gpio: -+ Usage: optional -+ Value type: -+ Definition: List of phandle and gpio specifier pairs. Should contain -+ - "perst" PCIe endpoint reset signal line -+ - "pewake" PCIe endpoint wake signal line -+ -+- pinctrl-0: -+ Usage: required -+ Value type: -+ Definition: List of phandles pointing at a pin(s) configuration -+ -+- pinctrl-names -+ Usage: required -+ Value type: -+ Definition: List of names of pinctrl-0 state -+ -+* Example for v0 -+ pcie0: pci@1b500000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b500000 0x1000 -+ 0x1b502000 0x80 -+ 0x1b600000 0x100 -+ 0x0ff00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */ -+ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ clocks = <&gcc PCIE_A_CLK>, -+ <&gcc PCIE_H_CLK>, -+ <&gcc PCIE_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ resets = <&gcc PCIE_ACLK_RESET>, -+ <&gcc PCIE_HCLK_RESET>, -+ <&gcc PCIE_POR_RESET>, -+ <&gcc PCIE_PCI_RESET>, -+ <&gcc PCIE_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ }; -+ -+* Example for v1 -+ pcie0@fc520000 { -+ compatible = "qcom,pcie-v1"; -+ reg = <0xfc520000 0x2000>, -+ <0xff000000 0x1000>, -+ <0xff001000 0x1000>, -+ <0xff002000 0x2000>; -+ reg-names = "parf", "dbi", "elbi", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */ -+ 0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>, -+ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, -+ <&gcc GCC_PCIE_0_SLV_AXI_CLK>, -+ <&gcc GCC_PCIE_0_AUX_CLK>; -+ clock-names = "iface", "master_bus", "slave_bus", "aux"; -+ resets = <&gcc GCC_PCIE_0_BCR>; -+ reset-names = "core"; -+ power-domains = <&gcc PCIE0_GDSC>; -+ vdda-supply = <&pma8084_l3>; -+ phys = <&pciephy0>; -+ phy-names = "pciephy"; -+ perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>; -+ pinctrl-0 = <&pcie0_pins_default>; -+ pinctrl-names = "default"; -+ }; diff --git a/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch deleted file mode 100644 index a0c9c7c36e..0000000000 --- a/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch +++ /dev/null @@ -1,753 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver -From: Stanimir Varbanov -X-Patchwork-Id: 6326161 -Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com> -To: Rob Herring , Kumar Gala , - Mark Rutland , - Grant Likely , - Bjorn Helgaas , - Kishon Vijay Abraham I , - Russell King , Arnd Bergmann -Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, - linux-pci@vger.kernel.org, Mathieu Olivari , - Srinivas Kandagatla , - Stanimir Varbanov -Date: Mon, 4 May 2015 15:42:17 +0300 - -The PCIe driver reuse the Designware common code for host -and MSI initialization, and also program the Qualcomm -application specific registers. - -Signed-off-by: Stanimir Varbanov - ---- -MAINTAINERS | 7 + - drivers/pci/host/Kconfig | 9 + - drivers/pci/host/Makefile | 1 + - drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 694 insertions(+), 0 deletions(-) - create mode 100644 drivers/pci/host/pcie-qcom.c - ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -7127,6 +7127,13 @@ L: linux-pci@vger.kernel.org - S: Maintained - F: drivers/pci/host/*spear* - -+PCIE DRIVER FOR QUALCOMM MSM -+M: Stanimir Varbanov -+L: linux-pci@vger.kernel.org -+L: linux-arm-msm@vger.kernel.org -+S: Maintained -+F: drivers/pci/host/*qcom* -+ - PCMCIA SUBSYSTEM - P: Linux PCMCIA Team - L: linux-pcmcia@lists.infradead.org ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig -@@ -91,4 +91,13 @@ config PCI_XGENE - There are 5 internal PCIe ports available. Each port is GEN3 capable - and have varied lanes from x1 to x8. - -+config PCIE_QCOM -+ bool "Qualcomm PCIe controller" -+ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST) -+ select PCIE_DW -+ select PCIEPORTBUS -+ help -+ Say Y here to enable PCIe controller support on Qualcomm SoCs. The -+ PCIe controller use Designware core plus Qualcomm specific hardware -+ wrappers. - endmenu ---- /dev/null -+++ b/drivers/pci/host/pcie-qcom.c -@@ -0,0 +1,677 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "pcie-designware.h" -+ -+#define PCIE20_PARF_PHY_CTRL 0x40 -+#define PCIE20_PARF_PHY_REFCLK 0x4C -+#define PCIE20_PARF_DBI_BASE_ADDR 0x168 -+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c -+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -+ -+#define PCIE20_ELBI_SYS_CTRL 0x04 -+#define PCIE20_ELBI_SYS_STTS 0x08 -+#define XMLH_LINK_UP BIT(10) -+ -+#define PCIE20_CAP 0x70 -+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -+ -+#define PERST_DELAY_MIN_US 1000 -+#define PERST_DELAY_MAX_US 1005 -+ -+#define LINKUP_DELAY_MIN_US 5000 -+#define LINKUP_DELAY_MAX_US 5100 -+#define LINKUP_RETRIES_COUNT 20 -+ -+#define PCIE_V0 0 /* apq8064 */ -+#define PCIE_V1 1 /* apq8084 */ -+ -+struct qcom_pcie_resources_v0 { -+ struct clk *iface_clk; -+ struct clk *core_clk; -+ struct clk *phy_clk; -+ struct reset_control *pci_reset; -+ struct reset_control *axi_reset; -+ struct reset_control *ahb_reset; -+ struct reset_control *por_reset; -+ struct reset_control *phy_reset; -+ struct regulator *vdda; -+ struct regulator *vdda_phy; -+ struct regulator *vdda_refclk; -+}; -+ -+struct qcom_pcie_resources_v1 { -+ struct clk *iface; -+ struct clk *aux; -+ struct clk *master_bus; -+ struct clk *slave_bus; -+ struct reset_control *core; -+ struct regulator *vdda; -+}; -+ -+union pcie_resources { -+ struct qcom_pcie_resources_v0 v0; -+ struct qcom_pcie_resources_v1 v1; -+}; -+ -+struct qcom_pcie { -+ struct pcie_port pp; -+ struct device *dev; -+ union pcie_resources res; -+ void __iomem *parf; -+ void __iomem *dbi; -+ void __iomem *elbi; -+ struct phy *phy; -+ struct gpio_desc *reset; -+ unsigned int version; -+}; -+ -+#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) -+ -+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_deassert(struct qcom_pcie *pcie, int assert) -+{ -+ int val, active_low; -+ -+ if (IS_ERR_OR_NULL(pcie->reset)) -+ return; -+ -+ active_low = gpiod_is_active_low(pcie->reset); -+ -+ if (assert) -+ val = !!active_low; -+ else -+ val = !active_low; -+ -+ gpiod_set_value(pcie->reset, val); -+ -+ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); -+} -+ -+static void qcom_ep_reset_assert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 1); -+} -+ -+static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 0); -+} -+ -+static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) -+{ -+ struct pcie_port *pp = arg; -+ -+ return dw_handle_msi_irq(pp); -+} -+ -+static int qcom_pcie_link_up(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS); -+ -+ return val & BIT(29) ? 1 : 0; -+} -+ -+static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ -+ reset_control_assert(res->pci_reset); -+ reset_control_assert(res->axi_reset); -+ reset_control_assert(res->ahb_reset); -+ reset_control_assert(res->por_reset); -+ reset_control_assert(res->pci_reset); -+ clk_disable_unprepare(res->iface_clk); -+ clk_disable_unprepare(res->core_clk); -+ clk_disable_unprepare(res->phy_clk); -+ regulator_disable(res->vdda); -+ regulator_disable(res->vdda_phy); -+ regulator_disable(res->vdda_refclk); -+} -+ -+static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ -+ reset_control_assert(res->core); -+ clk_disable_unprepare(res->slave_bus); -+ clk_disable_unprepare(res->master_bus); -+ clk_disable_unprepare(res->iface); -+ clk_disable_unprepare(res->aux); -+ regulator_disable(res->vdda); -+} -+ -+static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ return ret; -+ } -+ -+ ret = regulator_enable(res->vdda_refclk); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_refclk regulator\n"); -+ goto err_refclk; -+ } -+ -+ ret = regulator_enable(res->vdda_phy); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_phy regulator\n"); -+ goto err_vdda_phy; -+ } -+ -+ ret = clk_prepare_enable(res->iface_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_iface; -+ } -+ -+ 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 = reset_control_deassert(res->ahb_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert ahb reset\n"); -+ goto err_reset_ahb; -+ } -+ -+ return 0; -+ -+err_reset_ahb: -+ 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_iface: -+ regulator_disable(res->vdda_phy); -+err_vdda_phy: -+ regulator_disable(res->vdda_refclk); -+err_refclk: -+ regulator_disable(res->vdda); -+ return ret; -+} -+ -+static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = reset_control_deassert(res->core); -+ if (ret) { -+ dev_err(dev, "cannot deassert core reset\n"); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(res->aux); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable aux clock\n"); -+ goto err_res; -+ } -+ -+ ret = clk_prepare_enable(res->iface); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_aux; -+ } -+ -+ ret = clk_prepare_enable(res->master_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable master_bus clock\n"); -+ goto err_iface; -+ } -+ -+ ret = clk_prepare_enable(res->slave_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable slave_bus clock\n"); -+ goto err_master; -+ } -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ goto err_slave; -+ } -+ -+ return 0; -+ -+err_slave: -+ clk_disable_unprepare(res->slave_bus); -+err_master: -+ clk_disable_unprepare(res->master_bus); -+err_iface: -+ clk_disable_unprepare(res->iface); -+err_aux: -+ clk_disable_unprepare(res->aux); -+err_res: -+ reset_control_assert(res->core); -+ -+ return ret; -+} -+ -+static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); -+ if (IS_ERR(res->vdda_phy)) -+ return PTR_ERR(res->vdda_phy); -+ -+ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); -+ if (IS_ERR(res->vdda_refclk)) -+ return PTR_ERR(res->vdda_refclk); -+ -+ res->iface_clk = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface_clk)) -+ return PTR_ERR(res->iface_clk); -+ -+ res->core_clk = devm_clk_get(dev, "core"); -+ if (IS_ERR(res->core_clk)) -+ return PTR_ERR(res->core_clk); -+ -+ res->phy_clk = devm_clk_get(dev, "phy"); -+ if (IS_ERR(res->phy_clk)) -+ return PTR_ERR(res->phy_clk); -+ -+ res->pci_reset = devm_reset_control_get(dev, "pci"); -+ if (IS_ERR(res->pci_reset)) -+ return PTR_ERR(res->pci_reset); -+ -+ res->axi_reset = devm_reset_control_get(dev, "axi"); -+ if (IS_ERR(res->axi_reset)) -+ return PTR_ERR(res->axi_reset); -+ -+ res->ahb_reset = devm_reset_control_get(dev, "ahb"); -+ if (IS_ERR(res->ahb_reset)) -+ return PTR_ERR(res->ahb_reset); -+ -+ res->por_reset = devm_reset_control_get(dev, "por"); -+ if (IS_ERR(res->por_reset)) -+ return PTR_ERR(res->por_reset); -+ -+ res->phy_reset = devm_reset_control_get(dev, "phy"); -+ if (IS_ERR(res->phy_reset)) -+ return PTR_ERR(res->phy_reset); -+ -+ return 0; -+} -+ -+static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->iface = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface)) -+ return PTR_ERR(res->iface); -+ -+ res->aux = devm_clk_get(dev, "aux"); -+ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ else if (IS_ERR(res->aux)) -+ res->aux = NULL; -+ -+ res->master_bus = devm_clk_get(dev, "master_bus"); -+ if (IS_ERR(res->master_bus)) -+ return PTR_ERR(res->master_bus); -+ -+ res->slave_bus = devm_clk_get(dev, "slave_bus"); -+ if (IS_ERR(res->slave_bus)) -+ return PTR_ERR(res->slave_bus); -+ -+ res->core = devm_reset_control_get(dev, "core"); -+ if (IS_ERR(res->core)) -+ return PTR_ERR(res->core); -+ -+ return 0; -+} -+ -+static int qcom_pcie_enable_link_training(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct device *dev = pp->dev; -+ int retries; -+ u32 val; -+ -+ /* enable link training */ -+ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); -+ -+ /* wait for up to 100ms for the link to come up */ -+ retries = LINKUP_RETRIES_COUNT; -+ do { -+ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS); -+ if (val & XMLH_LINK_UP) -+ break; -+ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US); -+ } while (retries--); -+ -+ if (retries < 0 || !dw_pcie_link_up(pp)) { -+ dev_err(dev, "link initialization failed\n"); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+static void qcom_pcie_host_init_v1(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v1(pcie); -+ if (ret) -+ return; -+ -+ /* change DBI base address */ -+ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, -+ 0, BIT(31)); -+ -+ ret = phy_init(pcie->phy); -+ if (ret) -+ goto err_res; -+ -+ ret = phy_power_on(pcie->phy); -+ if (ret) -+ goto err_phy; -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+ -+err: -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+err_phy: -+ phy_exit(pcie->phy); -+err_res: -+ qcom_pcie_disable_resources_v1(pcie); -+} -+ -+static void qcom_pcie_host_init_v0(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v0(pcie); -+ if (ret) -+ return; -+ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); -+ -+ /* enable external reference clock */ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); -+ -+ ret = reset_control_deassert(res->phy_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert phy reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->pci_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert pci reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->por_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert por reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->axi_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert axi reset\n"); -+ return; -+ } -+ -+ /* wait 150ms for clock acquisition */ -+ usleep_range(10000, 15000); -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+err: -+ qcom_ep_reset_assert(pcie); -+ qcom_pcie_disable_resources_v0(pcie); -+} -+ -+static void qcom_pcie_host_init(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ -+ if (pcie->version == PCIE_V0) -+ return qcom_pcie_host_init_v0(pp); -+ else -+ return qcom_pcie_host_init_v1(pp); -+} -+ -+static int -+qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) -+{ -+ /* the device class is not reported correctly from the register */ -+ if (where == PCI_CLASS_REVISION && size == 4) { -+ *val = readl(pp->dbi_base + PCI_CLASS_REVISION); -+ *val &= ~(0xffff << 16); -+ *val |= PCI_CLASS_BRIDGE_PCI << 16; -+ return PCIBIOS_SUCCESSFUL; -+ } -+ -+ return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, -+ size, val); -+} -+ -+static struct pcie_host_ops qcom_pcie_ops = { -+ .link_up = qcom_pcie_link_up, -+ .host_init = qcom_pcie_host_init, -+ .rd_own_conf = qcom_pcie_rd_own_conf, -+}; -+ -+static const struct of_device_id qcom_pcie_match[] = { -+ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 }, -+ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 }, -+ { } -+}; -+ -+static int qcom_pcie_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct of_device_id *match; -+ struct resource *res; -+ struct qcom_pcie *pcie; -+ struct pcie_port *pp; -+ int ret; -+ -+ match = of_match_node(qcom_pcie_match, dev->of_node); -+ if (!match) -+ return -ENXIO; -+ -+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); -+ if (!pcie) -+ return -ENOMEM; -+ -+ pcie->version = (unsigned int)match->data; -+ -+ pcie->reset = devm_gpiod_get_optional(dev, "perst"); -+ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER) -+ return PTR_ERR(pcie->reset); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); -+ pcie->parf = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->parf)) -+ return PTR_ERR(pcie->parf); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -+ pcie->dbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->dbi)) -+ return PTR_ERR(pcie->dbi); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); -+ pcie->elbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->elbi)) -+ return PTR_ERR(pcie->elbi); -+ -+ pcie->phy = devm_phy_optional_get(dev, "pciephy"); -+ if (IS_ERR(pcie->phy)) -+ return PTR_ERR(pcie->phy); -+ -+ pcie->dev = dev; -+ -+ if (pcie->version == PCIE_V0) -+ ret = qcom_pcie_get_resources_v0(pcie); -+ else -+ ret = qcom_pcie_get_resources_v1(pcie); -+ -+ if (ret) -+ return ret; -+ -+ pp = &pcie->pp; -+ pp->dev = dev; -+ pp->dbi_base = pcie->dbi; -+ pp->root_bus_nr = -1; -+ pp->ops = &qcom_pcie_ops; -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) { -+ pp->msi_irq = platform_get_irq_byname(pdev, "msi"); -+ if (pp->msi_irq < 0) { -+ dev_err(dev, "cannot get msi irq\n"); -+ return pp->msi_irq; -+ } -+ -+ ret = devm_request_irq(dev, pp->msi_irq, -+ qcom_pcie_msi_irq_handler, -+ IRQF_SHARED, "qcom-pcie-msi", pp); -+ if (ret) { -+ dev_err(dev, "cannot request msi irq\n"); -+ return ret; -+ } -+ } -+ -+ ret = dw_pcie_host_init(pp); -+ if (ret) { -+ dev_err(dev, "cannot initialize host\n"); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, pcie); -+ -+ return 0; -+} -+ -+static int qcom_pcie_remove(struct platform_device *pdev) -+{ -+ struct qcom_pcie *pcie = platform_get_drvdata(pdev); -+ -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+ phy_exit(pcie->phy); -+ if (pcie->version == PCIE_V0) -+ qcom_pcie_disable_resources_v0(pcie); -+ else -+ qcom_pcie_disable_resources_v1(pcie); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_pcie_driver = { -+ .probe = qcom_pcie_probe, -+ .remove = qcom_pcie_remove, -+ .driver = { -+ .name = "qcom-pcie", -+ .of_match_table = qcom_pcie_match, -+ }, -+}; -+ -+module_platform_driver(qcom_pcie_driver); -+ -+MODULE_AUTHOR("Stanimir Varbanov "); -+MODULE_DESCRIPTION("Qualcomm PCIe root complex driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-pcie"); ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -11,3 +11,4 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spe - obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o - obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o - obj-$(CONFIG_PCI_XGENE) += pci-xgene.o -+obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o diff --git a/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch deleted file mode 100644 index a57de6c475..0000000000 --- a/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch +++ /dev/null @@ -1,244 +0,0 @@ -From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 21 Apr 2015 19:01:42 -0700 -Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms - -qcom-pcie driver now supports version 0 of the controller. This change -adds the corresponding entries to the IPQ806x dtsi file and -corresponding platform (AP148). - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 30 ++++++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 124 +++++++++++++++++++++++++++++++ - 2 files changed, 154 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -115,5 +115,15 @@ - usb30@1 { - status = "ok"; - }; -+ -+ pcie0: pci@1b500000 { -+ status = "ok"; -+ phy-tx0-term-offset = <7>; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ phy-tx0-term-offset = <7>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -128,5 +128,17 @@ - usb30@1 { - status = "ok"; - }; -+ -+ pcie0: pci@1b500000 { -+ status = "ok"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ }; -+ -+ pcie2: pci@1b900000 { -+ status = "ok"; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -3,6 +3,9 @@ - #include "skeleton.dtsi" - #include - #include -+#include -+#include -+#include - - / { - model = "Qualcomm IPQ8064"; -@@ -83,6 +86,33 @@ - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <0 32 0x4>; -+ -+ pcie0_pins: pcie0_pinmux { -+ mux { -+ pins = "gpio3"; -+ function = "pcie1_rst"; -+ drive-strength = <12>; -+ bias-disable; -+ }; -+ }; -+ -+ pcie1_pins: pcie1_pinmux { -+ mux { -+ pins = "gpio48"; -+ function = "pcie2_rst"; -+ drive-strength = <12>; -+ bias-disable; -+ }; -+ }; -+ -+ pcie2_pins: pcie2_pinmux { -+ mux { -+ pins = "gpio63"; -+ function = "pcie3_rst"; -+ drive-strength = <12>; -+ bias-disable; -+ }; -+ }; - }; - - intc: interrupt-controller@2000000 { -@@ -311,6 +341,144 @@ - reg = <0x01200600 0x100>; - }; - -+ pcie0: pci@1b500000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b500000 0x1000 -+ 0x1b502000 0x80 -+ 0x1b600000 0x100 -+ 0x0ff00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x0fe00000 0x0fe00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x08000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_A_CLK>, -+ <&gcc PCIE_H_CLK>, -+ <&gcc PCIE_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_ACLK_RESET>, -+ <&gcc PCIE_HCLK_RESET>, -+ <&gcc PCIE_POR_RESET>, -+ <&gcc PCIE_PCI_RESET>, -+ <&gcc PCIE_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie0_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b700000 0x1000 -+ 0x1b702000 0x80 -+ 0x1b800000 0x100 -+ 0x31f00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <1>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x31e00000 0x31e00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x2e000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_1_A_CLK>, -+ <&gcc PCIE_1_H_CLK>, -+ <&gcc PCIE_1_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_1_ACLK_RESET>, -+ <&gcc PCIE_1_HCLK_RESET>, -+ <&gcc PCIE_1_POR_RESET>, -+ <&gcc PCIE_1_PCI_RESET>, -+ <&gcc PCIE_1_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie1_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; -+ -+ pcie2: pci@1b900000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b900000 0x1000 -+ 0x1b902000 0x80 -+ 0x1ba00000 0x100 -+ 0x35f00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <2>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x35e00000 0x35e00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x32000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_2_A_CLK>, -+ <&gcc PCIE_2_H_CLK>, -+ <&gcc PCIE_2_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_2_ACLK_RESET>, -+ <&gcc PCIE_2_HCLK_RESET>, -+ <&gcc PCIE_2_POR_RESET>, -+ <&gcc PCIE_2_PCI_RESET>, -+ <&gcc PCIE_2_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie2_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpio = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; -+ - hs_phy_1: phy@100f8800 { - compatible = "qcom,dwc3-hs-usb-phy"; - reg = <0x100f8800 0x30>; diff --git a/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch deleted file mode 100644 index 4f0e45f656..0000000000 --- a/target/linux/ipq806x/patches-3.18/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch +++ /dev/null @@ -1,29 +0,0 @@ -From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 21 Apr 2015 19:09:07 -0700 -Subject: [PATCH 9/9] 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 ---- - 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 CLKSRC_OF - select PINCTRL - select QCOM_SCM if SMP -+ select PCI_DOMAINS if PCI - help - Support for Qualcomm's devicetree based systems. - diff --git a/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch deleted file mode 100644 index c00abca753..0000000000 --- a/target/linux/ipq806x/patches-3.18/114-pcie-add-ctlr-init.patch +++ /dev/null @@ -1,311 +0,0 @@ ---- a/drivers/pci/host/pcie-qcom.c -+++ b/drivers/pci/host/pcie-qcom.c -@@ -29,8 +29,53 @@ - - #include "pcie-designware.h" - -+/* DBI registers */ -+#define PCIE20_CAP 0x70 -+#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 -+ -+/* 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_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_CONFIG_BITS 0x50 -+#define PHY_RX0_EQ(x) (x << 24) -+ - #define PCIE20_PARF_DBI_BASE_ADDR 0x168 - #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c - #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -@@ -39,9 +84,6 @@ - #define PCIE20_ELBI_SYS_STTS 0x08 - #define XMLH_LINK_UP BIT(10) - --#define PCIE20_CAP 0x70 --#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -- - #define PERST_DELAY_MIN_US 1000 - #define PERST_DELAY_MAX_US 1005 - -@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 { - 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; - 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; -+ uint8_t phy_tx0_term_offset; - }; - - struct qcom_pcie_resources_v1 { -@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl - - static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) - { -- int val, active_low; -- - if (IS_ERR_OR_NULL(pcie->reset)) - return; - -- active_low = gpiod_is_active_low(pcie->reset); -- -- if (assert) -- val = !!active_low; -- else -- val = !active_low; -- -- gpiod_set_value(pcie->reset, val); -- -+ gpiod_set_value(pcie->reset, assert); - usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); - } - -@@ -156,10 +192,13 @@ static void qcom_pcie_disable_resources_ - reset_control_assert(res->axi_reset); - reset_control_assert(res->ahb_reset); - reset_control_assert(res->por_reset); -- reset_control_assert(res->pci_reset); -+ reset_control_assert(res->phy_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); -+ 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); -@@ -201,6 +240,12 @@ static int qcom_pcie_enable_resources_v0 - goto err_vdda_phy; - } - -+ ret = reset_control_deassert(res->ext_reset); -+ if (ret) { -+ 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"); -@@ -219,21 +264,40 @@ static int qcom_pcie_enable_resources_v0 - goto err_clk_phy; - } - -+ ret = clk_prepare_enable(res->aux_clk); -+ if (ret) { -+ 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); - if (ret) { - dev_err(dev, "cannot deassert ahb reset\n"); - goto err_reset_ahb; - } -+ udelay(1); - - return 0; - - err_reset_ahb: -+ 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_iface: -+ reset_control_assert(res->ext_reset); -+err_reset_ext: - regulator_disable(res->vdda_phy); - err_vdda_phy: - regulator_disable(res->vdda_refclk); -@@ -329,6 +393,14 @@ static int qcom_pcie_get_resources_v0(st - 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(dev, "pci"); - if (IS_ERR(res->pci_reset)) - return PTR_ERR(res->pci_reset); -@@ -349,6 +421,14 @@ static int qcom_pcie_get_resources_v0(st - if (IS_ERR(res->phy_reset)) - return PTR_ERR(res->phy_reset); - -+ res->ext_reset = devm_reset_control_get(dev, "ext"); -+ 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; -+ - return 0; - } - -@@ -461,6 +541,57 @@ err_res: - qcom_pcie_disable_resources_v1(pcie); - } - -+static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev) -+{ -+ struct pcie_port *pp = &pcie->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->dbi + PCIE20_PLR_IATU_VIEWPORT); -+ -+ writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1); -+ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); -+ writel(pp->cfg0_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); -+ writel((pp->cfg0_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); -+ writel((pp->cfg0_mod_base + pp->cfg0_size - 1), -+ pcie->dbi + PCIE20_PLR_IATU_LAR); -+ writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR); -+ writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR); -+} -+ -+static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie) -+{ -+ struct pcie_port *pp = &pcie->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->dbi + PCIE20_PLR_IATU_VIEWPORT); -+ -+ writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1); -+ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); -+ writel(pp->mem_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); -+ writel((pp->mem_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); -+ writel(pp->mem_mod_base + pp->mem_size - 1, -+ pcie->dbi + PCIE20_PLR_IATU_LAR); -+ writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR); -+ writel(upper_32_bits(pp->mem_bus_addr), -+ pcie->dbi + PCIE20_PLR_IATU_UTAR); -+ -+ /* 256B PCIE buffer setting */ -+ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); -+ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); -+} -+ - static void qcom_pcie_host_init_v0(struct pcie_port *pp) - { - struct qcom_pcie *pcie = to_qcom_pcie(pp); -@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc - - qcom_ep_reset_assert(pcie); - -+ reset_control_assert(res->ahb_reset); -+ - ret = qcom_pcie_enable_resources_v0(pcie); - if (ret) - return; - - writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); - -- /* enable external reference clock */ -- writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); -+ /* 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); - -+ /* De-assert PHY, PCIe, POR and AXI resets */ - ret = reset_control_deassert(res->phy_reset); - if (ret) { - dev_err(dev, "cannot deassert phy reset\n"); -@@ -517,6 +667,9 @@ static void qcom_pcie_host_init_v0(struc - if (ret) - goto err; - -+ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); -+ qcom_pcie_prog_viewport_mem2_outbound(pcie); -+ - return; - err: - qcom_ep_reset_assert(pcie); diff --git a/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch deleted file mode 100644 index 9f32e8fbc2..0000000000 --- a/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch +++ /dev/null @@ -1,80 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -369,15 +369,21 @@ - - clocks = <&gcc PCIE_A_CLK>, - <&gcc PCIE_H_CLK>, -- <&gcc PCIE_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_PHY_CLK>, -+ <&gcc PCIE_AUX_CLK>, -+ <&gcc PCIE_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_ACLK_RESET>, - <&gcc PCIE_HCLK_RESET>, - <&gcc PCIE_POR_RESET>, - <&gcc PCIE_PCI_RESET>, -- <&gcc PCIE_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_PHY_RESET>, -+ <&gcc PCIE_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie0_pins>; - pinctrl-names = "default"; -@@ -415,15 +421,21 @@ - - clocks = <&gcc PCIE_1_A_CLK>, - <&gcc PCIE_1_H_CLK>, -- <&gcc PCIE_1_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_1_PHY_CLK>, -+ <&gcc PCIE_1_AUX_CLK>, -+ <&gcc PCIE_1_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_1_ACLK_RESET>, - <&gcc PCIE_1_HCLK_RESET>, - <&gcc PCIE_1_POR_RESET>, - <&gcc PCIE_1_PCI_RESET>, -- <&gcc PCIE_1_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_1_PHY_RESET>, -+ <&gcc PCIE_1_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie1_pins>; - pinctrl-names = "default"; -@@ -461,15 +473,21 @@ - - clocks = <&gcc PCIE_2_A_CLK>, - <&gcc PCIE_2_H_CLK>, -- <&gcc PCIE_2_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_2_PHY_CLK>, -+ <&gcc PCIE_2_AUX_CLK>, -+ <&gcc PCIE_2_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_2_ACLK_RESET>, - <&gcc PCIE_2_HCLK_RESET>, - <&gcc PCIE_2_POR_RESET>, - <&gcc PCIE_2_PCI_RESET>, -- <&gcc PCIE_2_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_2_PHY_RESET>, -+ <&gcc PCIE_2_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie2_pins>; - pinctrl-names = "default"; diff --git a/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch b/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch deleted file mode 100644 index d8191422c4..0000000000 --- a/target/linux/ipq806x/patches-3.18/120-mfd-qcom-rpm-Driver-for-the-Qualcomm-RPM.patch +++ /dev/null @@ -1,654 +0,0 @@ -From 58e214382bdd1eb48c5a3519182bddcb26edabad Mon Sep 17 00:00:00 2001 -From: Bjorn Andersson -Date: Wed, 26 Nov 2014 13:51:00 -0800 -Subject: [PATCH] mfd: qcom-rpm: Driver for the Qualcomm RPM - -Driver for the Resource Power Manager (RPM) found in Qualcomm 8660, 8960 -and 8064 based devices. The driver exposes resources that child drivers -can operate on; to implementing regulator, clock and bus frequency -drivers. - -Signed-off-by: Bjorn Andersson -Signed-off-by: Lee Jones ---- - drivers/mfd/Kconfig | 14 ++ - drivers/mfd/Makefile | 1 + - drivers/mfd/qcom_rpm.c | 581 +++++++++++++++++++++++++++++++++++++++++++ - include/linux/mfd/qcom_rpm.h | 13 + - 4 files changed, 609 insertions(+) - create mode 100644 drivers/mfd/qcom_rpm.c - create mode 100644 include/linux/mfd/qcom_rpm.h - ---- a/drivers/mfd/Kconfig -+++ b/drivers/mfd/Kconfig -@@ -567,6 +567,20 @@ config MFD_PM8921_CORE - Say M here if you want to include support for PM8921 chip as a module. - This will build a module called "pm8921-core". - -+config MFD_QCOM_RPM -+ tristate "Qualcomm Resource Power Manager (RPM)" -+ depends on ARCH_QCOM && OF -+ help -+ If you say yes to this option, support will be included for the -+ Resource Power Manager system found in the Qualcomm 8660, 8960 and -+ 8064 based devices. -+ -+ This is required to access many regulators, clocks and bus -+ frequencies controlled by the RPM on these devices. -+ -+ Say M here if you want to include support for the Qualcomm RPM as a -+ module. This will build a module called "qcom_rpm". -+ - config MFD_SPMI_PMIC - tristate "Qualcomm SPMI PMICs" - depends on ARCH_QCOM || COMPILE_TEST ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -153,6 +153,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x- - obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o - obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o - obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o -+obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o - obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o - obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o - obj-$(CONFIG_MFD_TPS65090) += tps65090.o ---- /dev/null -+++ b/drivers/mfd/qcom_rpm.c -@@ -0,0 +1,581 @@ -+/* -+ * Copyright (c) 2014, Sony Mobile Communications AB. -+ * Copyright (c) 2013, The Linux Foundation. All rights reserved. -+ * Author: Bjorn Andersson -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+struct qcom_rpm_resource { -+ unsigned target_id; -+ unsigned status_id; -+ unsigned select_id; -+ unsigned size; -+}; -+ -+struct qcom_rpm_data { -+ u32 version; -+ const struct qcom_rpm_resource *resource_table; -+ unsigned n_resources; -+}; -+ -+struct qcom_rpm { -+ struct device *dev; -+ struct regmap *ipc_regmap; -+ unsigned ipc_offset; -+ unsigned ipc_bit; -+ -+ struct completion ack; -+ struct mutex lock; -+ -+ void __iomem *status_regs; -+ void __iomem *ctrl_regs; -+ void __iomem *req_regs; -+ -+ u32 ack_status; -+ -+ const struct qcom_rpm_data *data; -+}; -+ -+#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4) -+#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4) -+#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4) -+ -+#define RPM_REQUEST_TIMEOUT (5 * HZ) -+ -+#define RPM_REQUEST_CONTEXT 3 -+#define RPM_REQ_SELECT 11 -+#define RPM_ACK_CONTEXT 15 -+#define RPM_ACK_SELECTOR 23 -+#define RPM_SELECT_SIZE 7 -+ -+#define RPM_NOTIFICATION BIT(30) -+#define RPM_REJECTED BIT(31) -+ -+#define RPM_SIGNAL BIT(2) -+ -+static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = { -+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, -+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, -+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, -+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, -+ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, -+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, -+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, -+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, -+ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, -+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, -+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, -+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, -+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, -+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, -+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, -+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, -+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, -+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 }, -+ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 1 }, -+ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 1 }, -+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 }, -+ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 21 }, -+ [QCOM_RPM_PM8921_SMPS1] = { 116, 31, 30, 2 }, -+ [QCOM_RPM_PM8921_SMPS2] = { 118, 33, 31, 2 }, -+ [QCOM_RPM_PM8921_SMPS3] = { 120, 35, 32, 2 }, -+ [QCOM_RPM_PM8921_SMPS4] = { 122, 37, 33, 2 }, -+ [QCOM_RPM_PM8921_SMPS5] = { 124, 39, 34, 2 }, -+ [QCOM_RPM_PM8921_SMPS6] = { 126, 41, 35, 2 }, -+ [QCOM_RPM_PM8921_SMPS7] = { 128, 43, 36, 2 }, -+ [QCOM_RPM_PM8921_SMPS8] = { 130, 45, 37, 2 }, -+ [QCOM_RPM_PM8921_LDO1] = { 132, 47, 38, 2 }, -+ [QCOM_RPM_PM8921_LDO2] = { 134, 49, 39, 2 }, -+ [QCOM_RPM_PM8921_LDO3] = { 136, 51, 40, 2 }, -+ [QCOM_RPM_PM8921_LDO4] = { 138, 53, 41, 2 }, -+ [QCOM_RPM_PM8921_LDO5] = { 140, 55, 42, 2 }, -+ [QCOM_RPM_PM8921_LDO6] = { 142, 57, 43, 2 }, -+ [QCOM_RPM_PM8921_LDO7] = { 144, 59, 44, 2 }, -+ [QCOM_RPM_PM8921_LDO8] = { 146, 61, 45, 2 }, -+ [QCOM_RPM_PM8921_LDO9] = { 148, 63, 46, 2 }, -+ [QCOM_RPM_PM8921_LDO10] = { 150, 65, 47, 2 }, -+ [QCOM_RPM_PM8921_LDO11] = { 152, 67, 48, 2 }, -+ [QCOM_RPM_PM8921_LDO12] = { 154, 69, 49, 2 }, -+ [QCOM_RPM_PM8921_LDO13] = { 156, 71, 50, 2 }, -+ [QCOM_RPM_PM8921_LDO14] = { 158, 73, 51, 2 }, -+ [QCOM_RPM_PM8921_LDO15] = { 160, 75, 52, 2 }, -+ [QCOM_RPM_PM8921_LDO16] = { 162, 77, 53, 2 }, -+ [QCOM_RPM_PM8921_LDO17] = { 164, 79, 54, 2 }, -+ [QCOM_RPM_PM8921_LDO18] = { 166, 81, 55, 2 }, -+ [QCOM_RPM_PM8921_LDO19] = { 168, 83, 56, 2 }, -+ [QCOM_RPM_PM8921_LDO20] = { 170, 85, 57, 2 }, -+ [QCOM_RPM_PM8921_LDO21] = { 172, 87, 58, 2 }, -+ [QCOM_RPM_PM8921_LDO22] = { 174, 89, 59, 2 }, -+ [QCOM_RPM_PM8921_LDO23] = { 176, 91, 60, 2 }, -+ [QCOM_RPM_PM8921_LDO24] = { 178, 93, 61, 2 }, -+ [QCOM_RPM_PM8921_LDO25] = { 180, 95, 62, 2 }, -+ [QCOM_RPM_PM8921_LDO26] = { 182, 97, 63, 2 }, -+ [QCOM_RPM_PM8921_LDO27] = { 184, 99, 64, 2 }, -+ [QCOM_RPM_PM8921_LDO28] = { 186, 101, 65, 2 }, -+ [QCOM_RPM_PM8921_LDO29] = { 188, 103, 66, 2 }, -+ [QCOM_RPM_PM8921_CLK1] = { 190, 105, 67, 2 }, -+ [QCOM_RPM_PM8921_CLK2] = { 192, 107, 68, 2 }, -+ [QCOM_RPM_PM8921_LVS1] = { 194, 109, 69, 1 }, -+ [QCOM_RPM_PM8921_LVS2] = { 195, 110, 70, 1 }, -+ [QCOM_RPM_PM8921_LVS3] = { 196, 111, 71, 1 }, -+ [QCOM_RPM_PM8921_LVS4] = { 197, 112, 72, 1 }, -+ [QCOM_RPM_PM8921_LVS5] = { 198, 113, 73, 1 }, -+ [QCOM_RPM_PM8921_LVS6] = { 199, 114, 74, 1 }, -+ [QCOM_RPM_PM8921_LVS7] = { 200, 115, 75, 1 }, -+ [QCOM_RPM_PM8821_SMPS1] = { 201, 116, 76, 2 }, -+ [QCOM_RPM_PM8821_SMPS2] = { 203, 118, 77, 2 }, -+ [QCOM_RPM_PM8821_LDO1] = { 205, 120, 78, 2 }, -+ [QCOM_RPM_PM8921_NCP] = { 207, 122, 80, 2 }, -+ [QCOM_RPM_CXO_BUFFERS] = { 209, 124, 81, 1 }, -+ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 125, 82, 1 }, -+ [QCOM_RPM_HDMI_SWITCH] = { 211, 126, 83, 1 }, -+ [QCOM_RPM_DDR_DMM] = { 212, 127, 84, 2 }, -+ [QCOM_RPM_VDDMIN_GPIO] = { 215, 131, 89, 1 }, -+}; -+ -+static const struct qcom_rpm_data apq8064_template = { -+ .version = 3, -+ .resource_table = apq8064_rpm_resource_table, -+ .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table), -+}; -+ -+static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = { -+ [QCOM_RPM_CXO_CLK] = { 32, 12, 5, 1 }, -+ [QCOM_RPM_PXO_CLK] = { 33, 13, 6, 1 }, -+ [QCOM_RPM_PLL_4] = { 34, 14, 7, 1 }, -+ [QCOM_RPM_APPS_FABRIC_CLK] = { 35, 15, 8, 1 }, -+ [QCOM_RPM_SYS_FABRIC_CLK] = { 36, 16, 9, 1 }, -+ [QCOM_RPM_MM_FABRIC_CLK] = { 37, 17, 10, 1 }, -+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 38, 18, 11, 1 }, -+ [QCOM_RPM_SFPB_CLK] = { 39, 19, 12, 1 }, -+ [QCOM_RPM_CFPB_CLK] = { 40, 20, 13, 1 }, -+ [QCOM_RPM_MMFPB_CLK] = { 41, 21, 14, 1 }, -+ [QCOM_RPM_SMI_CLK] = { 42, 22, 15, 1 }, -+ [QCOM_RPM_EBI1_CLK] = { 43, 23, 16, 1 }, -+ [QCOM_RPM_APPS_L2_CACHE_CTL] = { 44, 24, 17, 1 }, -+ [QCOM_RPM_APPS_FABRIC_HALT] = { 45, 25, 18, 2 }, -+ [QCOM_RPM_APPS_FABRIC_MODE] = { 47, 26, 19, 3 }, -+ [QCOM_RPM_APPS_FABRIC_ARB] = { 51, 28, 21, 6 }, -+ [QCOM_RPM_SYS_FABRIC_HALT] = { 63, 29, 22, 2 }, -+ [QCOM_RPM_SYS_FABRIC_MODE] = { 65, 30, 23, 3 }, -+ [QCOM_RPM_SYS_FABRIC_ARB] = { 69, 32, 25, 22 }, -+ [QCOM_RPM_MM_FABRIC_HALT] = { 105, 33, 26, 2 }, -+ [QCOM_RPM_MM_FABRIC_MODE] = { 107, 34, 27, 3 }, -+ [QCOM_RPM_MM_FABRIC_ARB] = { 111, 36, 29, 23 }, -+ [QCOM_RPM_PM8901_SMPS0] = { 134, 37, 30, 2 }, -+ [QCOM_RPM_PM8901_SMPS1] = { 136, 39, 31, 2 }, -+ [QCOM_RPM_PM8901_SMPS2] = { 138, 41, 32, 2 }, -+ [QCOM_RPM_PM8901_SMPS3] = { 140, 43, 33, 2 }, -+ [QCOM_RPM_PM8901_SMPS4] = { 142, 45, 34, 2 }, -+ [QCOM_RPM_PM8901_LDO0] = { 144, 47, 35, 2 }, -+ [QCOM_RPM_PM8901_LDO1] = { 146, 49, 36, 2 }, -+ [QCOM_RPM_PM8901_LDO2] = { 148, 51, 37, 2 }, -+ [QCOM_RPM_PM8901_LDO3] = { 150, 53, 38, 2 }, -+ [QCOM_RPM_PM8901_LDO4] = { 152, 55, 39, 2 }, -+ [QCOM_RPM_PM8901_LDO5] = { 154, 57, 40, 2 }, -+ [QCOM_RPM_PM8901_LDO6] = { 156, 59, 41, 2 }, -+ [QCOM_RPM_PM8901_LVS0] = { 158, 61, 42, 1 }, -+ [QCOM_RPM_PM8901_LVS1] = { 159, 62, 43, 1 }, -+ [QCOM_RPM_PM8901_LVS2] = { 160, 63, 44, 1 }, -+ [QCOM_RPM_PM8901_LVS3] = { 161, 64, 45, 1 }, -+ [QCOM_RPM_PM8901_MVS] = { 162, 65, 46, 1 }, -+ [QCOM_RPM_PM8058_SMPS0] = { 163, 66, 47, 2 }, -+ [QCOM_RPM_PM8058_SMPS1] = { 165, 68, 48, 2 }, -+ [QCOM_RPM_PM8058_SMPS2] = { 167, 70, 49, 2 }, -+ [QCOM_RPM_PM8058_SMPS3] = { 169, 72, 50, 2 }, -+ [QCOM_RPM_PM8058_SMPS4] = { 171, 74, 51, 2 }, -+ [QCOM_RPM_PM8058_LDO0] = { 173, 76, 52, 2 }, -+ [QCOM_RPM_PM8058_LDO1] = { 175, 78, 53, 2 }, -+ [QCOM_RPM_PM8058_LDO2] = { 177, 80, 54, 2 }, -+ [QCOM_RPM_PM8058_LDO3] = { 179, 82, 55, 2 }, -+ [QCOM_RPM_PM8058_LDO4] = { 181, 84, 56, 2 }, -+ [QCOM_RPM_PM8058_LDO5] = { 183, 86, 57, 2 }, -+ [QCOM_RPM_PM8058_LDO6] = { 185, 88, 58, 2 }, -+ [QCOM_RPM_PM8058_LDO7] = { 187, 90, 59, 2 }, -+ [QCOM_RPM_PM8058_LDO8] = { 189, 92, 60, 2 }, -+ [QCOM_RPM_PM8058_LDO9] = { 191, 94, 61, 2 }, -+ [QCOM_RPM_PM8058_LDO10] = { 193, 96, 62, 2 }, -+ [QCOM_RPM_PM8058_LDO11] = { 195, 98, 63, 2 }, -+ [QCOM_RPM_PM8058_LDO12] = { 197, 100, 64, 2 }, -+ [QCOM_RPM_PM8058_LDO13] = { 199, 102, 65, 2 }, -+ [QCOM_RPM_PM8058_LDO14] = { 201, 104, 66, 2 }, -+ [QCOM_RPM_PM8058_LDO15] = { 203, 106, 67, 2 }, -+ [QCOM_RPM_PM8058_LDO16] = { 205, 108, 68, 2 }, -+ [QCOM_RPM_PM8058_LDO17] = { 207, 110, 69, 2 }, -+ [QCOM_RPM_PM8058_LDO18] = { 209, 112, 70, 2 }, -+ [QCOM_RPM_PM8058_LDO19] = { 211, 114, 71, 2 }, -+ [QCOM_RPM_PM8058_LDO20] = { 213, 116, 72, 2 }, -+ [QCOM_RPM_PM8058_LDO21] = { 215, 118, 73, 2 }, -+ [QCOM_RPM_PM8058_LDO22] = { 217, 120, 74, 2 }, -+ [QCOM_RPM_PM8058_LDO23] = { 219, 122, 75, 2 }, -+ [QCOM_RPM_PM8058_LDO24] = { 221, 124, 76, 2 }, -+ [QCOM_RPM_PM8058_LDO25] = { 223, 126, 77, 2 }, -+ [QCOM_RPM_PM8058_LVS0] = { 225, 128, 78, 1 }, -+ [QCOM_RPM_PM8058_LVS1] = { 226, 129, 79, 1 }, -+ [QCOM_RPM_PM8058_NCP] = { 227, 130, 80, 2 }, -+ [QCOM_RPM_CXO_BUFFERS] = { 229, 132, 81, 1 }, -+}; -+ -+static const struct qcom_rpm_data msm8660_template = { -+ .version = 2, -+ .resource_table = msm8660_rpm_resource_table, -+ .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table), -+}; -+ -+static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = { -+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, -+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, -+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, -+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, -+ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, -+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, -+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, -+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, -+ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, -+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, -+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, -+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, -+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, -+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, -+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, -+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, -+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, -+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 29 }, -+ [QCOM_RPM_MM_FABRIC_HALT] = { 88, 27, 26, 1 }, -+ [QCOM_RPM_MM_FABRIC_MODE] = { 90, 28, 27, 1 }, -+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 93, 29, 28, 1 }, -+ [QCOM_RPM_MM_FABRIC_ARB] = { 94, 30, 29, 23 }, -+ [QCOM_RPM_PM8921_SMPS1] = { 117, 31, 30, 2 }, -+ [QCOM_RPM_PM8921_SMPS2] = { 119, 33, 31, 2 }, -+ [QCOM_RPM_PM8921_SMPS3] = { 121, 35, 32, 2 }, -+ [QCOM_RPM_PM8921_SMPS4] = { 123, 37, 33, 2 }, -+ [QCOM_RPM_PM8921_SMPS5] = { 125, 39, 34, 2 }, -+ [QCOM_RPM_PM8921_SMPS6] = { 127, 41, 35, 2 }, -+ [QCOM_RPM_PM8921_SMPS7] = { 129, 43, 36, 2 }, -+ [QCOM_RPM_PM8921_SMPS8] = { 131, 45, 37, 2 }, -+ [QCOM_RPM_PM8921_LDO1] = { 133, 47, 38, 2 }, -+ [QCOM_RPM_PM8921_LDO2] = { 135, 49, 39, 2 }, -+ [QCOM_RPM_PM8921_LDO3] = { 137, 51, 40, 2 }, -+ [QCOM_RPM_PM8921_LDO4] = { 139, 53, 41, 2 }, -+ [QCOM_RPM_PM8921_LDO5] = { 141, 55, 42, 2 }, -+ [QCOM_RPM_PM8921_LDO6] = { 143, 57, 43, 2 }, -+ [QCOM_RPM_PM8921_LDO7] = { 145, 59, 44, 2 }, -+ [QCOM_RPM_PM8921_LDO8] = { 147, 61, 45, 2 }, -+ [QCOM_RPM_PM8921_LDO9] = { 149, 63, 46, 2 }, -+ [QCOM_RPM_PM8921_LDO10] = { 151, 65, 47, 2 }, -+ [QCOM_RPM_PM8921_LDO11] = { 153, 67, 48, 2 }, -+ [QCOM_RPM_PM8921_LDO12] = { 155, 69, 49, 2 }, -+ [QCOM_RPM_PM8921_LDO13] = { 157, 71, 50, 2 }, -+ [QCOM_RPM_PM8921_LDO14] = { 159, 73, 51, 2 }, -+ [QCOM_RPM_PM8921_LDO15] = { 161, 75, 52, 2 }, -+ [QCOM_RPM_PM8921_LDO16] = { 163, 77, 53, 2 }, -+ [QCOM_RPM_PM8921_LDO17] = { 165, 79, 54, 2 }, -+ [QCOM_RPM_PM8921_LDO18] = { 167, 81, 55, 2 }, -+ [QCOM_RPM_PM8921_LDO19] = { 169, 83, 56, 2 }, -+ [QCOM_RPM_PM8921_LDO20] = { 171, 85, 57, 2 }, -+ [QCOM_RPM_PM8921_LDO21] = { 173, 87, 58, 2 }, -+ [QCOM_RPM_PM8921_LDO22] = { 175, 89, 59, 2 }, -+ [QCOM_RPM_PM8921_LDO23] = { 177, 91, 60, 2 }, -+ [QCOM_RPM_PM8921_LDO24] = { 179, 93, 61, 2 }, -+ [QCOM_RPM_PM8921_LDO25] = { 181, 95, 62, 2 }, -+ [QCOM_RPM_PM8921_LDO26] = { 183, 97, 63, 2 }, -+ [QCOM_RPM_PM8921_LDO27] = { 185, 99, 64, 2 }, -+ [QCOM_RPM_PM8921_LDO28] = { 187, 101, 65, 2 }, -+ [QCOM_RPM_PM8921_LDO29] = { 189, 103, 66, 2 }, -+ [QCOM_RPM_PM8921_CLK1] = { 191, 105, 67, 2 }, -+ [QCOM_RPM_PM8921_CLK2] = { 193, 107, 68, 2 }, -+ [QCOM_RPM_PM8921_LVS1] = { 195, 109, 69, 1 }, -+ [QCOM_RPM_PM8921_LVS2] = { 196, 110, 70, 1 }, -+ [QCOM_RPM_PM8921_LVS3] = { 197, 111, 71, 1 }, -+ [QCOM_RPM_PM8921_LVS4] = { 198, 112, 72, 1 }, -+ [QCOM_RPM_PM8921_LVS5] = { 199, 113, 73, 1 }, -+ [QCOM_RPM_PM8921_LVS6] = { 200, 114, 74, 1 }, -+ [QCOM_RPM_PM8921_LVS7] = { 201, 115, 75, 1 }, -+ [QCOM_RPM_PM8921_NCP] = { 202, 116, 80, 2 }, -+ [QCOM_RPM_CXO_BUFFERS] = { 204, 118, 81, 1 }, -+ [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 }, -+ [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 }, -+ [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 }, -+}; -+ -+static const struct qcom_rpm_data msm8960_template = { -+ .version = 3, -+ .resource_table = msm8960_rpm_resource_table, -+ .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table), -+}; -+ -+static const struct of_device_id qcom_rpm_of_match[] = { -+ { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template }, -+ { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template }, -+ { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, qcom_rpm_of_match); -+ -+int qcom_rpm_write(struct qcom_rpm *rpm, -+ int state, -+ int resource, -+ u32 *buf, size_t count) -+{ -+ const struct qcom_rpm_resource *res; -+ const struct qcom_rpm_data *data = rpm->data; -+ u32 sel_mask[RPM_SELECT_SIZE] = { 0 }; -+ int left; -+ int ret = 0; -+ int i; -+ -+ if (WARN_ON(resource < 0 || resource >= data->n_resources)) -+ return -EINVAL; -+ -+ res = &data->resource_table[resource]; -+ if (WARN_ON(res->size != count)) -+ return -EINVAL; -+ -+ mutex_lock(&rpm->lock); -+ -+ for (i = 0; i < res->size; i++) -+ writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i)); -+ -+ bitmap_set((unsigned long *)sel_mask, res->select_id, 1); -+ for (i = 0; i < ARRAY_SIZE(sel_mask); i++) { -+ writel_relaxed(sel_mask[i], -+ RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i)); -+ } -+ -+ writel_relaxed(BIT(state), RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT)); -+ -+ reinit_completion(&rpm->ack); -+ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit)); -+ -+ left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT); -+ if (!left) -+ ret = -ETIMEDOUT; -+ else if (rpm->ack_status & RPM_REJECTED) -+ ret = -EIO; -+ -+ mutex_unlock(&rpm->lock); -+ -+ return ret; -+} -+EXPORT_SYMBOL(qcom_rpm_write); -+ -+static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev) -+{ -+ struct qcom_rpm *rpm = dev; -+ u32 ack; -+ int i; -+ -+ ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); -+ for (i = 0; i < RPM_SELECT_SIZE; i++) -+ writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i)); -+ writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); -+ -+ if (ack & RPM_NOTIFICATION) { -+ dev_warn(rpm->dev, "ignoring notification!\n"); -+ } else { -+ rpm->ack_status = ack; -+ complete(&rpm->ack); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev) -+{ -+ struct qcom_rpm *rpm = dev; -+ -+ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit)); -+ dev_err(rpm->dev, "RPM triggered fatal error\n"); -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev) -+{ -+ return IRQ_HANDLED; -+} -+ -+static int qcom_rpm_probe(struct platform_device *pdev) -+{ -+ const struct of_device_id *match; -+ struct device_node *syscon_np; -+ struct resource *res; -+ struct qcom_rpm *rpm; -+ u32 fw_version[3]; -+ int irq_wakeup; -+ int irq_ack; -+ int irq_err; -+ int ret; -+ -+ rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); -+ if (!rpm) -+ return -ENOMEM; -+ -+ rpm->dev = &pdev->dev; -+ mutex_init(&rpm->lock); -+ init_completion(&rpm->ack); -+ -+ irq_ack = platform_get_irq_byname(pdev, "ack"); -+ if (irq_ack < 0) { -+ dev_err(&pdev->dev, "required ack interrupt missing\n"); -+ return irq_ack; -+ } -+ -+ irq_err = platform_get_irq_byname(pdev, "err"); -+ if (irq_err < 0) { -+ dev_err(&pdev->dev, "required err interrupt missing\n"); -+ return irq_err; -+ } -+ -+ irq_wakeup = platform_get_irq_byname(pdev, "wakeup"); -+ if (irq_wakeup < 0) { -+ dev_err(&pdev->dev, "required wakeup interrupt missing\n"); -+ return irq_wakeup; -+ } -+ -+ match = of_match_device(qcom_rpm_of_match, &pdev->dev); -+ rpm->data = match->data; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ rpm->status_regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(rpm->status_regs)) -+ return PTR_ERR(rpm->status_regs); -+ rpm->ctrl_regs = rpm->status_regs + 0x400; -+ rpm->req_regs = rpm->status_regs + 0x600; -+ -+ syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0); -+ if (!syscon_np) { -+ dev_err(&pdev->dev, "no qcom,ipc node\n"); -+ return -ENODEV; -+ } -+ -+ rpm->ipc_regmap = syscon_node_to_regmap(syscon_np); -+ if (IS_ERR(rpm->ipc_regmap)) -+ return PTR_ERR(rpm->ipc_regmap); -+ -+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1, -+ &rpm->ipc_offset); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "no offset in qcom,ipc\n"); -+ return -EINVAL; -+ } -+ -+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2, -+ &rpm->ipc_bit); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "no bit in qcom,ipc\n"); -+ return -EINVAL; -+ } -+ -+ dev_set_drvdata(&pdev->dev, rpm); -+ -+ fw_version[0] = readl(RPM_STATUS_REG(rpm, 0)); -+ fw_version[1] = readl(RPM_STATUS_REG(rpm, 1)); -+ fw_version[2] = readl(RPM_STATUS_REG(rpm, 2)); -+ if (fw_version[0] != rpm->data->version) { -+ dev_err(&pdev->dev, -+ "RPM version %u.%u.%u incompatible with driver version %u", -+ fw_version[0], -+ fw_version[1], -+ fw_version[2], -+ rpm->data->version); -+ return -EFAULT; -+ } -+ -+ dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0], -+ fw_version[1], -+ fw_version[2]); -+ -+ ret = devm_request_irq(&pdev->dev, -+ irq_ack, -+ qcom_rpm_ack_interrupt, -+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, -+ "qcom_rpm_ack", -+ rpm); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to request ack interrupt\n"); -+ return ret; -+ } -+ -+ ret = irq_set_irq_wake(irq_ack, 1); -+ if (ret) -+ dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n"); -+ -+ ret = devm_request_irq(&pdev->dev, -+ irq_err, -+ qcom_rpm_err_interrupt, -+ IRQF_TRIGGER_RISING, -+ "qcom_rpm_err", -+ rpm); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to request err interrupt\n"); -+ return ret; -+ } -+ -+ ret = devm_request_irq(&pdev->dev, -+ irq_wakeup, -+ qcom_rpm_wakeup_interrupt, -+ IRQF_TRIGGER_RISING, -+ "qcom_rpm_wakeup", -+ rpm); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to request wakeup interrupt\n"); -+ return ret; -+ } -+ -+ ret = irq_set_irq_wake(irq_wakeup, 1); -+ if (ret) -+ dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n"); -+ -+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); -+} -+ -+static int qcom_rpm_remove(struct platform_device *pdev) -+{ -+ of_platform_depopulate(&pdev->dev); -+ return 0; -+} -+ -+static struct platform_driver qcom_rpm_driver = { -+ .probe = qcom_rpm_probe, -+ .remove = qcom_rpm_remove, -+ .driver = { -+ .name = "qcom_rpm", -+ .of_match_table = qcom_rpm_of_match, -+ }, -+}; -+ -+static int __init qcom_rpm_init(void) -+{ -+ return platform_driver_register(&qcom_rpm_driver); -+} -+arch_initcall(qcom_rpm_init); -+ -+static void __exit qcom_rpm_exit(void) -+{ -+ platform_driver_unregister(&qcom_rpm_driver); -+} -+module_exit(qcom_rpm_exit) -+ -+MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Bjorn Andersson "); ---- /dev/null -+++ b/include/linux/mfd/qcom_rpm.h -@@ -0,0 +1,13 @@ -+#ifndef __QCOM_RPM_H__ -+#define __QCOM_RPM_H__ -+ -+#include -+ -+struct qcom_rpm; -+ -+#define QCOM_RPM_ACTIVE_STATE 0 -+#define QCOM_RPM_SLEEP_STATE 1 -+ -+int qcom_rpm_write(struct qcom_rpm *rpm, int state, int resource, u32 *buf, size_t count); -+ -+#endif diff --git a/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch b/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch deleted file mode 100644 index e5e9e4e6cc..0000000000 --- a/target/linux/ipq806x/patches-3.18/121-mfd-qcom_rpm-Add-support-for-IPQ8064.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 4d54b0adfa67476e6509bc8646b9dbac642e8a29 Mon Sep 17 00:00:00 2001 -From: Josh Cartwright -Date: Thu, 26 Mar 2015 11:29:26 -0700 -Subject: [PATCH] mfd: qcom_rpm: Add support for IPQ8064 - -The IPQ8064 also includes an RPM following the same message structure as -other chips. In addition, it supports a few new resource types to -support the NSS fabric clocks and the SMB208/SMB209 regulators found on -the reference boards. - -Signed-off-by: Josh Cartwright -Signed-off-by: Stephen Boyd -Signed-off-by: Lee Jones ---- - drivers/mfd/qcom_rpm.c | 41 +++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 41 insertions(+) - ---- a/drivers/mfd/qcom_rpm.c -+++ b/drivers/mfd/qcom_rpm.c -@@ -323,10 +323,51 @@ static const struct qcom_rpm_data msm896 - .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table), - }; - -+static const struct qcom_rpm_resource ipq806x_rpm_resource_table[] = { -+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, -+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, -+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, -+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, -+ [QCOM_RPM_NSS_FABRIC_0_CLK] = { 29, 13, 10, 1 }, -+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, -+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, -+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, -+ [QCOM_RPM_NSS_FABRIC_1_CLK] = { 33, 17, 14, 1 }, -+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, -+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 2 }, -+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 3 }, -+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, -+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, -+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 2 }, -+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 3 }, -+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, -+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 }, -+ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 2 }, -+ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 3 }, -+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 }, -+ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 2 }, -+ [QCOM_RPM_CXO_BUFFERS] = { 209, 33, 31, 1 }, -+ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 34, 32, 1 }, -+ [QCOM_RPM_HDMI_SWITCH] = { 211, 35, 33, 1 }, -+ [QCOM_RPM_DDR_DMM] = { 212, 36, 34, 2 }, -+ [QCOM_RPM_VDDMIN_GPIO] = { 215, 40, 39, 1 }, -+ [QCOM_RPM_SMB208_S1a] = { 216, 41, 90, 2 }, -+ [QCOM_RPM_SMB208_S1b] = { 218, 43, 91, 2 }, -+ [QCOM_RPM_SMB208_S2a] = { 220, 45, 92, 2 }, -+ [QCOM_RPM_SMB208_S2b] = { 222, 47, 93, 2 }, -+}; -+ -+static const struct qcom_rpm_data ipq806x_template = { -+ .version = 3, -+ .resource_table = ipq806x_rpm_resource_table, -+ .n_resources = ARRAY_SIZE(ipq806x_rpm_resource_table), -+}; -+ - static const struct of_device_id qcom_rpm_of_match[] = { - { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template }, - { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template }, - { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template }, -+ { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template }, - { } - }; - MODULE_DEVICE_TABLE(of, qcom_rpm_of_match); diff --git a/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch b/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch deleted file mode 100644 index a65d2c5131..0000000000 --- a/target/linux/ipq806x/patches-3.18/122-mfd-devicetree-bindings-Add-Qualcomm-RPM-DT-binding.patch +++ /dev/null @@ -1,247 +0,0 @@ -From aa0c4b815045420ea54d5ae5362f5a0190609d46 Mon Sep 17 00:00:00 2001 -From: Bjorn Andersson -Date: Wed, 26 Nov 2014 13:50:59 -0800 -Subject: [PATCH] mfd: devicetree: bindings: Add Qualcomm RPM DT binding - -Add binding for the Qualcomm Resource Power Manager (RPM) found in 8660, -8960 and 8064 based devices. - -Signed-off-by: Bjorn Andersson -Signed-off-by: Lee Jones ---- - Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 70 ++++++++++ - include/dt-bindings/mfd/qcom-rpm.h | 154 +++++++++++++++++++++ - 2 files changed, 224 insertions(+) - create mode 100644 Documentation/devicetree/bindings/mfd/qcom-rpm.txt - create mode 100644 include/dt-bindings/mfd/qcom-rpm.h - ---- /dev/null -+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt -@@ -0,0 +1,70 @@ -+Qualcomm Resource Power Manager (RPM) -+ -+This driver is used to interface with the Resource Power Manager (RPM) found in -+various Qualcomm platforms. The RPM allows each component in the system to vote -+for state of the system resources, such as clocks, regulators and bus -+frequencies. -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: must be one of: -+ "qcom,rpm-apq8064" -+ "qcom,rpm-msm8660" -+ "qcom,rpm-msm8960" -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: base address and size of the RPM's message ram -+ -+- interrupts: -+ Usage: required -+ Value type: -+ Definition: three entries specifying the RPM's: -+ 1. acknowledgement interrupt -+ 2. error interrupt -+ 3. wakeup interrupt -+ -+- interrupt-names: -+ Usage: required -+ Value type: -+ Definition: must be the three strings "ack", "err" and "wakeup", in order -+ -+- #address-cells: -+ Usage: required -+ Value type: -+ Definition: must be 1 -+ -+- #size-cells: -+ Usage: required -+ Value type: -+ Definition: must be 0 -+ -+- qcom,ipc: -+ Usage: required -+ Value type: -+ -+ Definition: three entries specifying the outgoing ipc bit used for -+ signaling the RPM: -+ - phandle to a syscon node representing the apcs registers -+ - u32 representing offset to the register within the syscon -+ - u32 representing the ipc bit within the register -+ -+ -+= EXAMPLE -+ -+ #include -+ -+ rpm@108000 { -+ compatible = "qcom,rpm-msm8960"; -+ reg = <0x108000 0x1000>; -+ qcom,ipc = <&apcs 0x8 2>; -+ -+ interrupts = <0 19 0>, <0 21 0>, <0 22 0>; -+ interrupt-names = "ack", "err", "wakeup"; -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ ---- /dev/null -+++ b/include/dt-bindings/mfd/qcom-rpm.h -@@ -0,0 +1,154 @@ -+/* -+ * This header provides constants for the Qualcomm RPM bindings. -+ */ -+ -+#ifndef _DT_BINDINGS_MFD_QCOM_RPM_H -+#define _DT_BINDINGS_MFD_QCOM_RPM_H -+ -+/* -+ * Constants use to identify individual resources in the RPM. -+ */ -+#define QCOM_RPM_APPS_FABRIC_ARB 1 -+#define QCOM_RPM_APPS_FABRIC_CLK 2 -+#define QCOM_RPM_APPS_FABRIC_HALT 3 -+#define QCOM_RPM_APPS_FABRIC_IOCTL 4 -+#define QCOM_RPM_APPS_FABRIC_MODE 5 -+#define QCOM_RPM_APPS_L2_CACHE_CTL 6 -+#define QCOM_RPM_CFPB_CLK 7 -+#define QCOM_RPM_CXO_BUFFERS 8 -+#define QCOM_RPM_CXO_CLK 9 -+#define QCOM_RPM_DAYTONA_FABRIC_CLK 10 -+#define QCOM_RPM_DDR_DMM 11 -+#define QCOM_RPM_EBI1_CLK 12 -+#define QCOM_RPM_HDMI_SWITCH 13 -+#define QCOM_RPM_MMFPB_CLK 14 -+#define QCOM_RPM_MM_FABRIC_ARB 15 -+#define QCOM_RPM_MM_FABRIC_CLK 16 -+#define QCOM_RPM_MM_FABRIC_HALT 17 -+#define QCOM_RPM_MM_FABRIC_IOCTL 18 -+#define QCOM_RPM_MM_FABRIC_MODE 19 -+#define QCOM_RPM_PLL_4 20 -+#define QCOM_RPM_PM8058_LDO0 21 -+#define QCOM_RPM_PM8058_LDO1 22 -+#define QCOM_RPM_PM8058_LDO2 23 -+#define QCOM_RPM_PM8058_LDO3 24 -+#define QCOM_RPM_PM8058_LDO4 25 -+#define QCOM_RPM_PM8058_LDO5 26 -+#define QCOM_RPM_PM8058_LDO6 27 -+#define QCOM_RPM_PM8058_LDO7 28 -+#define QCOM_RPM_PM8058_LDO8 29 -+#define QCOM_RPM_PM8058_LDO9 30 -+#define QCOM_RPM_PM8058_LDO10 31 -+#define QCOM_RPM_PM8058_LDO11 32 -+#define QCOM_RPM_PM8058_LDO12 33 -+#define QCOM_RPM_PM8058_LDO13 34 -+#define QCOM_RPM_PM8058_LDO14 35 -+#define QCOM_RPM_PM8058_LDO15 36 -+#define QCOM_RPM_PM8058_LDO16 37 -+#define QCOM_RPM_PM8058_LDO17 38 -+#define QCOM_RPM_PM8058_LDO18 39 -+#define QCOM_RPM_PM8058_LDO19 40 -+#define QCOM_RPM_PM8058_LDO20 41 -+#define QCOM_RPM_PM8058_LDO21 42 -+#define QCOM_RPM_PM8058_LDO22 43 -+#define QCOM_RPM_PM8058_LDO23 44 -+#define QCOM_RPM_PM8058_LDO24 45 -+#define QCOM_RPM_PM8058_LDO25 46 -+#define QCOM_RPM_PM8058_LVS0 47 -+#define QCOM_RPM_PM8058_LVS1 48 -+#define QCOM_RPM_PM8058_NCP 49 -+#define QCOM_RPM_PM8058_SMPS0 50 -+#define QCOM_RPM_PM8058_SMPS1 51 -+#define QCOM_RPM_PM8058_SMPS2 52 -+#define QCOM_RPM_PM8058_SMPS3 53 -+#define QCOM_RPM_PM8058_SMPS4 54 -+#define QCOM_RPM_PM8821_LDO1 55 -+#define QCOM_RPM_PM8821_SMPS1 56 -+#define QCOM_RPM_PM8821_SMPS2 57 -+#define QCOM_RPM_PM8901_LDO0 58 -+#define QCOM_RPM_PM8901_LDO1 59 -+#define QCOM_RPM_PM8901_LDO2 60 -+#define QCOM_RPM_PM8901_LDO3 61 -+#define QCOM_RPM_PM8901_LDO4 62 -+#define QCOM_RPM_PM8901_LDO5 63 -+#define QCOM_RPM_PM8901_LDO6 64 -+#define QCOM_RPM_PM8901_LVS0 65 -+#define QCOM_RPM_PM8901_LVS1 66 -+#define QCOM_RPM_PM8901_LVS2 67 -+#define QCOM_RPM_PM8901_LVS3 68 -+#define QCOM_RPM_PM8901_MVS 69 -+#define QCOM_RPM_PM8901_SMPS0 70 -+#define QCOM_RPM_PM8901_SMPS1 71 -+#define QCOM_RPM_PM8901_SMPS2 72 -+#define QCOM_RPM_PM8901_SMPS3 73 -+#define QCOM_RPM_PM8901_SMPS4 74 -+#define QCOM_RPM_PM8921_CLK1 75 -+#define QCOM_RPM_PM8921_CLK2 76 -+#define QCOM_RPM_PM8921_LDO1 77 -+#define QCOM_RPM_PM8921_LDO2 78 -+#define QCOM_RPM_PM8921_LDO3 79 -+#define QCOM_RPM_PM8921_LDO4 80 -+#define QCOM_RPM_PM8921_LDO5 81 -+#define QCOM_RPM_PM8921_LDO6 82 -+#define QCOM_RPM_PM8921_LDO7 83 -+#define QCOM_RPM_PM8921_LDO8 84 -+#define QCOM_RPM_PM8921_LDO9 85 -+#define QCOM_RPM_PM8921_LDO10 86 -+#define QCOM_RPM_PM8921_LDO11 87 -+#define QCOM_RPM_PM8921_LDO12 88 -+#define QCOM_RPM_PM8921_LDO13 89 -+#define QCOM_RPM_PM8921_LDO14 90 -+#define QCOM_RPM_PM8921_LDO15 91 -+#define QCOM_RPM_PM8921_LDO16 92 -+#define QCOM_RPM_PM8921_LDO17 93 -+#define QCOM_RPM_PM8921_LDO18 94 -+#define QCOM_RPM_PM8921_LDO19 95 -+#define QCOM_RPM_PM8921_LDO20 96 -+#define QCOM_RPM_PM8921_LDO21 97 -+#define QCOM_RPM_PM8921_LDO22 98 -+#define QCOM_RPM_PM8921_LDO23 99 -+#define QCOM_RPM_PM8921_LDO24 100 -+#define QCOM_RPM_PM8921_LDO25 101 -+#define QCOM_RPM_PM8921_LDO26 102 -+#define QCOM_RPM_PM8921_LDO27 103 -+#define QCOM_RPM_PM8921_LDO28 104 -+#define QCOM_RPM_PM8921_LDO29 105 -+#define QCOM_RPM_PM8921_LVS1 106 -+#define QCOM_RPM_PM8921_LVS2 107 -+#define QCOM_RPM_PM8921_LVS3 108 -+#define QCOM_RPM_PM8921_LVS4 109 -+#define QCOM_RPM_PM8921_LVS5 110 -+#define QCOM_RPM_PM8921_LVS6 111 -+#define QCOM_RPM_PM8921_LVS7 112 -+#define QCOM_RPM_PM8921_MVS 113 -+#define QCOM_RPM_PM8921_NCP 114 -+#define QCOM_RPM_PM8921_SMPS1 115 -+#define QCOM_RPM_PM8921_SMPS2 116 -+#define QCOM_RPM_PM8921_SMPS3 117 -+#define QCOM_RPM_PM8921_SMPS4 118 -+#define QCOM_RPM_PM8921_SMPS5 119 -+#define QCOM_RPM_PM8921_SMPS6 120 -+#define QCOM_RPM_PM8921_SMPS7 121 -+#define QCOM_RPM_PM8921_SMPS8 122 -+#define QCOM_RPM_PXO_CLK 123 -+#define QCOM_RPM_QDSS_CLK 124 -+#define QCOM_RPM_SFPB_CLK 125 -+#define QCOM_RPM_SMI_CLK 126 -+#define QCOM_RPM_SYS_FABRIC_ARB 127 -+#define QCOM_RPM_SYS_FABRIC_CLK 128 -+#define QCOM_RPM_SYS_FABRIC_HALT 129 -+#define QCOM_RPM_SYS_FABRIC_IOCTL 130 -+#define QCOM_RPM_SYS_FABRIC_MODE 131 -+#define QCOM_RPM_USB_OTG_SWITCH 132 -+#define QCOM_RPM_VDDMIN_GPIO 133 -+ -+/* -+ * Constants used to select force mode for regulators. -+ */ -+#define QCOM_RPM_FORCE_MODE_NONE 0 -+#define QCOM_RPM_FORCE_MODE_LPM 1 -+#define QCOM_RPM_FORCE_MODE_HPM 2 -+#define QCOM_RPM_FORCE_MODE_AUTO 3 -+#define QCOM_RPM_FORCE_MODE_BYPASS 4 -+ -+#endif diff --git a/target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch b/target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch deleted file mode 100644 index c8a9f3f5d0..0000000000 --- a/target/linux/ipq806x/patches-3.18/123-mfd-devicetree-qcom_rpm-Document-IPQ8064-resources.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 30bc3aa5c4ed3072bdff7d915772df1b91307ed4 Mon Sep 17 00:00:00 2001 -From: Josh Cartwright -Date: Thu, 26 Mar 2015 11:29:25 -0700 -Subject: [PATCH] mfd: devicetree: qcom_rpm: Document IPQ8064 resources - -The IPQ8064 SoC has several RPM-controlled resources, an NSS fabrick -clock and four regulator resources. Provide definitions for them. - -Signed-off-by: Josh Cartwright -[sboyd@codeaurora.org: Drop regulator part of binding] -Signed-off-by: Stephen Boyd -Signed-off-by: Lee Jones ---- - Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 1 + - include/dt-bindings/mfd/qcom-rpm.h | 6 ++++++ - 2 files changed, 7 insertions(+) - ---- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt -+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt -@@ -12,6 +12,7 @@ frequencies. - "qcom,rpm-apq8064" - "qcom,rpm-msm8660" - "qcom,rpm-msm8960" -+ "qcom,rpm-ipq8064" - - - reg: - Usage: required ---- a/include/dt-bindings/mfd/qcom-rpm.h -+++ b/include/dt-bindings/mfd/qcom-rpm.h -@@ -141,6 +141,12 @@ - #define QCOM_RPM_SYS_FABRIC_MODE 131 - #define QCOM_RPM_USB_OTG_SWITCH 132 - #define QCOM_RPM_VDDMIN_GPIO 133 -+#define QCOM_RPM_NSS_FABRIC_0_CLK 134 -+#define QCOM_RPM_NSS_FABRIC_1_CLK 135 -+#define QCOM_RPM_SMB208_S1a 136 -+#define QCOM_RPM_SMB208_S1b 137 -+#define QCOM_RPM_SMB208_S2a 138 -+#define QCOM_RPM_SMB208_S2b 139 - - /* - * Constants used to select force mode for regulators. diff --git a/target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch b/target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch deleted file mode 100644 index e4f094c213..0000000000 --- a/target/linux/ipq806x/patches-3.18/124-regulator-rpm-add-support-for-RPM-controller-SMB208.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0f5bb5b5de3b18877373f746bdb85d8ea0efeedf Mon Sep 17 00:00:00 2001 -From: Josh Cartwright -Date: Thu, 20 Nov 2014 13:41:25 -0600 -Subject: [PATCH] regulator: rpm: add support for RPM-controller SMB208 - -The IPQ8064 reference boards make use of SMB208 regulators which are -controlled by RPM. Implement support for these regulators in the RPM -regulator driver. - -Signed-off-by: Josh Cartwright -Acked-by: Bjorn Andersson -Signed-off-by: Mark Brown ---- - drivers/regulator/qcom_rpm-regulator.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - ---- a/drivers/regulator/qcom_rpm-regulator.c -+++ b/drivers/regulator/qcom_rpm-regulator.c -@@ -183,6 +183,13 @@ static const struct regulator_linear_ran - REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000), - }; - -+static const struct regulator_linear_range smb208_ranges[] = { -+ REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500), -+ REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500), -+ REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000), -+ REGULATOR_LINEAR_RANGE(3100000, 154, 234, 25000), -+}; -+ - static const struct regulator_linear_range ncp_ranges[] = { - REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000), - }; -@@ -559,6 +566,16 @@ static const struct qcom_rpm_reg pm8921_ - .parts = &rpm8960_switch_parts, - }; - -+static const struct qcom_rpm_reg smb208_smps = { -+ .desc.linear_ranges = smb208_ranges, -+ .desc.n_linear_ranges = ARRAY_SIZE(smb208_ranges), -+ .desc.n_voltages = 235, -+ .desc.ops = &uV_ops, -+ .parts = &rpm8960_smps_parts, -+ .supports_force_mode_auto = false, -+ .supports_force_mode_bypass = false, -+}; -+ - static const struct of_device_id rpm_of_match[] = { - { .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo }, - { .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo }, -@@ -578,6 +595,8 @@ static const struct of_device_id rpm_of_ - { .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps }, - { .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp }, - { .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch }, -+ -+ { .compatible = "qcom,rpm-smb208", .data = &smb208_smps }, - { } - }; - MODULE_DEVICE_TABLE(of, rpm_of_match); diff --git a/target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch b/target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch deleted file mode 100644 index 48921a8429..0000000000 --- a/target/linux/ipq806x/patches-3.18/125-regulator-qcom-rpm-Add-missing-state-flag-in-call-to.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 803926825fa4db007437f76654e3e63bafb9b906 Mon Sep 17 00:00:00 2001 -From: Bjorn Andersson -Date: Wed, 26 Nov 2014 13:51:01 -0800 -Subject: [PATCH] regulator: qcom-rpm: Add missing state flag in call to RPM - -This adds the missing state parameter to the call down to the RPM. This -is currently hard coded to the active state, as that's all we're -supporting at this moment. - -Signed-off-by: Bjorn Andersson -Signed-off-by: Lee Jones ---- - drivers/regulator/qcom_rpm-regulator.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/regulator/qcom_rpm-regulator.c -+++ b/drivers/regulator/qcom_rpm-regulator.c -@@ -205,6 +205,7 @@ static int rpm_reg_write(struct qcom_rpm - vreg->val[req->word] |= value << req->shift; - - return qcom_rpm_write(vreg->rpm, -+ QCOM_RPM_ACTIVE_STATE, - vreg->resource, - vreg->val, - vreg->parts->request_len); diff --git a/target/linux/ipq806x/patches-3.18/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-3.18/126-add-rpm-to-ipq8064-dts.patch deleted file mode 100644 index a40738edb1..0000000000 --- a/target/linux/ipq806x/patches-3.18/126-add-rpm-to-ipq8064-dts.patch +++ /dev/null @@ -1,87 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -2,6 +2,7 @@ - - #include "skeleton.dtsi" - #include -+#include - #include - #include - #include -@@ -77,6 +78,63 @@ - ranges; - compatible = "simple-bus"; - -+ rpm@108000 { -+ compatible = "qcom,rpm-ipq8064"; -+ reg = <0x108000 0x1000>; -+ qcom,ipc = <&l2cc 0x8 2>; -+ -+ interrupts = <0 19 0>, -+ <0 21 0>, -+ <0 22 0>; -+ interrupt-names = "ack", -+ "err", -+ "wakeup"; -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ smb208_s1a: smb208-s1a { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ -+ }; -+ -+ smb208_s1b: smb208-s1b { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ -+ smb208_s2a: smb208-s2a { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ -+ smb208_s2b: smb208-s2b { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ }; -+ - qcom_pinmux: pinmux@800000 { - compatible = "qcom,ipq8064-pinctrl"; - reg = <0x800000 0x4000>; -@@ -148,6 +206,12 @@ - reg = <0x02098000 0x1000>, <0x02008000 0x1000>; - }; - -+ l2cc: clock-controller@2011000 { -+ compatible = "qcom,kpss-gcc", "syscon"; -+ reg = <0x2011000 0x1000>; -+ clock-output-names = "acpu_l2_aux"; -+ }; -+ - saw0: regulator@2089000 { - compatible = "qcom,saw2"; - reg = <0x02089000 0x1000>, <0x02009000 0x1000>; diff --git a/target/linux/ipq806x/patches-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch b/target/linux/ipq806x/patches-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch deleted file mode 100644 index 29f74b760d..0000000000 --- a/target/linux/ipq806x/patches-3.18/130-clk_mux-Fix-set_parent-doing-the-wrong-thing-when-IN.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 6793b3cd5da817c4be218bd8632f07cf4d2b0d26 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Wed, 19 Nov 2014 14:48:59 +0100 -Subject: [PATCH] clk_mux: Fix set_parent doing the wrong thing when INDEX_BIT - && index >= 3 - -If CLK_MUX_INDEX_BIT is set, then each bit turns on / off a single parent, -so theoretically multiple parents could be enabled at the same time, but in -practice only one bit should ever be 1. So to select parent 0, set -the register (*) to 0x01, to select parent 1 set it 0x02, parent 2, 0x04, -parent 3, 0x08, etc. - -But the current code does: - - if (mux->flags & CLK_MUX_INDEX_BIT) - index = (1 << ffs(index)); - -Which means that: - -For an input index of 0, ffs returns 0, so we set the register -to 0x01, ok. - -For an input index of 1, ffs returns 1, so we set the register -to 0x02, ok. - -For an input index of 2, ffs returns 2, so we set the register -to 0x04, ok. - -For an input index of 3, ffs returns 1, so we set the register -to 0x02, not good! - -The code should simply be: - - if (mux->flags & CLK_MUX_INDEX_BIT) - index = 1 << index; - -Which always does the right thing, this commit fixes this. - -Signed-off-by: Hans de Goede -Signed-off-by: Michael Turquette ---- - drivers/clk/clk-mux.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/clk/clk-mux.c -+++ b/drivers/clk/clk-mux.c -@@ -77,7 +77,7 @@ static int clk_mux_set_parent(struct clk - - else { - if (mux->flags & CLK_MUX_INDEX_BIT) -- index = (1 << ffs(index)); -+ index = 1 << index; - - if (mux->flags & CLK_MUX_INDEX_ONE) - index++; diff --git a/target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch b/target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch deleted file mode 100644 index 18972f3537..0000000000 --- a/target/linux/ipq806x/patches-3.18/131-clk-Add-__clk_mux_determine_rate_closest.patch +++ /dev/null @@ -1,120 +0,0 @@ -From 15a02c1f6dd7c2bb150c61d00ffb33f584ff2288 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Mon, 19 Jan 2015 18:05:28 -0800 -Subject: [PATCH] clk: Add __clk_mux_determine_rate_closest - -Some clock drivers want to find the closest rate on the input of -a mux instead of a rate that's less than or equal to the desired -rate. Add a generic mux function to support this. - -Signed-off-by: Stephen Boyd -Tested-by: Kenneth Westfield -Signed-off-by: Michael Turquette ---- - drivers/clk/clk.c | 47 +++++++++++++++++++++++++++++++++++--------- - include/linux/clk-provider.h | 8 +++++++- - 2 files changed, 45 insertions(+), 10 deletions(-) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -695,14 +695,20 @@ struct clk *__clk_lookup(const char *nam - return NULL; - } - --/* -- * Helper for finding best parent to provide a given frequency. This can be used -- * directly as a determine_rate callback (e.g. for a mux), or from a more -- * complex clock that may combine a mux with other operations. -- */ --long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, -- unsigned long *best_parent_rate, -- struct clk **best_parent_p) -+static bool mux_is_better_rate(unsigned long rate, unsigned long now, -+ unsigned long best, unsigned long flags) -+{ -+ if (flags & CLK_MUX_ROUND_CLOSEST) -+ return abs(now - rate) < abs(best - rate); -+ -+ return now <= rate && now > best; -+} -+ -+static long -+clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate, -+ unsigned long *best_parent_rate, -+ struct clk **best_parent_p, -+ unsigned long flags) - { - struct clk *clk = hw->clk, *parent, *best_parent = NULL; - int i, num_parents; -@@ -730,7 +736,7 @@ long __clk_mux_determine_rate(struct clk - parent_rate = __clk_round_rate(parent, rate); - else - parent_rate = __clk_get_rate(parent); -- if (parent_rate <= rate && parent_rate > best) { -+ if (mux_is_better_rate(rate, parent_rate, best, flags)) { - best_parent = parent; - best = parent_rate; - } -@@ -743,8 +749,31 @@ out: - - return best; - } -+ -+/* -+ * Helper for finding best parent to provide a given frequency. This can be used -+ * directly as a determine_rate callback (e.g. for a mux), or from a more -+ * complex clock that may combine a mux with other operations. -+ */ -+long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *best_parent_rate, -+ struct clk **best_parent_p) -+{ -+ return clk_mux_determine_rate_flags(hw, rate, best_parent_rate, -+ best_parent_p, 0); -+} - EXPORT_SYMBOL_GPL(__clk_mux_determine_rate); - -+long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate, -+ unsigned long *best_parent_rate, -+ struct clk **best_parent_p) -+{ -+ return clk_mux_determine_rate_flags(hw, rate, best_parent_rate, -+ best_parent_p, -+ CLK_MUX_ROUND_CLOSEST); -+} -+EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); -+ - /*** clk api ***/ - - void __clk_unprepare(struct clk *clk) ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -382,6 +382,8 @@ struct clk *clk_register_divider_table(s - * register, and mask of mux bits are in higher 16-bit of this register. - * While setting the mux bits, higher 16-bit should also be updated to - * indicate changing mux bits. -+ * CLK_MUX_ROUND_CLOSEST - Use the parent rate that is closest to the desired -+ * frequency. - */ - struct clk_mux { - struct clk_hw hw; -@@ -396,7 +398,8 @@ struct clk_mux { - #define CLK_MUX_INDEX_ONE BIT(0) - #define CLK_MUX_INDEX_BIT BIT(1) - #define CLK_MUX_HIWORD_MASK BIT(2) --#define CLK_MUX_READ_ONLY BIT(3) /* mux setting cannot be changed */ -+#define CLK_MUX_READ_ONLY BIT(3) /* mux can't be changed */ -+#define CLK_MUX_ROUND_CLOSEST BIT(4) - - extern const struct clk_ops clk_mux_ops; - extern const struct clk_ops clk_mux_ro_ops; -@@ -554,6 +557,9 @@ struct clk *__clk_lookup(const char *nam - long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *best_parent_rate, - struct clk **best_parent_p); -+long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate, -+ unsigned long *best_parent_rate, -+ struct clk **best_parent_p); - - /* - * FIXME clock api without lock protection diff --git a/target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch b/target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch deleted file mode 100644 index 790f25d28d..0000000000 --- a/target/linux/ipq806x/patches-3.18/132-clk-Add-clk_unregister_-divider-gate-mux-to-close-me.patch +++ /dev/null @@ -1,115 +0,0 @@ -From 4e3c021fb995bcbb5d1f814d00584cb80eb904a8 Mon Sep 17 00:00:00 2001 -From: Krzysztof Kozlowski -Date: Mon, 5 Jan 2015 10:52:40 +0100 -Subject: [PATCH] clk: Add clk_unregister_{divider, gate, mux} to close memory - leak - -The common clk_register_{divider,gate,mux} functions allocated memory -for internal data which wasn't freed anywhere. Drivers using these -helpers could only unregister clocks but the memory would still leak. - -Add corresponding unregister functions which will release all resources. - -Signed-off-by: Krzysztof Kozlowski -Reviewed-by: Stephen Boyd -Signed-off-by: Michael Turquette ---- - drivers/clk/clk-divider.c | 16 ++++++++++++++++ - drivers/clk/clk-gate.c | 16 ++++++++++++++++ - drivers/clk/clk-mux.c | 16 ++++++++++++++++ - include/linux/clk-provider.h | 4 ++++ - 4 files changed, 52 insertions(+) - ---- a/drivers/clk/clk-divider.c -+++ b/drivers/clk/clk-divider.c -@@ -461,3 +461,19 @@ struct clk *clk_register_divider_table(s - width, clk_divider_flags, table, lock); - } - EXPORT_SYMBOL_GPL(clk_register_divider_table); -+ -+void clk_unregister_divider(struct clk *clk) -+{ -+ struct clk_divider *div; -+ struct clk_hw *hw; -+ -+ hw = __clk_get_hw(clk); -+ if (!hw) -+ return; -+ -+ div = to_clk_divider(hw); -+ -+ clk_unregister(clk); -+ kfree(div); -+} -+EXPORT_SYMBOL_GPL(clk_unregister_divider); ---- a/drivers/clk/clk-gate.c -+++ b/drivers/clk/clk-gate.c -@@ -162,3 +162,19 @@ struct clk *clk_register_gate(struct dev - return clk; - } - EXPORT_SYMBOL_GPL(clk_register_gate); -+ -+void clk_unregister_gate(struct clk *clk) -+{ -+ struct clk_gate *gate; -+ struct clk_hw *hw; -+ -+ hw = __clk_get_hw(clk); -+ if (!hw) -+ return; -+ -+ gate = to_clk_gate(hw); -+ -+ clk_unregister(clk); -+ kfree(gate); -+} -+EXPORT_SYMBOL_GPL(clk_unregister_gate); ---- a/drivers/clk/clk-mux.c -+++ b/drivers/clk/clk-mux.c -@@ -177,3 +177,19 @@ struct clk *clk_register_mux(struct devi - NULL, lock); - } - EXPORT_SYMBOL_GPL(clk_register_mux); -+ -+void clk_unregister_mux(struct clk *clk) -+{ -+ struct clk_mux *mux; -+ struct clk_hw *hw; -+ -+ hw = __clk_get_hw(clk); -+ if (!hw) -+ return; -+ -+ mux = to_clk_mux(hw); -+ -+ clk_unregister(clk); -+ kfree(mux); -+} -+EXPORT_SYMBOL_GPL(clk_unregister_mux); ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -294,6 +294,7 @@ struct clk *clk_register_gate(struct dev - const char *parent_name, unsigned long flags, - void __iomem *reg, u8 bit_idx, - u8 clk_gate_flags, spinlock_t *lock); -+void clk_unregister_gate(struct clk *clk); - - struct clk_div_table { - unsigned int val; -@@ -361,6 +362,7 @@ struct clk *clk_register_divider_table(s - void __iomem *reg, u8 shift, u8 width, - u8 clk_divider_flags, const struct clk_div_table *table, - spinlock_t *lock); -+void clk_unregister_divider(struct clk *clk); - - /** - * struct clk_mux - multiplexer clock -@@ -414,6 +416,8 @@ struct clk *clk_register_mux_table(struc - void __iomem *reg, u8 shift, u32 mask, - u8 clk_mux_flags, u32 *table, spinlock_t *lock); - -+void clk_unregister_mux(struct clk *clk); -+ - void of_fixed_factor_clk_setup(struct device_node *node); - - /** diff --git a/target/linux/ipq806x/patches-3.18/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-3.18/133-ARM-Add-Krait-L2-register-accessor-functions.patch deleted file mode 100644 index 36a92c858a..0000000000 --- a/target/linux/ipq806x/patches-3.18/133-ARM-Add-Krait-L2-register-accessor-functions.patch +++ /dev/null @@ -1,144 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions -From: Stephen Boyd -X-Patchwork-Id: 6063051 -Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , - Mark Rutland , Russell King , - Courtney Cavin -Date: Fri, 20 Mar 2015 23:45:20 -0700 - -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 -Cc: Russell King -Cc: Courtney Cavin -Signed-off-by: Stephen Boyd - ---- -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 -@@ -9,6 +9,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_ICST) += icst.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 -+#include -+ -+#include -+#include -+ -+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 diff --git a/target/linux/ipq806x/patches-3.18/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-3.18/134-clk-mux-Split-out-register-accessors-for-reuse.patch deleted file mode 100644 index 50022e6107..0000000000 --- a/target/linux/ipq806x/patches-3.18/134-clk-mux-Split-out-register-accessors-for-reuse.patch +++ /dev/null @@ -1,192 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,02/13] clk: mux: Split out register accessors for reuse -From: Stephen Boyd -X-Patchwork-Id: 6063111 -Message-Id: <1426920332-9340-3-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:21 -0700 - -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 - ---- -drivers/clk/clk-mux.c | 76 +++++++++++++++++++++++++++----------------- - include/linux/clk-provider.h | 9 ++++-- - 2 files changed, 54 insertions(+), 31 deletions(-) - ---- a/drivers/clk/clk-mux.c -+++ b/drivers/clk/clk-mux.c -@@ -29,35 +29,24 @@ - - #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) - --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_get_num_parents(hw->clk); -- 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) -@@ -65,24 +54,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); -+} - -- else { -- if (mux->flags & CLK_MUX_INDEX_BIT) -- index = 1 << index; -+unsigned int clk_mux_reindex(u8 index, unsigned int *table, -+ unsigned long flags) -+{ -+ unsigned int val = index; - -- if (mux->flags & CLK_MUX_INDEX_ONE) -- index++; -+ if (table) { -+ val = table[val]; -+ } else { -+ if (flags & CLK_MUX_INDEX_BIT) -+ val = 1 << 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); - -@@ -102,21 +120,21 @@ 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); - - struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **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 *clk; ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -390,7 +390,7 @@ void clk_unregister_divider(struct clk * - struct clk_mux { - struct clk_hw hw; - void __iomem *reg; -- u32 *table; -+ unsigned int *table; - u32 mask; - u8 shift; - u8 flags; -@@ -406,6 +406,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 **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, -@@ -414,7 +419,7 @@ struct clk *clk_register_mux(struct devi - struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **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); - diff --git a/target/linux/ipq806x/patches-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch b/target/linux/ipq806x/patches-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch deleted file mode 100644 index 02d96ad08a..0000000000 --- a/target/linux/ipq806x/patches-3.18/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch +++ /dev/null @@ -1,129 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3, 03/13] clk: Avoid sending high rates to downstream clocks during - set_rate -From: Stephen Boyd -X-Patchwork-Id: 6063271 -Message-Id: <1426920332-9340-4-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:22 -0700 - -If a clock is on and we call clk_set_rate() on it we may get into -a situation where the clock temporarily increases in rate -dramatically while we walk the tree and call .set_rate() ops. For -example, consider a case where a PLL feeds into a divider. -Initially the divider is set to divide by 1 and the PLL is -running fairly slow (100MHz). The downstream consumer of the -divider output can only handle rates =< 400 MHz, but the divider -can only choose between divisors of 1 and 4. - - +-----+ +----------------+ - | PLL |-->| div 1 or div 4 |---> consumer device - +-----+ +----------------+ - -To achieve a rate of 400MHz on the output of the divider, we -would have to set the rate of the PLL to 1.6 GHz and then divide -it by 4. The current code would set the PLL to 1.6GHz first while -the divider is still set to 1, thus causing the downstream -consumer of the clock to receive a few clock cycles of 1.6GHz -clock (far beyond it's maximum acceptable rate). We should be -changing the divider first before increasing the PLL rate to -avoid this problem. - -Therefore, set the rate of any child clocks that are increasing -in rate from their current rate so that they can increase their -dividers if necessary. We assume that there isn't such a thing as -minimum rate requirements. - -Signed-off-by: Stephen Boyd - ---- -drivers/clk/clk.c | 34 ++++++++++++++++++++++------------ - 1 file changed, 22 insertions(+), 12 deletions(-) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -1476,21 +1476,23 @@ static struct clk *clk_propagate_rate_ch - * walk down a subtree and set the new rates notifying the rate - * change on the way - */ --static void clk_change_rate(struct clk *clk) -+static void clk_change_rate(struct clk *clk, unsigned long best_parent_rate) - { - struct clk *child; - struct hlist_node *tmp; - unsigned long old_rate; -- unsigned long best_parent_rate = 0; - bool skip_set_rate = false; - struct clk *old_parent; - -- old_rate = clk->rate; -+ hlist_for_each_entry(child, &clk->children, child_node) { -+ /* Skip children who will be reparented to another clock */ -+ if (child->new_parent && child->new_parent != clk) -+ continue; -+ if (child->new_rate > child->rate) -+ clk_change_rate(child, clk->new_rate); -+ } - -- if (clk->new_parent) -- best_parent_rate = clk->new_parent->rate; -- else if (clk->parent) -- best_parent_rate = clk->parent->rate; -+ old_rate = clk->rate; - - if (clk->new_parent && clk->new_parent != clk->parent) { - old_parent = __clk_set_parent_before(clk, clk->new_parent); -@@ -1510,7 +1512,7 @@ static void clk_change_rate(struct clk * - if (!skip_set_rate && clk->ops->set_rate) - clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); - -- clk->rate = clk_recalc(clk, best_parent_rate); -+ clk->rate = clk->new_rate; - - if (clk->notifier_count && old_rate != clk->rate) - __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); -@@ -1523,12 +1525,13 @@ static void clk_change_rate(struct clk * - /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) - continue; -- clk_change_rate(child); -+ if (child->new_rate != child->rate) -+ clk_change_rate(child, clk->new_rate); - } - - /* handle the new child who might not be in clk->children yet */ -- if (clk->new_child) -- clk_change_rate(clk->new_child); -+ if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate) -+ clk_change_rate(clk->new_child, clk->new_rate); - } - - /** -@@ -1556,6 +1559,7 @@ int clk_set_rate(struct clk *clk, unsign - { - struct clk *top, *fail_clk; - int ret = 0; -+ unsigned long parent_rate; - - if (!clk) - return 0; -@@ -1589,8 +1593,13 @@ int clk_set_rate(struct clk *clk, unsign - goto out; - } - -+ if (top->parent) -+ parent_rate = top->parent->rate; -+ else -+ parent_rate = 0; -+ - /* change the rates */ -- clk_change_rate(top); -+ clk_change_rate(top, parent_rate); - - out: - clk_prepare_unlock(); diff --git a/target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch deleted file mode 100644 index 227f8cea05..0000000000 --- a/target/linux/ipq806x/patches-3.18/136-clk-Add-safe-switch-hook.patch +++ /dev/null @@ -1,170 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,04/13] clk: Add safe switch hook -From: Stephen Boyd -X-Patchwork-Id: 6063211 -Message-Id: <1426920332-9340-5-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:23 -0700 - -Sometimes clocks can't accept their parent source turning off -while the source is reprogrammed to a different rate. Most -notably CPU clocks require a way to switch away from the current -PLL they're running on, reprogram that PLL to a new rate, and -then switch back to the PLL with the new rate once they're done. -Add a hook that drivers can implement allowing them to return a -'safe parent' that they can switch their parent to while the -upstream source is reprogrammed to support this. - -Signed-off-by: Stephen Boyd - ---- -This patch is good enough for Krait, but soon I'll need to -support a "safe rate" where we ask a clock what rate it needs to be running -at to be sure it's within voltage constraints. Right now safe parent -handles that problem on Krait, but on other platforms it won't work. - - drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------ - include/linux/clk-provider.h | 1 + - 2 files changed, 54 insertions(+), 8 deletions(-) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -1350,7 +1350,8 @@ out: - static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, - struct clk *new_parent, u8 p_index) - { -- struct clk *child; -+ struct clk *child, *parent; -+ struct clk_hw *parent_hw; - - clk->new_rate = new_rate; - clk->new_parent = new_parent; -@@ -1360,6 +1361,18 @@ static void clk_calc_subtree(struct clk - if (new_parent && new_parent != clk->parent) - new_parent->new_child = clk; - -+ if (clk->ops->get_safe_parent) { -+ parent_hw = clk->ops->get_safe_parent(clk->hw); -+ if (parent_hw) { -+ parent = parent_hw->clk; -+ p_index = clk_fetch_parent_index(clk, parent); -+ clk->safe_parent_index = p_index; -+ clk->safe_parent = parent; -+ } -+ } else { -+ clk->safe_parent = NULL; -+ } -+ - hlist_for_each_entry(child, &clk->children, child_node) { - child->new_rate = clk_recalc(child, new_rate); - clk_calc_subtree(child, child->new_rate, NULL, 0); -@@ -1439,17 +1452,47 @@ out: - * so that in case of an error we can walk down the whole tree again and - * abort the change. - */ --static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) -+static struct clk *clk_propagate_rate_change(struct clk *clk, -+ unsigned long event) - { - struct clk *child, *tmp_clk, *fail_clk = NULL; -+ struct clk *old_parent; - int ret = NOTIFY_DONE; - -- if (clk->rate == clk->new_rate) -+ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE) - return NULL; - -+ switch (event) { -+ case PRE_RATE_CHANGE: -+ if (clk->safe_parent) -+ clk->ops->set_parent(clk->hw, clk->safe_parent_index); -+ clk->old_rate = clk->rate; -+ break; -+ case POST_RATE_CHANGE: -+ if (clk->safe_parent) { -+ old_parent = __clk_set_parent_before(clk, -+ clk->new_parent); -+ if (clk->ops->set_rate_and_parent) { -+ clk->ops->set_rate_and_parent(clk->hw, -+ clk->new_rate, -+ clk->new_parent ? -+ clk->new_parent->rate : 0, -+ clk->new_parent_index); -+ } else if (clk->ops->set_parent) { -+ clk->ops->set_parent(clk->hw, -+ clk->new_parent_index); -+ } -+ __clk_set_parent_after(clk, clk->new_parent, -+ old_parent); -+ } -+ break; -+ } -+ - if (clk->notifier_count) { -- ret = __clk_notify(clk, event, clk->rate, clk->new_rate); -- if (ret & NOTIFY_STOP_MASK) -+ if (event != POST_RATE_CHANGE || clk->old_rate != clk->rate) -+ ret = __clk_notify(clk, event, clk->old_rate, -+ clk->new_rate); -+ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) - fail_clk = clk; - } - -@@ -1494,7 +1537,8 @@ static void clk_change_rate(struct clk * - - old_rate = clk->rate; - -- if (clk->new_parent && clk->new_parent != clk->parent) { -+ if (clk->new_parent && clk->new_parent != clk->parent && -+ !clk->safe_parent) { - old_parent = __clk_set_parent_before(clk, clk->new_parent); - - if (clk->ops->set_rate_and_parent) { -@@ -1514,9 +1558,6 @@ static void clk_change_rate(struct clk * - - clk->rate = clk->new_rate; - -- if (clk->notifier_count && old_rate != clk->rate) -- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); -- - /* - * Use safe iteration, as change_rate can actually swap parents - * for certain clock types. -@@ -1601,6 +1642,8 @@ int clk_set_rate(struct clk *clk, unsign - /* change the rates */ - clk_change_rate(top, parent_rate); - -+ clk_propagate_rate_change(top, POST_RATE_CHANGE); -+ - out: - clk_prepare_unlock(); - ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -179,6 +179,7 @@ struct clk_ops { - struct clk **best_parent_clk); - int (*set_parent)(struct clk_hw *hw, u8 index); - u8 (*get_parent)(struct clk_hw *hw); -+ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate); - int (*set_rate_and_parent)(struct clk_hw *hw, ---- a/include/linux/clk-private.h -+++ b/include/linux/clk-private.h -@@ -38,8 +38,11 @@ struct clk { - struct clk **parents; - u8 num_parents; - u8 new_parent_index; -+ u8 safe_parent_index; - unsigned long rate; -+ unsigned long old_rate; - unsigned long new_rate; -+ struct clk *safe_parent; - struct clk *new_parent; - struct clk *new_child; - unsigned long flags; diff --git a/target/linux/ipq806x/patches-3.18/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-3.18/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch deleted file mode 100644 index 701d5e7046..0000000000 --- a/target/linux/ipq806x/patches-3.18/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch +++ /dev/null @@ -1,351 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) -From: Stephen Boyd -X-Patchwork-Id: 6063261 -Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:24 -0700 - -HFPLLs are the main frequency source for Krait CPU clocks. Add -support for changing the rate of these PLLs. - -Signed-off-by: Stephen Boyd - ---- -I'd really like to get rid of __clk_hfpll_init_once() if possible... - - 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 -@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o - clk-qcom-y += clk-rcg.o - clk-qcom-y += clk-rcg2.o - clk-qcom-y += clk-branch.o -+clk-qcom-y += clk-hfpll.o - clk-qcom-y += reset.o - - obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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_get_rate(hw->clk); -+ -+ /* 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. -+ */ -+ udelay(10); -+ -+ /* 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 { -+ udelay(60); -+ } -+ -+ /* 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 -+#include -+#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 diff --git a/target/linux/ipq806x/patches-3.18/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-3.18/138-clk-qcom-Add-HFPLL-driver.patch deleted file mode 100644 index a0b1d64dc6..0000000000 --- a/target/linux/ipq806x/patches-3.18/138-clk-qcom-Add-HFPLL-driver.patch +++ /dev/null @@ -1,206 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,06/13] clk: qcom: Add HFPLL driver -From: Stephen Boyd -X-Patchwork-Id: 6063231 -Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:25 -0700 - -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: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++ - drivers/clk/qcom/Kconfig | 8 ++ - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++ - 4 files changed, 158 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: -+ Definition: must be "qcom,hfpll" -+ -+- reg: -+ Usage: required -+ Value type: -+ 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: -+ 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 -@@ -70,3 +70,11 @@ config MSM_MMCC_8974 - Support for the multimedia clock controller on msm8974 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 -@@ -17,3 +17,4 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm896 - obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o -+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o ---- /dev/null -+++ b/drivers/clk/qcom/hfpll.c -@@ -0,0 +1,109 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 clk *clk; -+ 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); -+ -+ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr); -+ -+ return PTR_ERR_OR_ZERO(clk); -+} -+ -+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"); diff --git a/target/linux/ipq806x/patches-3.18/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-3.18/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch deleted file mode 100644 index 7fd53d1d9c..0000000000 --- a/target/linux/ipq806x/patches-3.18/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch +++ /dev/null @@ -1,127 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs -From: Stephen Boyd -X-Patchwork-Id: 6063241 -Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:27 -0700 - -Describe the HFPLLs present on IPQ806X devices. - -Signed-off-by: Stephen Boyd - ---- -drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 83 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 = { -@@ -102,6 +103,85 @@ 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, -@@ -2261,6 +2341,9 @@ static struct clk_regmap *gcc_ipq806x_cl - [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr, - [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr, - [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, -+ [PLL9] = &hfpll0.clkr, -+ [PLL10] = &hfpll1.clkr, -+ [PLL12] = &hfpll_l2.clkr, - }; - - static const struct qcom_reset_map gcc_ipq806x_resets[] = { diff --git a/target/linux/ipq806x/patches-3.18/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-3.18/140-clk-qcom-Add-support-for-Krait-clocks.patch deleted file mode 100644 index 63292e8611..0000000000 --- a/target/linux/ipq806x/patches-3.18/140-clk-qcom-Add-support-for-Krait-clocks.patch +++ /dev/null @@ -1,271 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,09/13] clk: qcom: Add support for Krait clocks -From: Stephen Boyd -X-Patchwork-Id: 6063251 -Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:28 -0700 - -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 - ---- -drivers/clk/qcom/Kconfig | 4 ++ - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++ - drivers/clk/qcom/clk-krait.h | 49 +++++++++++++ - 4 files changed, 220 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 -@@ -78,3 +78,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 -@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o - clk-qcom-y += clk-rcg.o - clk-qcom-y += clk-rcg2.o - clk-qcom-y += clk-branch.o -+clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o - clk-qcom-y += clk-hfpll.o - clk-qcom-y += reset.o - ---- /dev/null -+++ b/drivers/clk/qcom/clk-krait.c -@@ -0,0 +1,166 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#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); -+} -+ -+static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw) -+{ -+ int i; -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ int num_parents = __clk_get_num_parents(hw->clk); -+ -+ i = mux->safe_sel; -+ for (i = 0; i < num_parents; i++) -+ if (mux->safe_sel == mux->parent_map[i]) -+ break; -+ -+ return __clk_get_hw(clk_get_parent_by_index(hw->clk, i)); -+} -+ -+static int krait_mux_enable(struct clk_hw *hw) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ -+ __krait_mux_set_sel(mux, mux->en_mask); -+ -+ return 0; -+} -+ -+static void krait_mux_disable(struct clk_hw *hw) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ -+ __krait_mux_set_sel(mux, mux->safe_sel); -+} -+ -+const struct clk_ops krait_mux_clk_ops = { -+ .enable = krait_mux_enable, -+ .disable = krait_mux_disable, -+ .set_parent = krait_mux_set_parent, -+ .get_parent = krait_mux_get_parent, -+ .determine_rate = __clk_mux_determine_rate_closest, -+ .get_safe_parent = krait_mux_get_safe_parent, -+}; -+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_round_rate(__clk_get_parent(hw->clk), 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,49 @@ -+/* -+ * 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 -+ -+struct krait_mux_clk { -+ unsigned int *parent_map; -+ bool has_safe_parent; -+ u8 safe_sel; -+ u32 offset; -+ u32 mask; -+ u32 shift; -+ u32 en_mask; -+ bool lpl; -+ -+ struct clk_hw hw; -+}; -+ -+#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 diff --git a/target/linux/ipq806x/patches-3.18/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-3.18/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch deleted file mode 100644 index 06b14d8b53..0000000000 --- a/target/linux/ipq806x/patches-3.18/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch +++ /dev/null @@ -1,205 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver -From: Stephen Boyd -X-Patchwork-Id: 6063201 -Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:29 -0700 - -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: -Signed-off-by: Stephen Boyd - ---- -.../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 | 95 ++++++++++++++++++++++ - 5 files changed, 139 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: -+ 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: -+ Definition: should be one of: -+ "qcom,kpss-gcc" -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: base address and size of the register region -+ -+- clock-output-names: -+ Usage: required -+ Value type: -+ 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 -@@ -79,6 +79,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 -@@ -18,4 +18,5 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm896 - obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.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,95 @@ -+/* 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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"); diff --git a/target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch deleted file mode 100644 index 98a09ac08e..0000000000 --- a/target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch +++ /dev/null @@ -1,435 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver -From: Stephen Boyd -X-Patchwork-Id: 6063121 -Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:30 -0700 - -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: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ - drivers/clk/qcom/Kconfig | 8 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++ - 4 files changed, 383 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: -+ Definition: must be one of: -+ "qcom,krait-cc-v1" -+ "qcom,krait-cc-v2" -+ -+- #clock-cells: -+ Usage: required -+ Value type: -+ 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 -@@ -87,6 +87,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 -@@ -20,3 +20,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8 - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.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,352 @@ -+/* 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 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 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->has_safe_parent = true; -+ mux->safe_sel = 2; -+ 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 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->has_safe_parent = true; -+ mux->safe_sel = 0; -+ 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 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, CLK_IS_ROOT, 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"); diff --git a/target/linux/ipq806x/patches-3.18/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-3.18/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch deleted file mode 100644 index c3ca9b5f70..0000000000 --- a/target/linux/ipq806x/patches-3.18/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch +++ /dev/null @@ -1,304 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs -From: Stephen Boyd -X-Patchwork-Id: 6063191 -Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:31 -0700 - -Register a cpufreq-generic device whenever we detect that a -"qcom,krait" compatible CPU is present in DT. - -Cc: -Signed-off-by: Stephen Boyd - ---- -.../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: -+ 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: -+ 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 -@@ -129,6 +129,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 -@@ -64,6 +64,7 @@ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += o - obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o - obj-$(CONFIG_PXA27x) += 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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"); diff --git a/target/linux/ipq806x/patches-3.18/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-3.18/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch deleted file mode 100644 index c398487ae5..0000000000 --- a/target/linux/ipq806x/patches-3.18/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch +++ /dev/null @@ -1,100 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -25,6 +25,11 @@ - next-level-cache = <&L2>; - qcom,acc = <&acc0>; - qcom,saw = <&saw0>; -+ clocks = <&kraitcc 0>; -+ clock-names = "cpu"; -+ clock-latency = <100000>; -+ core-supply = <&smb208_s2a>; -+ voltage-tolerance = <5>; - }; - - cpu@1 { -@@ -35,11 +40,24 @@ - next-level-cache = <&L2>; - qcom,acc = <&acc1>; - qcom,saw = <&saw1>; -+ clocks = <&kraitcc 1>; -+ clock-names = "cpu"; -+ clock-latency = <100000>; -+ core-supply = <&smb208_s2b>; - }; - - L2: l2-cache { - compatible = "cache"; - cache-level = <2>; -+ clocks = <&kraitcc 4>; -+ clock-names = "cache"; -+ cache-points-kHz = < -+ /* kHz uV CPU kHz */ -+ 1200000 1150000 1200000 -+ 1000000 1100000 600000 -+ 384000 1100000 384000 -+ >; -+ vdd_dig-supply = <&smb208_s1a>; - }; - }; - -@@ -72,6 +90,46 @@ - }; - }; - -+ kraitcc: clock-controller { -+ compatible = "qcom,krait-cc-v1"; -+ #clock-cells = <1>; -+ }; -+ -+ qcom,pvs { -+ qcom,pvs-format-a; -+ qcom,speed0-pvs0-bin-v0 = -+ < 1400000000 1250000 >, -+ < 1200000000 1200000 >, -+ < 1000000000 1150000 >, -+ < 800000000 1100000 >, -+ < 600000000 1050000 >, -+ < 384000000 1000000 >; -+ -+ qcom,speed0-pvs1-bin-v0 = -+ < 1400000000 1175000 >, -+ < 1200000000 1125000 >, -+ < 1000000000 1075000 >, -+ < 800000000 1025000 >, -+ < 600000000 975000 >, -+ < 384000000 925000 >; -+ -+ qcom,speed0-pvs2-bin-v0 = -+ < 1400000000 1125000 >, -+ < 1200000000 1075000 >, -+ < 1000000000 1025000 >, -+ < 800000000 995000 >, -+ < 600000000 925000 >, -+ < 384000000 875000 >; -+ -+ qcom,speed0-pvs3-bin-v0 = -+ < 1400000000 1050000 >, -+ < 1200000000 1000000 >, -+ < 1000000000 950000 >, -+ < 800000000 900000 >, -+ < 600000000 850000 >, -+ < 384000000 800000 >; -+ }; -+ - soc: soc { - #address-cells = <1>; - #size-cells = <1>; -@@ -199,11 +257,13 @@ - acc0: clock-controller@2088000 { - compatible = "qcom,kpss-acc-v1"; - reg = <0x02088000 0x1000>, <0x02008000 0x1000>; -+ clock-output-names = "acpu0_aux"; - }; - - acc1: clock-controller@2098000 { - compatible = "qcom,kpss-acc-v1"; - reg = <0x02098000 0x1000>, <0x02008000 0x1000>; -+ clock-output-names = "acpu1_aux"; - }; - - l2cc: clock-controller@2011000 { diff --git a/target/linux/ipq806x/patches-3.18/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-3.18/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch deleted file mode 100644 index 6052b417ac..0000000000 --- a/target/linux/ipq806x/patches-3.18/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch +++ /dev/null @@ -1,461 +0,0 @@ -From dd77db4143290689d3a5e1ec61627233d0711b66 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 30 May 2014 16:36:11 -0700 -Subject: [PATCH] FROMLIST: cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 - -Krait processors have individual clocks for each CPU that can -scale independently from one another. cpufreq-cpu0 is fairly -close to this, but assumes that there is only one clock for all -CPUs. Add a driver to support the Krait configuration. - -TODO: Merge into cpufreq-cpu0? Or make generic? - -Signed-off-by: Stephen Boyd - ---- - drivers/cpufreq/Kconfig | 13 +++ - drivers/cpufreq/Makefile | 1 + - drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 204 insertions(+) - create mode 100644 drivers/cpufreq/cpufreq-krait.c - ---- a/drivers/cpufreq/Kconfig -+++ b/drivers/cpufreq/Kconfig -@@ -196,6 +196,19 @@ config CPUFREQ_DT - - If in doubt, say N. - -+config GENERIC_CPUFREQ_KRAIT -+ tristate "Krait cpufreq driver" -+ depends on HAVE_CLK && OF -+ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: -+ depends on !CPU_THERMAL || THERMAL -+ select PM_OPP -+ help -+ This adds a generic cpufreq driver for CPU0 frequency management. -+ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) -+ systems which share clock and voltage across all CPUs. -+ -+ If in doubt, say N. -+ - menu "x86 CPU frequency scaling drivers" - depends on X86 - source "drivers/cpufreq/Kconfig.x86" ---- a/drivers/cpufreq/Makefile -+++ b/drivers/cpufreq/Makefile -@@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) - obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o - - obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o -+obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o - - ################################################################################## - # x86 drivers. ---- /dev/null -+++ b/drivers/cpufreq/cpufreq-krait.c -@@ -0,0 +1,390 @@ -+/* -+ * Copyright (C) 2012 Freescale Semiconductor, Inc. -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * The OPP code in function krait_set_target() is reused from -+ * drivers/cpufreq/omap-cpufreq.c -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static unsigned int transition_latency; -+static unsigned int voltage_tolerance; /* in percentage */ -+ -+static struct device *cpu_dev; -+static DEFINE_PER_CPU(struct clk *, krait_cpu_clks); -+static DEFINE_PER_CPU(struct regulator *, krait_supply_core); -+static struct cpufreq_frequency_table *freq_table; -+static struct thermal_cooling_device *cdev; -+ -+struct cache_points { -+ unsigned long cache_freq; -+ unsigned int cache_volt; -+ unsigned long cpu_freq; -+}; -+ -+static struct regulator *krait_l2_reg; -+static struct clk *krait_l2_clk; -+static struct cache_points *krait_l2_points; -+static int nr_krait_l2_points; -+ -+static int krait_parse_cache_points(struct device *dev, -+ struct device_node *of_node) -+{ -+ const struct property *prop; -+ const __be32 *val; -+ int nr, i; -+ -+ prop = of_find_property(of_node, "cache-points-kHz", NULL); -+ if (!prop) -+ return -ENODEV; -+ if (!prop->value) -+ return -ENODATA; -+ -+ /* -+ * Each OPP is a set of tuples consisting of frequency and -+ * cpu-frequency like . -+ */ -+ nr = prop->length / sizeof(u32); -+ if (nr % 3) { -+ dev_err(dev, "%s: Invalid cache points\n", __func__); -+ return -EINVAL; -+ } -+ nr /= 3; -+ -+ krait_l2_points = devm_kcalloc(dev, nr, sizeof(*krait_l2_points), -+ GFP_KERNEL); -+ if (!krait_l2_points) -+ return -ENOMEM; -+ nr_krait_l2_points = nr; -+ -+ for (i = 0, val = prop->value; i < nr; i++) { -+ unsigned long cache_freq = be32_to_cpup(val++) * 1000; -+ unsigned int cache_volt = be32_to_cpup(val++); -+ unsigned long cpu_freq = be32_to_cpup(val++) * 1000; -+ -+ krait_l2_points[i].cache_freq = cache_freq; -+ krait_l2_points[i].cache_volt = cache_volt; -+ krait_l2_points[i].cpu_freq = cpu_freq; -+ } -+ -+ return 0; -+} -+ -+static int krait_set_target(struct cpufreq_policy *policy, unsigned int index) -+{ -+ struct dev_pm_opp *opp; -+ unsigned long volt = 0, volt_old = 0, tol = 0; -+ unsigned long freq, max_cpu_freq = 0; -+ unsigned int old_freq, new_freq; -+ long freq_Hz, freq_exact; -+ int ret, i; -+ struct clk *cpu_clk; -+ struct regulator *core; -+ unsigned int cpu; -+ -+ cpu_clk = per_cpu(krait_cpu_clks, policy->cpu); -+ -+ freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); -+ if (freq_Hz <= 0) -+ freq_Hz = freq_table[index].frequency * 1000; -+ -+ freq_exact = freq_Hz; -+ new_freq = freq_Hz / 1000; -+ old_freq = clk_get_rate(cpu_clk) / 1000; -+ -+ core = per_cpu(krait_supply_core, policy->cpu); -+ -+ rcu_read_lock(); -+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); -+ if (IS_ERR(opp)) { -+ rcu_read_unlock(); -+ pr_err("failed to find OPP for %ld\n", freq_Hz); -+ return PTR_ERR(opp); -+ } -+ volt = dev_pm_opp_get_voltage(opp); -+ rcu_read_unlock(); -+ tol = volt * voltage_tolerance / 100; -+ volt_old = regulator_get_voltage(core); -+ -+ pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", -+ old_freq / 1000, volt_old ? volt_old / 1000 : -1, -+ new_freq / 1000, volt ? volt / 1000 : -1); -+ -+ /* scaling up? scale voltage before frequency */ -+ if (new_freq > old_freq) { -+ ret = regulator_set_voltage_tol(core, volt, tol); -+ if (ret) { -+ pr_err("failed to scale voltage up: %d\n", ret); -+ return ret; -+ } -+ } -+ -+ ret = clk_set_rate(cpu_clk, freq_exact); -+ if (ret) { -+ pr_err("failed to set clock rate: %d\n", ret); -+ return ret; -+ } -+ -+ /* scaling down? scale voltage after frequency */ -+ if (new_freq < old_freq) { -+ ret = regulator_set_voltage_tol(core, volt, tol); -+ if (ret) { -+ pr_err("failed to scale voltage down: %d\n", ret); -+ clk_set_rate(cpu_clk, old_freq * 1000); -+ } -+ } -+ -+ for_each_possible_cpu(cpu) { -+ freq = clk_get_rate(per_cpu(krait_cpu_clks, cpu)); -+ max_cpu_freq = max(max_cpu_freq, freq); -+ } -+ -+ for (i = 0; i < nr_krait_l2_points; i++) { -+ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { -+ if (krait_l2_reg) { -+ ret = regulator_set_voltage_tol(krait_l2_reg, -+ krait_l2_points[i].cache_volt, -+ tol); -+ if (ret) { -+ pr_err("failed to scale l2 voltage: %d\n", -+ ret); -+ } -+ } -+ ret = clk_set_rate(krait_l2_clk, -+ krait_l2_points[i].cache_freq); -+ if (ret) -+ pr_err("failed to scale l2 clk: %d\n", ret); -+ break; -+ } -+ -+ } -+ -+ return ret; -+} -+ -+static int krait_cpufreq_init(struct cpufreq_policy *policy) -+{ -+ int ret; -+ -+ policy->clk = per_cpu(krait_cpu_clks, policy->cpu); -+ -+ ret = cpufreq_table_validate_and_show(policy, freq_table); -+ if (ret) { -+ pr_err("%s: invalid frequency table: %d\n", __func__, ret); -+ return ret; -+ } -+ -+ policy->cpuinfo.transition_latency = transition_latency; -+ -+ return 0; -+} -+ -+static struct cpufreq_driver krait_cpufreq_driver = { -+ .flags = CPUFREQ_STICKY, -+ .verify = cpufreq_generic_frequency_table_verify, -+ .target_index = krait_set_target, -+ .get = cpufreq_generic_get, -+ .init = krait_cpufreq_init, -+ .name = "generic_krait", -+ .attr = cpufreq_generic_attr, -+}; -+ -+static int krait_cpufreq_probe(struct platform_device *pdev) -+{ -+ struct device_node *np, *cache; -+ int ret, i; -+ unsigned int cpu; -+ struct device *dev; -+ struct clk *clk; -+ struct regulator *core; -+ unsigned long freq_Hz, freq, max_cpu_freq = 0; -+ struct dev_pm_opp *opp; -+ unsigned long volt, tol; -+ -+ cpu_dev = get_cpu_device(0); -+ if (!cpu_dev) { -+ pr_err("failed to get krait device\n"); -+ return -ENODEV; -+ } -+ -+ np = of_node_get(cpu_dev->of_node); -+ if (!np) { -+ pr_err("failed to find krait node\n"); -+ return -ENOENT; -+ } -+ -+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); -+ if (ret) { -+ pr_err("failed to init cpufreq table: %d\n", ret); -+ goto out_put_node; -+ } -+ -+ of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance); -+ -+ if (of_property_read_u32(np, "clock-latency", &transition_latency)) -+ transition_latency = CPUFREQ_ETERNAL; -+ -+ cache = of_find_next_cache_node(np); -+ if (cache) { -+ struct device_node *vdd; -+ -+ vdd = of_parse_phandle(cache, "vdd_dig-supply", 0); -+ if (vdd) { -+ krait_l2_reg = regulator_get(NULL, vdd->name); -+ if (IS_ERR(krait_l2_reg)) { -+ pr_warn("failed to get l2 vdd_dig supply\n"); -+ krait_l2_reg = NULL; -+ } -+ of_node_put(vdd); -+ } -+ -+ krait_l2_clk = of_clk_get(cache, 0); -+ if (!IS_ERR(krait_l2_clk)) { -+ ret = krait_parse_cache_points(&pdev->dev, cache); -+ if (ret) -+ clk_put(krait_l2_clk); -+ } -+ if (IS_ERR(krait_l2_clk) || ret) -+ krait_l2_clk = NULL; -+ } -+ -+ for_each_possible_cpu(cpu) { -+ dev = get_cpu_device(cpu); -+ if (!dev) { -+ pr_err("failed to get krait device\n"); -+ ret = -ENOENT; -+ goto out_free_table; -+ } -+ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(clk)) { -+ ret = PTR_ERR(clk); -+ goto out_free_table; -+ } -+ core = devm_regulator_get(dev, "core"); -+ if (IS_ERR(core)) { -+ pr_debug("failed to get core regulator\n"); -+ ret = PTR_ERR(core); -+ goto out_free_table; -+ } -+ per_cpu(krait_supply_core, cpu) = core; -+ -+ freq = freq_Hz = clk_get_rate(clk); -+ -+ rcu_read_lock(); -+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); -+ if (IS_ERR(opp)) { -+ rcu_read_unlock(); -+ pr_err("failed to find OPP for %ld\n", freq_Hz); -+ ret = PTR_ERR(opp); -+ goto out_free_table; -+ } -+ volt = dev_pm_opp_get_voltage(opp); -+ rcu_read_unlock(); -+ -+ tol = volt * voltage_tolerance / 100; -+ ret = regulator_set_voltage_tol(core, volt, tol); -+ if (ret) { -+ pr_err("failed to scale voltage up: %d\n", ret); -+ goto out_free_table; -+ } -+ ret = regulator_enable(core); -+ if (ret) { -+ pr_err("failed to enable regulator: %d\n", ret); -+ goto out_free_table; -+ } -+ max_cpu_freq = max(max_cpu_freq, freq); -+ } -+ -+ for (i = 0; i < nr_krait_l2_points; i++) { -+ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { -+ if (krait_l2_reg) { -+ ret = regulator_set_voltage_tol(krait_l2_reg, -+ krait_l2_points[i].cache_volt, -+ tol); -+ if (ret) -+ pr_err("failed to scale l2 voltage: %d\n", -+ ret); -+ ret = regulator_enable(krait_l2_reg); -+ if (ret) -+ pr_err("failed to enable l2 voltage: %d\n", -+ ret); -+ } -+ break; -+ } -+ -+ } -+ -+ ret = cpufreq_register_driver(&krait_cpufreq_driver); -+ if (ret) { -+ pr_err("failed register driver: %d\n", ret); -+ goto out_free_table; -+ } -+ of_node_put(np); -+ -+ /* -+ * For now, just loading the cooling device; -+ * thermal DT code takes care of matching them. -+ */ -+ for_each_possible_cpu(cpu) { -+ dev = get_cpu_device(cpu); -+ np = of_node_get(dev->of_node); -+ if (of_find_property(np, "#cooling-cells", NULL)) { -+ cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu)); -+ if (IS_ERR(cdev)) -+ pr_err("running cpufreq without cooling device: %ld\n", -+ PTR_ERR(cdev)); -+ } -+ of_node_put(np); -+ } -+ -+ return 0; -+ -+out_free_table: -+ regulator_put(krait_l2_reg); -+ clk_put(krait_l2_clk); -+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -+out_put_node: -+ of_node_put(np); -+ return ret; -+} -+ -+static int krait_cpufreq_remove(struct platform_device *pdev) -+{ -+ cpufreq_cooling_unregister(cdev); -+ cpufreq_unregister_driver(&krait_cpufreq_driver); -+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -+ clk_put(krait_l2_clk); -+ regulator_put(krait_l2_reg); -+ -+ return 0; -+} -+ -+static struct platform_driver krait_cpufreq_platdrv = { -+ .driver = { -+ .name = "cpufreq-krait", -+ .owner = THIS_MODULE, -+ }, -+ .probe = krait_cpufreq_probe, -+ .remove = krait_cpufreq_remove, -+}; -+module_platform_driver(krait_cpufreq_platdrv); -+ -+MODULE_DESCRIPTION("Krait CPUfreq driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/cpufreq/qcom-cpufreq.c -+++ b/drivers/cpufreq/qcom-cpufreq.c -@@ -168,11 +168,8 @@ static int __init qcom_cpufreq_populate_ - - 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), -+ .name = "cpufreq-krait", - }; - struct device *cpu_dev; - struct device_node *np; diff --git a/target/linux/ipq806x/patches-3.18/150-dmaengine-Rework-dma_chan_get.patch b/target/linux/ipq806x/patches-3.18/150-dmaengine-Rework-dma_chan_get.patch deleted file mode 100644 index 880e67c46b..0000000000 --- a/target/linux/ipq806x/patches-3.18/150-dmaengine-Rework-dma_chan_get.patch +++ /dev/null @@ -1,70 +0,0 @@ -From d2f4f99db3e9ec8b063cf2e45704e2bb95428317 Mon Sep 17 00:00:00 2001 -From: Maxime Ripard -Date: Mon, 17 Nov 2014 14:41:58 +0100 -Subject: [PATCH] dmaengine: Rework dma_chan_get - -dma_chan_get uses a rather interesting error handling and code path. - -Change it to something more usual in the kernel. - -Signed-off-by: Maxime Ripard -Acked-by: Laurent Pinchart -Signed-off-by: Vinod Koul ---- - drivers/dma/dmaengine.c | 36 +++++++++++++++++++----------------- - 1 file changed, 19 insertions(+), 17 deletions(-) - ---- a/drivers/dma/dmaengine.c -+++ b/drivers/dma/dmaengine.c -@@ -222,31 +222,33 @@ static void balance_ref_count(struct dma - */ - static int dma_chan_get(struct dma_chan *chan) - { -- int err = -ENODEV; - struct module *owner = dma_chan_to_owner(chan); -+ int ret; - -+ /* The channel is already in use, update client count */ - if (chan->client_count) { - __module_get(owner); -- err = 0; -- } else if (try_module_get(owner)) -- err = 0; -+ goto out; -+ } - -- if (err == 0) -- chan->client_count++; -+ if (!try_module_get(owner)) -+ return -ENODEV; - - /* allocate upon first client reference */ -- if (chan->client_count == 1 && err == 0) { -- int desc_cnt = chan->device->device_alloc_chan_resources(chan); -- -- if (desc_cnt < 0) { -- err = desc_cnt; -- chan->client_count = 0; -- module_put(owner); -- } else if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask)) -- balance_ref_count(chan); -- } -- -- return err; -+ ret = chan->device->device_alloc_chan_resources(chan); -+ if (ret < 0) -+ goto err_out; -+ -+ if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask)) -+ balance_ref_count(chan); -+ -+out: -+ chan->client_count++; -+ return 0; -+ -+err_out: -+ module_put(owner); -+ return ret; - } - - /** diff --git a/target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch b/target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch deleted file mode 100644 index b24a10b911..0000000000 --- a/target/linux/ipq806x/patches-3.18/151-dmaengine-Remove-the-need-to-declare-device_control.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 4f8ef9f4140cc286d7d1cf9237da7a7439e4fc0b Mon Sep 17 00:00:00 2001 -From: Maxime Ripard -Date: Mon, 17 Nov 2014 14:42:03 +0100 -Subject: [PATCH] dmaengine: Remove the need to declare device_control - -In order to migrate the drivers without triggering a BUG_ON for the converted -drivers, which would cause bisectability issues, we need to remove that check -before removing the device_control function entirely. - -Signed-off-by: Maxime Ripard -Acked-by: Laurent Pinchart -Signed-off-by: Vinod Koul ---- - drivers/dma/dmaengine.c | 2 -- - 1 file changed, 2 deletions(-) - ---- a/drivers/dma/dmaengine.c -+++ b/drivers/dma/dmaengine.c -@@ -814,8 +814,6 @@ int dma_async_device_register(struct dma - !device->device_prep_dma_sg); - BUG_ON(dma_has_cap(DMA_CYCLIC, device->cap_mask) && - !device->device_prep_dma_cyclic); -- BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && -- !device->device_control); - BUG_ON(dma_has_cap(DMA_INTERLEAVE, device->cap_mask) && - !device->device_prep_interleaved_dma); - diff --git a/target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch b/target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch deleted file mode 100644 index 63372594d8..0000000000 --- a/target/linux/ipq806x/patches-3.18/152-dmaengine-Make-channel-allocation-callbacks-optional.patch +++ /dev/null @@ -1,62 +0,0 @@ -From c4b54a648e682f678c338619df848233a6babc46 Mon Sep 17 00:00:00 2001 -From: Maxime Ripard -Date: Mon, 17 Nov 2014 14:41:59 +0100 -Subject: [PATCH] dmaengine: Make channel allocation callbacks optional - -Nowadays, some drivers don't have anything in there channel allocation -callbacks anymore. - -Remove the BUG_ON if those callbacks aren't implemented, in order to allow -drivers to not implement them. - -Signed-off-by: Maxime Ripard -Acked-by: Laurent Pinchart -Signed-off-by: Vinod Koul ---- - drivers/dma/dmaengine.c | 18 +++++++++++------- - 1 file changed, 11 insertions(+), 7 deletions(-) - ---- a/drivers/dma/dmaengine.c -+++ b/drivers/dma/dmaengine.c -@@ -235,9 +235,11 @@ static int dma_chan_get(struct dma_chan - return -ENODEV; - - /* allocate upon first client reference */ -- ret = chan->device->device_alloc_chan_resources(chan); -- if (ret < 0) -- goto err_out; -+ if (chan->device->device_alloc_chan_resources) { -+ ret = chan->device->device_alloc_chan_resources(chan); -+ if (ret < 0) -+ goto err_out; -+ } - - if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask)) - balance_ref_count(chan); -@@ -259,11 +261,15 @@ err_out: - */ - static void dma_chan_put(struct dma_chan *chan) - { -+ /* This channel is not in use, bail out */ - if (!chan->client_count) -- return; /* this channel failed alloc_chan_resources */ -+ return; -+ - chan->client_count--; - module_put(dma_chan_to_owner(chan)); -- if (chan->client_count == 0) -+ -+ /* This channel is not in use anymore, free it */ -+ if (!chan->client_count && chan->device->device_free_chan_resources) - chan->device->device_free_chan_resources(chan); - } - -@@ -817,8 +823,6 @@ int dma_async_device_register(struct dma - BUG_ON(dma_has_cap(DMA_INTERLEAVE, device->cap_mask) && - !device->device_prep_interleaved_dma); - -- BUG_ON(!device->device_alloc_chan_resources); -- BUG_ON(!device->device_free_chan_resources); - BUG_ON(!device->device_tx_status); - BUG_ON(!device->device_issue_pending); - BUG_ON(!device->dev); diff --git a/target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch b/target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch deleted file mode 100644 index ced452f8a0..0000000000 --- a/target/linux/ipq806x/patches-3.18/153-dmaengine-Introduce-a-device_config-callback.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 94a73e30dfe6722e9f4ef19f7892901d7d00eab1 Mon Sep 17 00:00:00 2001 -From: Maxime Ripard -Date: Mon, 17 Nov 2014 14:42:00 +0100 -Subject: [PATCH] dmaengine: Introduce a device_config callback - -The fact that the channel configuration is done in device_control is rather -misleading, since it's not really advertised as such, plus, the fact that the -framework exposes a function of its own makes it not really intuitive, while -we're losing the type checking whenever we pass that unsigned long argument. - -Add a device_config callback to dma_device, with a fallback on the old -behaviour for now for existing drivers to opt in. - -Signed-off-by: Maxime Ripard -Acked-by: Laurent Pinchart -Signed-off-by: Vinod Koul ---- - include/linux/dmaengine.h | 8 ++++++++ - 1 file changed, 8 insertions(+) - ---- a/include/linux/dmaengine.h -+++ b/include/linux/dmaengine.h -@@ -607,6 +607,8 @@ struct dma_tx_state { - * The function takes a buffer of size buf_len. The callback function will - * be called after period_len bytes have been transferred. - * @device_prep_interleaved_dma: Transfer expression in a generic way. -+ * @device_config: Pushes a new configuration to a channel, return 0 or an error -+ * code - * @device_control: manipulate all pending operations on a channel, returns - * zero or error code - * @device_tx_status: poll for transaction completion, the optional -@@ -673,6 +675,9 @@ struct dma_device { - struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( - struct dma_chan *chan, struct dma_interleaved_template *xt, - unsigned long flags); -+ -+ int (*device_config)(struct dma_chan *chan, -+ struct dma_slave_config *config); - int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg); - -@@ -696,6 +701,9 @@ static inline int dmaengine_device_contr - static inline int dmaengine_slave_config(struct dma_chan *chan, - struct dma_slave_config *config) - { -+ if (chan->device->device_config) -+ return chan->device->device_config(chan, config); -+ - return dmaengine_device_control(chan, DMA_SLAVE_CONFIG, - (unsigned long)config); - } diff --git a/target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch b/target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch deleted file mode 100644 index 422f7fbe42..0000000000 --- a/target/linux/ipq806x/patches-3.18/154-dmaengine-Add-device_terminate_all-callback.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 7fa0cf462daa6f6121b332b87833d7f5bdb515c0 Mon Sep 17 00:00:00 2001 -From: Maxime Ripard -Date: Mon, 17 Nov 2014 14:42:02 +0100 -Subject: [PATCH] dmaengine: Add device_terminate_all callback - -Split out the terminate_all command from device_control to a dma_device -callback. In order to preserve backward capability, still rely on -device_control if no such callback has been implemented. - -Eventually, this will allow to create a generic dma_slave_caps callback. - -Signed-off-by: Maxime Ripard -Acked-by: Laurent Pinchart -Signed-off-by: Vinod Koul ---- - include/linux/dmaengine.h | 6 ++++++ - 1 file changed, 6 insertions(+) - ---- a/include/linux/dmaengine.h -+++ b/include/linux/dmaengine.h -@@ -611,6 +611,8 @@ struct dma_tx_state { - * code - * @device_control: manipulate all pending operations on a channel, returns - * zero or error code -+ * @device_terminate_all: Aborts all transfers on a channel. Returns 0 -+ * or an error code - * @device_tx_status: poll for transaction completion, the optional - * txstate parameter can be supplied with a pointer to get a - * struct with auxiliary transfer status information, otherwise the call -@@ -680,6 +682,7 @@ struct dma_device { - struct dma_slave_config *config); - int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg); -+ int (*device_terminate_all)(struct dma_chan *chan); - - enum dma_status (*device_tx_status)(struct dma_chan *chan, - dma_cookie_t cookie, -@@ -789,6 +792,9 @@ static inline int dma_get_slave_caps(str - - static inline int dmaengine_terminate_all(struct dma_chan *chan) - { -+ if (chan->device->device_terminate_all) -+ return chan->device->device_terminate_all(chan); -+ - return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); - } - diff --git a/target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch deleted file mode 100644 index 4f5c0efb7b..0000000000 --- a/target/linux/ipq806x/patches-3.18/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch +++ /dev/null @@ -1,76 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers -From: Andy Gross -X-Patchwork-Id: 6027361 -Message-Id: <1426571172-9711-2-git-send-email-agross@codeaurora.org> -To: Vinod Koul -Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, - linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, Kumar Gala , - Bjorn Andersson , - Andy Gross -Date: Tue, 17 Mar 2015 00:46:11 -0500 - -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 - ---- -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: - -@@ -56,7 +52,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"; - }; diff --git a/target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch deleted file mode 100644 index 16d000e8a5..0000000000 --- a/target/linux/ipq806x/patches-3.18/156-dmaengine-Add-ADM-driver.patch +++ /dev/null @@ -1,958 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v6,2/2] dmaengine: Add ADM driver -From: Andy Gross -X-Patchwork-Id: 6027351 -Message-Id: <1426571172-9711-3-git-send-email-agross@codeaurora.org> -To: Vinod Koul -Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, - linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, Kumar Gala , - Bjorn Andersson , - Andy Gross -Date: Tue, 17 Mar 2015 00:46:12 -0500 - -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 -Reviewed-by: sricharan - ---- -drivers/dma/Kconfig | 10 + - drivers/dma/Makefile | 1 + - drivers/dma/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 911 insertions(+) - create mode 100644 drivers/dma/qcom_adm.c - ---- a/drivers/dma/Kconfig -+++ b/drivers/dma/Kconfig -@@ -457,4 +457,14 @@ config QCOM_BAM_DMA - Enable support for the QCOM BAM DMA controller. This controller - provides DMA capabilities for a variety of on-chip devices. - -+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. -+ - endif ---- /dev/null -+++ b/drivers/dma/qcom_adm.c -@@ -0,0 +1,896 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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; -+ 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_NOWAIT); -+ 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 = dma_alloc_writecombine(adev->dev, async_desc->dma_len, -+ &async_desc->dma_addr, GFP_NOWAIT); -+ -+ if (!async_desc->cpl) { -+ kfree(async_desc); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ 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); -+ -+ /* init cmd list */ -+ *cple = ADM_CPLE_LP; -+ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3; -+ -+ 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); -+ } -+ -+ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); -+} -+ -+/** -+ * 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_free_writecombine(async_desc->adev->dev, async_desc->dma_len, -+ async_desc->cpl, async_desc->dma_addr); -+ 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.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 "); -+MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/dma/Makefile -+++ b/drivers/dma/Makefile -@@ -49,3 +49,4 @@ obj-y += xilinx/ - obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o - obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o - obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o -+obj-$(CONFIG_QCOM_ADM) += qcom_adm.o diff --git a/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch deleted file mode 100644 index a6dc89764c..0000000000 --- a/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Wed, 20 May 2015 15:41:07 +0530 -Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node - -This patch adds support for the ADM DMA on the IPQ8064 SOC - -Signed-off-by: Andy Gross ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 4 ++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++ - 2 files changed, 25 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -705,6 +705,26 @@ - }; - }; - -+ adm_dma: dma@18300000 { -+ compatible = "qcom,adm"; -+ reg = <0x18300000 0x100000>; -+ interrupts = <0 170 0>; -+ #dma-cells = <1>; -+ -+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; -+ clock-names = "core", "iface"; -+ -+ resets = <&gcc ADM0_RESET>, -+ <&gcc ADM0_PBUS_RESET>, -+ <&gcc ADM0_C0_RESET>, -+ <&gcc ADM0_C1_RESET>, -+ <&gcc ADM0_C2_RESET>; -+ reset-names = "clk", "pbus", "c0", "c1", "c2"; -+ qcom,ee = <0>; -+ -+ status = "disabled"; -+ }; -+ - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch b/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch deleted file mode 100644 index 77d29d8ac4..0000000000 --- a/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 4c385b25fab119144bffb255ad77712fe586ac10 Mon Sep 17 00:00:00 2001 -From: Archit Taneja -Date: Thu, 2 Apr 2015 11:20:41 +0530 -Subject: [PATCH] clk: qcom: Add EBI2 clocks for IPQ806x - -The NAND controller within EBI2 requires EBI2_CLK and -EBI2_ALWAYS_ON_CLK clocks. Create structs for these clocks so -that they can be used by the NAND controller driver. Add an entry -for EBI2_AON_CLK in the gcc-ipq806x DT binding document. - -Signed-off-by: Archit Taneja -Signed-off-by: Stephen Boyd ---- - drivers/clk/qcom/gcc-ipq806x.c | 32 ++++++++++++++++++++++++++++ - include/dt-bindings/clock/qcom,gcc-ipq806x.h | 1 + - 2 files changed, 33 insertions(+) - ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -2239,6 +2239,36 @@ static struct clk_branch usb_fs1_h_clk = - }, - }; - -+static struct clk_branch ebi2_clk = { -+ .hwcg_reg = 0x3b00, -+ .hwcg_bit = 6, -+ .halt_reg = 0x2fcc, -+ .halt_bit = 1, -+ .clkr = { -+ .enable_reg = 0x3b00, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "ebi2_clk", -+ .ops = &clk_branch_ops, -+ .flags = CLK_IS_ROOT, -+ }, -+ }, -+}; -+ -+static struct clk_branch ebi2_aon_clk = { -+ .halt_reg = 0x2fcc, -+ .halt_bit = 0, -+ .clkr = { -+ .enable_reg = 0x3b00, -+ .enable_mask = BIT(8), -+ .hw.init = &(struct clk_init_data){ -+ .name = "ebi2_always_on_clk", -+ .ops = &clk_branch_ops, -+ .flags = CLK_IS_ROOT, -+ }, -+ }, -+}; -+ - static struct clk_regmap *gcc_ipq806x_clks[] = { - [PLL0] = &pll0.clkr, - [PLL0_VOTE] = &pll0_vote, -@@ -2341,6 +2371,8 @@ static struct clk_regmap *gcc_ipq806x_cl - [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr, - [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr, - [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, -+ [EBI2_CLK] = &ebi2_clk.clkr, -+ [EBI2_AON_CLK] = &ebi2_aon_clk.clkr, - [PLL9] = &hfpll0.clkr, - [PLL10] = &hfpll1.clkr, - [PLL12] = &hfpll_l2.clkr, ---- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h -+++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h -@@ -289,5 +289,6 @@ - #define UBI32_CORE2_CLK_SRC 278 - #define UBI32_CORE1_CLK 279 - #define UBI32_CORE2_CLK 280 -+#define EBI2_AON_CLK 281 - - #endif diff --git a/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch deleted file mode 100644 index 3fb47850fc..0000000000 --- a/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch +++ /dev/null @@ -1,84 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3, - 1/5] mtd: nand: Create a BBT flag to access bad block markers in raw - mode -From: Archit Taneja -X-Patchwork-Id: 6927081 -Message-Id: <1438578498-32254-2-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja -Date: Mon, 3 Aug 2015 10:38:14 +0530 - -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 -Signed-off-by: Archit Taneja - ---- -drivers/mtd/nand/nand_base.c | 6 +++++- - drivers/mtd/nand/nand_bbt.c | 6 +++++- - include/linux/mtd/bbm.h | 7 +++++++ - 3 files changed, 17 insertions(+), 2 deletions(-) - ---- a/drivers/mtd/nand/nand_base.c -+++ b/drivers/mtd/nand/nand_base.c -@@ -396,7 +396,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 -@@ -423,7 +423,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,13 @@ 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. diff --git a/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch deleted file mode 100644 index 6172f7dec8..0000000000 --- a/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch +++ /dev/null @@ -1,2024 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver -From: Archit Taneja -X-Patchwork-Id: 6927101 -Message-Id: <1438578498-32254-3-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja -Date: Mon, 3 Aug 2015 10:38:15 +0530 - -The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx, -MDM9x15 series. - -It exists as a sub block inside the IPs EBI2 (External Bus Interface 2) -and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a -broader interface for external slow peripheral devices such as LCD and -NAND/NOR flash memory or SRAM like interfaces. - -We add support for the NAND controller found within EBI2. For the SoCs -of our interest, we only use the NAND controller within EBI2. Therefore, -it's safe for us to assume that the NAND controller is a standalone block -within the SoC. - -The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND -flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and -16 bit correction/step) and RS ECC(4 bit correction/step) that covers main -and spare data. The controller contains an internal 512 byte page buffer -to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA -for register read/write and data transfers. The controller performs page -reads and writes at a codeword/step level of 512 bytes. It can support up -to 2 external chips of different configurations. - -The driver prepares register read and write configuration descriptors for -each codeword, followed by data descriptors to read or write data from the -controller's internal buffer. It uses a single ADM DMA channel that we get -via dmaengine API. The controller requires 2 ADM CRCIs for command and -data flow control. These are passed via DT. - -The ecc layout used by the controller is syndrome like, but we can't use -the standard syndrome ecc ops because of several reasons. First, the amount -of data bytes covered by ecc isn't same in each step. Second, writing to -free oob space requires us writing to the entire step in which the oob -lies. This forces us to create our own ecc ops. - -One more difference is how the controller accesses the bad block marker. -The controller ignores reading the marker when ECC is enabled. ECC needs -to be explicity disabled to read or write to the bad block marker. For -this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to -read the factory provided bad block markers. - -v3: -- Refactor dma functions for maximum reuse -- Use dma_slave_confing on stack -- optimize and clean upempty_page_fixup using memchr_inv -- ensure portability with dma register reads using le32_* funcs -- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves -- fix handling of return values of dmaengine funcs -- constify wherever possible -- Remove dependency on ADM DMA in Kconfig -- Misc fixes and clean ups - -v2: -- Use new BBT flag that allows us to read BBM in raw mode -- reduce memcpy-s in the driver -- some refactor and clean ups because of above changes - -Reviewed-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -drivers/mtd/nand/Kconfig | 7 + - drivers/mtd/nand/Makefile | 1 + - drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 1921 insertions(+) - create mode 100644 drivers/mtd/nand/qcom_nandc.c - ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -516,4 +516,11 @@ config MTD_NAND_XWAY - Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached - to the External Bus Unit (EBU). - -+config MTD_NAND_QCOM -+ tristate "Support for NAND on QCOM SoCs" -+ depends on ARCH_QCOM -+ help -+ Enables support for NAND flash chips on SoCs containing the EBI2 NAND -+ controller. This controller is found on IPQ806x SoC. -+ - endif # MTD_NAND ---- /dev/null -+++ b/drivers/mtd/nand/qcom_nandc.c -@@ -0,0 +1,1918 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* NANDc reg offsets */ -+#define NAND_FLASH_CMD 0x00 -+#define NAND_ADDR0 0x04 -+#define NAND_ADDR1 0x08 -+#define NAND_FLASH_CHIP_SELECT 0x0c -+#define NAND_EXEC_CMD 0x10 -+#define NAND_FLASH_STATUS 0x14 -+#define NAND_BUFFER_STATUS 0x18 -+#define NAND_DEV0_CFG0 0x20 -+#define NAND_DEV0_CFG1 0x24 -+#define NAND_DEV0_ECC_CFG 0x28 -+#define NAND_DEV1_ECC_CFG 0x2c -+#define NAND_DEV1_CFG0 0x30 -+#define NAND_DEV1_CFG1 0x34 -+#define NAND_READ_ID 0x40 -+#define NAND_READ_STATUS 0x44 -+#define NAND_DEV_CMD0 0xa0 -+#define NAND_DEV_CMD1 0xa4 -+#define NAND_DEV_CMD2 0xa8 -+#define NAND_DEV_CMD_VLD 0xac -+#define SFLASHC_BURST_CFG 0xe0 -+#define NAND_ERASED_CW_DETECT_CFG 0xe8 -+#define NAND_ERASED_CW_DETECT_STATUS 0xec -+#define NAND_EBI2_ECC_BUF_CFG 0xf0 -+#define FLASH_BUF_ACC 0x100 -+ -+#define NAND_CTRL 0xf00 -+#define NAND_VERSION 0xf08 -+#define NAND_READ_LOCATION_0 0xf20 -+#define NAND_READ_LOCATION_1 0xf24 -+ -+/* dummy register offsets, used by write_reg_dma */ -+#define NAND_DEV_CMD1_RESTORE 0xdead -+#define NAND_DEV_CMD_VLD_RESTORE 0xbeef -+ -+/* NAND_FLASH_CMD bits */ -+#define PAGE_ACC BIT(4) -+#define LAST_PAGE BIT(5) -+ -+/* NAND_FLASH_CHIP_SELECT bits */ -+#define NAND_DEV_SEL 0 -+#define DM_EN BIT(2) -+ -+/* NAND_FLASH_STATUS bits */ -+#define FS_OP_ERR BIT(4) -+#define FS_READY_BSY_N BIT(5) -+#define FS_MPU_ERR BIT(8) -+#define FS_DEVICE_STS_ERR BIT(16) -+#define FS_DEVICE_WP BIT(23) -+ -+/* NAND_BUFFER_STATUS bits */ -+#define BS_UNCORRECTABLE_BIT BIT(8) -+#define BS_CORRECTABLE_ERR_MSK 0x1f -+ -+/* NAND_DEVn_CFG0 bits */ -+#define DISABLE_STATUS_AFTER_WRITE 4 -+#define CW_PER_PAGE 6 -+#define UD_SIZE_BYTES 9 -+#define ECC_PARITY_SIZE_BYTES_RS 19 -+#define SPARE_SIZE_BYTES 23 -+#define NUM_ADDR_CYCLES 27 -+#define STATUS_BFR_READ 30 -+#define SET_RD_MODE_AFTER_STATUS 31 -+ -+/* NAND_DEVn_CFG0 bits */ -+#define DEV0_CFG1_ECC_DISABLE 0 -+#define WIDE_FLASH 1 -+#define NAND_RECOVERY_CYCLES 2 -+#define CS_ACTIVE_BSY 5 -+#define BAD_BLOCK_BYTE_NUM 6 -+#define BAD_BLOCK_IN_SPARE_AREA 16 -+#define WR_RD_BSY_GAP 17 -+#define ENABLE_BCH_ECC 27 -+ -+/* NAND_DEV0_ECC_CFG bits */ -+#define ECC_CFG_ECC_DISABLE 0 -+#define ECC_SW_RESET 1 -+#define ECC_MODE 4 -+#define ECC_PARITY_SIZE_BYTES_BCH 8 -+#define ECC_NUM_DATA_BYTES 16 -+#define ECC_FORCE_CLK_OPEN 30 -+ -+/* NAND_DEV_CMD1 bits */ -+#define READ_ADDR 0 -+ -+/* NAND_DEV_CMD_VLD bits */ -+#define READ_START_VLD 0 -+ -+/* NAND_EBI2_ECC_BUF_CFG bits */ -+#define NUM_STEPS 0 -+ -+/* NAND_ERASED_CW_DETECT_CFG bits */ -+#define ERASED_CW_ECC_MASK 1 -+#define AUTO_DETECT_RES 0 -+#define MASK_ECC (1 << ERASED_CW_ECC_MASK) -+#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) -+#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) -+#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) -+#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) -+ -+/* NAND_ERASED_CW_DETECT_STATUS bits */ -+#define PAGE_ALL_ERASED BIT(7) -+#define CODEWORD_ALL_ERASED BIT(6) -+#define PAGE_ERASED BIT(5) -+#define CODEWORD_ERASED BIT(4) -+#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) -+#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) -+ -+/* Version Mask */ -+#define NAND_VERSION_MAJOR_MASK 0xf0000000 -+#define NAND_VERSION_MAJOR_SHIFT 28 -+#define NAND_VERSION_MINOR_MASK 0x0fff0000 -+#define NAND_VERSION_MINOR_SHIFT 16 -+ -+/* NAND OP_CMDs */ -+#define PAGE_READ 0x2 -+#define PAGE_READ_WITH_ECC 0x3 -+#define PAGE_READ_WITH_ECC_SPARE 0x4 -+#define PROGRAM_PAGE 0x6 -+#define PAGE_PROGRAM_WITH_ECC 0x7 -+#define PROGRAM_PAGE_SPARE 0x9 -+#define BLOCK_ERASE 0xa -+#define FETCH_ID 0xb -+#define RESET_DEVICE 0xd -+ -+/* -+ * the NAND controller performs reads/writes with ECC in 516 byte chunks. -+ * the driver calls the chunks 'step' or 'codeword' interchangeably -+ */ -+#define NANDC_STEP_SIZE 512 -+ -+/* -+ * the largest page size we support is 8K, this will have 16 steps/codewords -+ * of 512 bytes each -+ */ -+#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) -+ -+/* we read at most 3 registers per codeword scan */ -+#define MAX_REG_RD (3 * MAX_NUM_STEPS) -+ -+/* ECC modes */ -+#define ECC_NONE BIT(0) -+#define ECC_RS_4BIT BIT(1) -+#define ECC_BCH_4BIT BIT(2) -+#define ECC_BCH_8BIT BIT(3) -+ -+struct desc_info { -+ struct list_head list; -+ -+ enum dma_transfer_direction dir; -+ struct scatterlist sgl; -+ struct dma_async_tx_descriptor *dma_desc; -+}; -+ -+/* -+ * holds the current register values that we want to write. acts as a contiguous -+ * chunk of memory which we use to write the controller registers through DMA. -+ */ -+struct nandc_regs { -+ u32 cmd; -+ u32 addr0; -+ u32 addr1; -+ u32 chip_sel; -+ u32 exec; -+ -+ u32 cfg0; -+ u32 cfg1; -+ u32 ecc_bch_cfg; -+ -+ u32 clrflashstatus; -+ u32 clrreadstatus; -+ -+ u32 cmd1; -+ u32 vld; -+ -+ u32 orig_cmd1; -+ u32 orig_vld; -+ -+ u32 ecc_buf_cfg; -+}; -+ -+/* -+ * @cmd_crci: ADM DMA CRCI for command flow control -+ * @data_crci: ADM DMA CRCI for data flow control -+ * @list: DMA descriptor list (list of desc_infos) -+ * @dma_done: completion param to denote end of last -+ * descriptor in the list -+ * @data_buffer: our local DMA buffer for page read/writes, -+ * used when we can't use the buffer provided -+ * by upper layers directly -+ * @buf_size/count/start: markers for chip->read_buf/write_buf functions -+ * @reg_read_buf: buffer for reading register data via DMA -+ * @reg_read_pos: marker for data read in reg_read_buf -+ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for -+ * ecc/non-ecc mode for the current nand flash -+ * device -+ * @regs: a contiguous chunk of memory for DMA register -+ * writes -+ * @ecc_strength: 4 bit or 8 bit ecc, received via DT -+ * @bus_width: 8 bit or 16 bit NAND bus width, received via DT -+ * @ecc_modes: supported ECC modes by the current controller, -+ * initialized via DT match data -+ * @cw_size: the number of bytes in a single step/codeword -+ * of a page, consisting of all data, ecc, spare -+ * and reserved bytes -+ * @cw_data: the number of bytes within a codeword protected -+ * by ECC -+ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used -+ * @status: value to be returned if NAND_CMD_STATUS command -+ * is executed -+ */ -+struct qcom_nandc_data { -+ struct platform_device *pdev; -+ struct device *dev; -+ -+ void __iomem *base; -+ struct resource *res; -+ -+ struct clk *core_clk; -+ struct clk *aon_clk; -+ -+ /* DMA stuff */ -+ struct dma_chan *chan; -+ struct dma_slave_config slave_conf; -+ unsigned int cmd_crci; -+ unsigned int data_crci; -+ struct list_head list; -+ struct completion dma_done; -+ -+ /* MTD stuff */ -+ struct nand_chip chip; -+ struct mtd_info mtd; -+ -+ /* local data buffer and markers */ -+ u8 *data_buffer; -+ int buf_size; -+ int buf_count; -+ int buf_start; -+ -+ /* local buffer to read back registers */ -+ u32 *reg_read_buf; -+ int reg_read_pos; -+ -+ /* required configs */ -+ u32 cfg0, cfg1; -+ u32 cfg0_raw, cfg1_raw; -+ u32 ecc_buf_cfg; -+ u32 ecc_bch_cfg; -+ u32 clrflashstatus; -+ u32 clrreadstatus; -+ u32 sflashc_burst_cfg; -+ u32 cmd1, vld; -+ -+ /* register state */ -+ struct nandc_regs *regs; -+ -+ /* things we get from DT */ -+ int ecc_strength; -+ int bus_width; -+ -+ u32 ecc_modes; -+ -+ /* misc params */ -+ int cw_size; -+ int cw_data; -+ bool use_ecc; -+ bool bch_enabled; -+ u8 status; -+ int last_command; -+}; -+ -+static inline u32 nandc_read(struct qcom_nandc_data *this, int offset) -+{ -+ return ioread32(this->base + offset); -+} -+ -+static inline void nandc_write(struct qcom_nandc_data *this, int offset, -+ u32 val) -+{ -+ iowrite32(val, this->base + offset); -+} -+ -+/* helper to configure address register values */ -+static void set_address(struct qcom_nandc_data *this, u16 column, int page) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nandc_regs *regs = this->regs; -+ -+ if (chip->options & NAND_BUSWIDTH_16) -+ column >>= 1; -+ -+ regs->addr0 = page << 16 | column; -+ regs->addr1 = page >> 16 & 0xff; -+} -+ -+/* -+ * update_rw_regs: set up read/write register values, these will be -+ * written to the NAND controller registers via DMA -+ * -+ * @num_cw: number of steps for the read/write operation -+ * @read: read or write operation -+ */ -+static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ if (read) { -+ if (this->use_ecc) -+ regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; -+ else -+ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; -+ } else { -+ regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; -+ } -+ -+ if (this->use_ecc) { -+ regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) | -+ (num_cw - 1) << CW_PER_PAGE; -+ -+ regs->cfg1 = this->cfg1; -+ regs->ecc_bch_cfg = this->ecc_bch_cfg; -+ } else { -+ regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) | -+ (num_cw - 1) << CW_PER_PAGE; -+ -+ regs->cfg1 = this->cfg1_raw; -+ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; -+ } -+ -+ regs->ecc_buf_cfg = this->ecc_buf_cfg; -+ regs->clrflashstatus = this->clrflashstatus; -+ regs->clrreadstatus = this->clrreadstatus; -+ regs->exec = 1; -+} -+ -+static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off, -+ const void *vaddr, int size, bool flow_control) -+{ -+ struct desc_info *desc; -+ struct dma_async_tx_descriptor *dma_desc; -+ struct scatterlist *sgl; -+ struct dma_slave_config slave_conf; -+ int r; -+ -+ desc = kzalloc(sizeof(*desc), GFP_KERNEL); -+ if (!desc) -+ return -ENOMEM; -+ -+ list_add_tail(&desc->list, &this->list); -+ -+ sgl = &desc->sgl; -+ -+ sg_init_one(sgl, vaddr, size); -+ -+ desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; -+ -+ r = dma_map_sg(this->dev, sgl, 1, desc->dir); -+ if (r == 0) { -+ r = -ENOMEM; -+ goto err; -+ } -+ -+ memset(&slave_conf, 0x00, sizeof(slave_conf)); -+ -+ slave_conf.device_fc = flow_control; -+ if (read) { -+ slave_conf.src_maxburst = 16; -+ slave_conf.src_addr = this->res->start + reg_off; -+ slave_conf.slave_id = this->data_crci; -+ } else { -+ slave_conf.dst_maxburst = 16; -+ slave_conf.dst_addr = this->res->start + reg_off; -+ slave_conf.slave_id = this->cmd_crci; -+ } -+ -+ r = dmaengine_slave_config(this->chan, &slave_conf); -+ if (r) { -+ dev_err(this->dev, "failed to configure dma channel\n"); -+ goto err; -+ } -+ -+ dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0); -+ if (!dma_desc) { -+ dev_err(this->dev, "failed to prepare desc\n"); -+ r = -EINVAL; -+ goto err; -+ } -+ -+ desc->dma_desc = dma_desc; -+ -+ return 0; -+err: -+ kfree(desc); -+ -+ return r; -+} -+ -+/* -+ * read_reg_dma: prepares a descriptor to read a given number of -+ * contiguous registers to the reg_read_buf pointer -+ * -+ * @first: offset of the first register in the contiguous block -+ * @num_regs: number of registers to read -+ */ -+static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) -+{ -+ bool flow_control = false; -+ void *vaddr; -+ int size; -+ -+ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) -+ flow_control = true; -+ -+ size = num_regs * sizeof(u32); -+ vaddr = this->reg_read_buf + this->reg_read_pos; -+ this->reg_read_pos += num_regs; -+ -+ return prep_dma_desc(this, true, first, vaddr, size, flow_control); -+} -+ -+/* -+ * write_reg_dma: prepares a descriptor to write a given number of -+ * contiguous registers -+ * -+ * @first: offset of the first register in the contiguous block -+ * @num_regs: number of registers to write -+ */ -+static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) -+{ -+ bool flow_control = false; -+ struct nandc_regs *regs = this->regs; -+ void *vaddr; -+ int size; -+ -+ switch (first) { -+ case NAND_FLASH_CMD: -+ vaddr = ®s->cmd; -+ flow_control = true; -+ break; -+ case NAND_EXEC_CMD: -+ vaddr = ®s->exec; -+ break; -+ case NAND_FLASH_STATUS: -+ vaddr = ®s->clrflashstatus; -+ break; -+ case NAND_DEV0_CFG0: -+ vaddr = ®s->cfg0; -+ break; -+ case NAND_READ_STATUS: -+ vaddr = ®s->clrreadstatus; -+ break; -+ case NAND_DEV_CMD1: -+ vaddr = ®s->cmd1; -+ break; -+ case NAND_DEV_CMD1_RESTORE: -+ first = NAND_DEV_CMD1; -+ vaddr = ®s->orig_cmd1; -+ break; -+ case NAND_DEV_CMD_VLD: -+ vaddr = ®s->vld; -+ break; -+ case NAND_DEV_CMD_VLD_RESTORE: -+ first = NAND_DEV_CMD_VLD; -+ vaddr = ®s->orig_vld; -+ break; -+ case NAND_EBI2_ECC_BUF_CFG: -+ vaddr = ®s->ecc_buf_cfg; -+ break; -+ default: -+ dev_err(this->dev, "invalid starting register\n"); -+ return -EINVAL; -+ } -+ -+ size = num_regs * sizeof(u32); -+ -+ return prep_dma_desc(this, false, first, vaddr, size, flow_control); -+} -+ -+/* -+ * read_data_dma: prepares a DMA descriptor to transfer data from the -+ * controller's internal buffer to the buffer 'vaddr' -+ * -+ * @reg_off: offset within the controller's data buffer -+ * @vaddr: virtual address of the buffer we want to write to -+ * @size: DMA transaction size in bytes -+ */ -+static int read_data_dma(struct qcom_nandc_data *this, int reg_off, -+ const u8 *vaddr, int size) -+{ -+ return prep_dma_desc(this, true, reg_off, vaddr, size, false); -+} -+ -+/* -+ * write_data_dma: prepares a DMA descriptor to transfer data from -+ * 'vaddr' to the controller's internal buffer -+ * -+ * @reg_off: offset within the controller's data buffer -+ * @vaddr: virtual address of the buffer we want to read from -+ * @size: DMA transaction size in bytes -+ */ -+static int write_data_dma(struct qcom_nandc_data *this, int reg_off, -+ const u8 *vaddr, int size) -+{ -+ return prep_dma_desc(this, false, reg_off, vaddr, size, false); -+} -+ -+/* -+ * helper to prepare dma descriptors to configure registers needed for reading a -+ * codeword/step in a page -+ */ -+static void config_cw_read(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 3); -+ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); -+ -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 2); -+ read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1); -+} -+ -+/* -+ * helpers to prepare dma descriptors used to configure registers needed for -+ * writing a codeword/step in a page -+ */ -+static void config_cw_write_pre(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 3); -+ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); -+} -+ -+static void config_cw_write_post(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ write_reg_dma(this, NAND_FLASH_STATUS, 1); -+ write_reg_dma(this, NAND_READ_STATUS, 1); -+} -+ -+/* -+ * the following functions are used within chip->cmdfunc() to perform different -+ * NAND_CMD_* commands -+ */ -+ -+/* sets up descriptors for NAND_CMD_PARAM */ -+static int nandc_param(struct qcom_nandc_data *this) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ /* -+ * NAND_CMD_PARAM is called before we know much about the FLASH chip -+ * in use. we configure the controller to perform a raw read of 512 -+ * bytes to read onfi params -+ */ -+ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; -+ regs->addr0 = 0; -+ regs->addr1 = 0; -+ regs->cfg0 = 0 << CW_PER_PAGE -+ | 512 << UD_SIZE_BYTES -+ | 5 << NUM_ADDR_CYCLES -+ | 0 << SPARE_SIZE_BYTES; -+ -+ regs->cfg1 = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | 17 << BAD_BLOCK_BYTE_NUM -+ | 1 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | 0 << WIDE_FLASH -+ | 1 << DEV0_CFG1_ECC_DISABLE; -+ -+ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; -+ -+ /* configure CMD1 and VLD for ONFI param probing */ -+ regs->vld = (this->vld & ~(1 << READ_START_VLD)) -+ | 0 << READ_START_VLD; -+ -+ regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR)) -+ | NAND_CMD_PARAM << READ_ADDR; -+ -+ regs->exec = 1; -+ -+ regs->orig_cmd1 = this->cmd1; -+ regs->orig_vld = this->vld; -+ -+ write_reg_dma(this, NAND_DEV_CMD_VLD, 1); -+ write_reg_dma(this, NAND_DEV_CMD1, 1); -+ -+ this->buf_count = 512; -+ memset(this->data_buffer, 0xff, this->buf_count); -+ -+ config_cw_read(this); -+ -+ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count); -+ -+ /* restore CMD1 and VLD regs */ -+ write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1); -+ write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_ERASE1 */ -+static int erase_block(struct qcom_nandc_data *this, int page_addr) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE; -+ regs->addr0 = page_addr; -+ regs->addr1 = 0; -+ regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE); -+ regs->cfg1 = this->cfg1_raw; -+ regs->exec = 1; -+ regs->clrflashstatus = this->clrflashstatus; -+ regs->clrreadstatus = this->clrreadstatus; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 2); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ write_reg_dma(this, NAND_FLASH_STATUS, 1); -+ write_reg_dma(this, NAND_READ_STATUS, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_READID */ -+static int read_id(struct qcom_nandc_data *this, int column) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ if (column == -1) -+ return 0; -+ -+ regs->cmd = FETCH_ID; -+ regs->addr0 = column; -+ regs->addr1 = 0; -+ regs->chip_sel = DM_EN; -+ regs->exec = 1; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 4); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_READ_ID, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_RESET */ -+static int reset(struct qcom_nandc_data *this) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ regs->cmd = RESET_DEVICE; -+ regs->exec = 1; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 1); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ return 0; -+} -+ -+/* helpers to submit/free our list of dma descriptors */ -+static void dma_callback(void *param) -+{ -+ struct qcom_nandc_data *this = param; -+ struct completion *c = &this->dma_done; -+ -+ complete(c); -+} -+ -+static int submit_descs(struct qcom_nandc_data *this) -+{ -+ struct completion *c = &this->dma_done; -+ struct desc_info *desc; -+ int r; -+ -+ init_completion(c); -+ -+ list_for_each_entry(desc, &this->list, list) { -+ /* -+ * we add a callback to the last descriptor in our list to -+ * notify completion of command -+ */ -+ if (list_is_last(&desc->list, &this->list)) { -+ desc->dma_desc->callback = dma_callback; -+ desc->dma_desc->callback_param = this; -+ } -+ -+ dmaengine_submit(desc->dma_desc); -+ } -+ -+ dma_async_issue_pending(this->chan); -+ -+ r = wait_for_completion_timeout(c, msecs_to_jiffies(500)); -+ if (!r) -+ return -ETIMEDOUT; -+ -+ return 0; -+} -+ -+static void free_descs(struct qcom_nandc_data *this) -+{ -+ struct desc_info *desc, *n; -+ -+ list_for_each_entry_safe(desc, n, &this->list, list) { -+ list_del(&desc->list); -+ dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir); -+ kfree(desc); -+ } -+} -+ -+/* reset the register read buffer for next NAND operation */ -+static void clear_read_regs(struct qcom_nandc_data *this) -+{ -+ this->reg_read_pos = 0; -+ memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf)); -+} -+ -+static void pre_command(struct qcom_nandc_data *this, int command) -+{ -+ this->buf_count = 0; -+ this->buf_start = 0; -+ this->use_ecc = false; -+ this->last_command = command; -+ -+ clear_read_regs(this); -+} -+ -+/* -+ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our -+ * privately maintained status byte, this status byte can be read after -+ * NAND_CMD_STATUS is called -+ */ -+static void parse_erase_write_errors(struct qcom_nandc_data *this, int command) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int num_cw; -+ int i; -+ -+ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; -+ -+ for (i = 0; i < num_cw; i++) { -+ __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]); -+ -+ if (flash_status & FS_MPU_ERR) -+ this->status &= ~NAND_STATUS_WP; -+ -+ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && -+ (flash_status & FS_DEVICE_STS_ERR))) -+ this->status |= NAND_STATUS_FAIL; -+ } -+} -+ -+static void post_command(struct qcom_nandc_data *this, int command) -+{ -+ switch (command) { -+ case NAND_CMD_READID: -+ memcpy(this->data_buffer, this->reg_read_buf, this->buf_count); -+ break; -+ case NAND_CMD_PAGEPROG: -+ case NAND_CMD_ERASE1: -+ parse_erase_write_errors(this, command); -+ break; -+ default: -+ break; -+ } -+} -+ -+/* -+ * Implements chip->cmdfunc. It's only used for a limited set of commands. -+ * The rest of the commands wouldn't be called by upper layers. For example, -+ * NAND_CMD_READOOB would never be called because we have our own versions -+ * of read_oob ops for nand_ecc_ctrl. -+ */ -+static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, -+ int column, int page_addr) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ struct qcom_nandc_data *this = chip->priv; -+ bool wait = false; -+ int r = 0; -+ -+ pre_command(this, command); -+ -+ switch (command) { -+ case NAND_CMD_RESET: -+ r = reset(this); -+ wait = true; -+ break; -+ -+ case NAND_CMD_READID: -+ this->buf_count = 4; -+ r = read_id(this, column); -+ wait = true; -+ break; -+ -+ case NAND_CMD_PARAM: -+ r = nandc_param(this); -+ wait = true; -+ break; -+ -+ case NAND_CMD_ERASE1: -+ r = erase_block(this, page_addr); -+ wait = true; -+ break; -+ -+ case NAND_CMD_READ0: -+ /* we read the entire page for now */ -+ WARN_ON(column != 0); -+ -+ this->use_ecc = true; -+ set_address(this, 0, page_addr); -+ update_rw_regs(this, ecc->steps, true); -+ break; -+ -+ case NAND_CMD_SEQIN: -+ WARN_ON(column != 0); -+ set_address(this, 0, page_addr); -+ break; -+ -+ case NAND_CMD_PAGEPROG: -+ case NAND_CMD_STATUS: -+ case NAND_CMD_NONE: -+ default: -+ break; -+ } -+ -+ if (r) { -+ dev_err(this->dev, "failure executing command %d\n", -+ command); -+ free_descs(this); -+ return; -+ } -+ -+ if (wait) { -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, -+ "failure submitting descs for command %d\n", -+ command); -+ } -+ -+ free_descs(this); -+ -+ post_command(this, command); -+} -+ -+/* -+ * when using RS ECC, the NAND controller flags an error when reading an -+ * erased page. however, there are special characters at certain offsets when -+ * we read the erased page. we check here if the page is really empty. if so, -+ * we replace the magic characters with 0xffs -+ */ -+static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = ecc->steps; -+ u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS]; -+ int i, j; -+ -+ /* if BCH is enabled, HW will take care of detecting erased pages */ -+ if (this->bch_enabled || !this->use_ecc) -+ return false; -+ -+ for (i = 0; i < cwperpage; i++) { -+ u8 *empty1, *empty2; -+ __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]); -+ -+ /* -+ * an erased page flags an error in NAND_FLASH_STATUS, check if -+ * the page is erased by looking for 0x54s at offsets 3 and 175 -+ * from the beginning of each codeword -+ */ -+ if (!(flash_status & FS_OP_ERR)) -+ break; -+ -+ empty1 = &data_buf[3 + i * this->cw_data]; -+ empty2 = &data_buf[175 + i * this->cw_data]; -+ -+ /* -+ * if the error wasn't because of an erased page, bail out and -+ * and let someone else do the error checking -+ */ -+ if ((*empty1 == 0x54 && *empty2 == 0xff) || -+ (*empty1 == 0xff && *empty2 == 0x54)) { -+ orig1[i] = *empty1; -+ orig2[i] = *empty2; -+ -+ *empty1 = 0xff; -+ *empty2 = 0xff; -+ } else { -+ break; -+ } -+ } -+ -+ if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize)) -+ goto not_empty; -+ -+ /* -+ * tell the caller that the page was empty and is fixed up, so that -+ * parse_read_errors() doesn't think it's an error -+ */ -+ return true; -+ -+not_empty: -+ /* restore original values if not empty*/ -+ for (j = 0; j < i; j++) { -+ data_buf[3 + j * this->cw_data] = orig1[j]; -+ data_buf[175 + j * this->cw_data] = orig2[j]; -+ } -+ -+ return false; -+} -+ -+struct read_stats { -+ __le32 flash; -+ __le32 buffer; -+ __le32 erased_cw; -+}; -+ -+/* -+ * reads back status registers set by the controller to notify page read -+ * errors. this is equivalent to what 'ecc->correct()' would do. -+ */ -+static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = ecc->steps; -+ unsigned int max_bitflips = 0; -+ int i; -+ -+ for (i = 0; i < cwperpage; i++) { -+ int stat; -+ struct read_stats *buf; -+ -+ buf = (struct read_stats *) (this->reg_read_buf + 3 * i); -+ -+ buf->flash = le32_to_cpu(buf->flash); -+ buf->buffer = le32_to_cpu(buf->buffer); -+ buf->erased_cw = le32_to_cpu(buf->erased_cw); -+ -+ if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) { -+ -+ /* ignore erased codeword errors */ -+ if (this->bch_enabled) { -+ if ((buf->erased_cw & ERASED_CW) == ERASED_CW) -+ continue; -+ } else if (erased_page) { -+ continue; -+ } -+ -+ if (buf->buffer & BS_UNCORRECTABLE_BIT) { -+ mtd->ecc_stats.failed++; -+ continue; -+ } -+ } -+ -+ stat = buf->buffer & BS_CORRECTABLE_ERR_MSK; -+ mtd->ecc_stats.corrected += stat; -+ -+ max_bitflips = max_t(unsigned int, max_bitflips, stat); -+ } -+ -+ return max_bitflips; -+} -+ -+/* -+ * helper to perform the actual page read operation, used by ecc->read_page() -+ * and ecc->read_oob() -+ */ -+static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf, -+ u8 *oob_buf) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int i, r; -+ -+ /* queue cmd descs for each codeword */ -+ for (i = 0; i < ecc->steps; i++) { -+ int data_size, oob_size; -+ -+ if (i == (ecc->steps - 1)) { -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ } else { -+ data_size = this->cw_data; -+ oob_size = ecc->bytes; -+ } -+ -+ config_cw_read(this); -+ -+ if (data_buf) -+ read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); -+ -+ if (oob_buf) -+ read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf, -+ oob_size); -+ -+ if (data_buf) -+ data_buf += data_size; -+ if (oob_buf) -+ oob_buf += oob_size; -+ } -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failure to read page/oob\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* -+ * a helper that copies the last step/codeword of a page (containing free oob) -+ * into our local buffer -+ */ -+static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int size; -+ int r; -+ -+ clear_read_regs(this); -+ -+ size = use_ecc ? this->cw_data : this->cw_size; -+ -+ /* prepare a clean read buffer */ -+ memset(this->data_buffer, 0xff, size); -+ -+ this->use_ecc = use_ecc; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, true); -+ -+ config_cw_read(this); -+ -+ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size); -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failed to copy last codeword\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* implements ecc->read_page() */ -+static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, -+ uint8_t *buf, int oob_required, int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ u8 *data_buf, *oob_buf = NULL; -+ bool erased_page; -+ int r; -+ -+ data_buf = buf; -+ oob_buf = oob_required ? chip->oob_poi : NULL; -+ -+ r = read_page_low(this, data_buf, oob_buf); -+ if (r) { -+ dev_err(this->dev, "failure to read page\n"); -+ return r; -+ } -+ -+ erased_page = empty_page_fixup(this, data_buf); -+ -+ return parse_read_errors(this, erased_page); -+} -+ -+/* implements ecc->read_oob() */ -+static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int r; -+ -+ clear_read_regs(this); -+ -+ this->use_ecc = true; -+ set_address(this, 0, page); -+ update_rw_regs(this, ecc->steps, true); -+ -+ r = read_page_low(this, NULL, chip->oob_poi); -+ if (r) -+ dev_err(this->dev, "failure to read oob\n"); -+ -+ return r; -+} -+ -+/* implements ecc->read_oob_raw(), used to read the bad block marker flag */ -+static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int start, length; -+ int r; -+ -+ /* -+ * configure registers for a raw page read, the address is set to the -+ * beginning of the last codeword, we don't care about reading ecc -+ * portion of oob, just the free stuff -+ */ -+ r = copy_last_cw(this, false, page); -+ if (r) -+ return r; -+ -+ /* -+ * reading raw oob has 2 parts, first the bad block byte, then the -+ * actual free oob region. perform a memcpy in two steps -+ */ -+ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); -+ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; -+ -+ memcpy(oob, this->data_buffer + start, length); -+ -+ oob += length; -+ -+ start = this->cw_data - (ecc->steps << 2) + 1; -+ length = ecc->steps << 2; -+ -+ memcpy(oob, this->data_buffer + start, length); -+ -+ return 0; -+} -+ -+/* implements ecc->write_page() */ -+static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, -+ const uint8_t *buf, int oob_required) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ u8 *data_buf, *oob_buf; -+ int i, r = 0; -+ -+ clear_read_regs(this); -+ -+ data_buf = (u8 *) buf; -+ oob_buf = chip->oob_poi; -+ -+ this->use_ecc = true; -+ update_rw_regs(this, ecc->steps, false); -+ -+ for (i = 0; i < ecc->steps; i++) { -+ int data_size, oob_size; -+ -+ if (i == (ecc->steps - 1)) { -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ } else { -+ data_size = this->cw_data; -+ oob_size = ecc->bytes; -+ } -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); -+ -+ /* -+ * we don't really need to write anything to oob for the -+ * first n - 1 codewords since these oob regions just -+ * contain ecc that's written by the controller itself -+ */ -+ if (i == (ecc->steps - 1)) -+ write_data_dma(this, FLASH_BUF_ACC + data_size, -+ oob_buf, oob_size); -+ config_cw_write_post(this); -+ -+ data_buf += data_size; -+ oob_buf += oob_size; -+ } -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failure to write page\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* -+ * implements ecc->write_oob() -+ * -+ * the NAND controller cannot write only data or only oob within a codeword, -+ * since ecc is calculated for the combined codeword. we first copy the -+ * entire contents for the last codeword(data + oob), replace the old oob -+ * with the new one in chip->oob_poi, and then write the entire codeword. -+ * this read-copy-write operation results in a slight perormance loss. -+ */ -+static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int free_boff; -+ int data_size, oob_size; -+ int r, status = 0; -+ -+ r = copy_last_cw(this, true, page); -+ if (r) -+ return r; -+ -+ clear_read_regs(this); -+ -+ /* calculate the data and oob size for the last codeword/step */ -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ -+ /* -+ * the location of spare data in the oob buffer, we could also use -+ * ecc->layout.oobfree here -+ */ -+ free_boff = ecc->bytes * (ecc->steps - 1); -+ -+ /* override new oob content to last codeword */ -+ memcpy(this->data_buffer + data_size, oob + free_boff, oob_size); -+ -+ this->use_ecc = true; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, false); -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, -+ data_size + oob_size); -+ config_cw_write_post(this); -+ -+ r = submit_descs(this); -+ -+ free_descs(this); -+ -+ if (r) { -+ dev_err(this->dev, "failure to write oob\n"); -+ return -EIO; -+ } -+ -+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); -+ -+ status = chip->waitfunc(mtd, chip); -+ -+ return status & NAND_STATUS_FAIL ? -EIO : 0; -+} -+ -+/* implements ecc->write_oob_raw(), used to write bad block marker flag */ -+static int qcom_nandc_write_oob_raw(struct mtd_info *mtd, -+ struct nand_chip *chip, int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int start, length; -+ int r, status = 0; -+ -+ r = copy_last_cw(this, false, page); -+ if (r) -+ return r; -+ -+ clear_read_regs(this); -+ -+ /* -+ * writing raw oob has 2 parts, first the bad block region, then the -+ * actual free region -+ */ -+ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); -+ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; -+ -+ memcpy(this->data_buffer + start, oob, length); -+ -+ oob += length; -+ -+ start = this->cw_data - (ecc->steps << 2) + 1; -+ length = ecc->steps << 2; -+ -+ memcpy(this->data_buffer + start, oob, length); -+ -+ /* prepare write */ -+ this->use_ecc = false; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, false); -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size); -+ config_cw_write_post(this); -+ -+ r = submit_descs(this); -+ -+ free_descs(this); -+ -+ if (r) { -+ dev_err(this->dev, "failure to write updated oob\n"); -+ return -EIO; -+ } -+ -+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); -+ -+ status = chip->waitfunc(mtd, chip); -+ -+ return status & NAND_STATUS_FAIL ? -EIO : 0; -+} -+ -+/* -+ * the three functions below implement chip->read_byte(), chip->read_buf() -+ * and chip->write_buf() respectively. these aren't used for -+ * reading/writing page data, they are used for smaller data like reading -+ * id, status etc -+ */ -+static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ uint8_t *buf = this->data_buffer; -+ uint8_t ret = 0x0; -+ -+ if (this->last_command == NAND_CMD_STATUS) { -+ ret = this->status; -+ -+ this->status = NAND_STATUS_READY | NAND_STATUS_WP; -+ -+ return ret; -+ } -+ -+ if (this->buf_start < this->buf_count) -+ ret = buf[this->buf_start++]; -+ -+ return ret; -+} -+ -+static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); -+ -+ memcpy(buf, this->data_buffer + this->buf_start, real_len); -+ this->buf_start += real_len; -+} -+ -+static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, -+ int len) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); -+ -+ memcpy(this->data_buffer + this->buf_start, buf, real_len); -+ -+ this->buf_start += real_len; -+} -+ -+/* we support only one external chip for now */ -+static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ -+ if (chipnr <= 0) -+ return; -+ -+ dev_warn(this->dev, "invalid chip select\n"); -+} -+ -+/* -+ * NAND controller page layout info -+ * -+ * |-----------------------| |---------------------------------| -+ * | xx.......xx| | *********xx.......xx| -+ * | DATA xx..ECC..xx| | DATA **SPARE**xx..ECC..xx| -+ * | (516) xx.......xx| | (516-n*4) **(n*4)**xx.......xx| -+ * | xx.......xx| | *********xx.......xx| -+ * |-----------------------| |---------------------------------| -+ * codeword 1,2..n-1 codeword n -+ * <---(528/532 Bytes)----> <-------(528/532 Bytes)----------> -+ * -+ * n = number of codewords in the page -+ * . = ECC bytes -+ * * = spare bytes -+ * x = unused/reserved bytes -+ * -+ * 2K page: n = 4, spare = 16 bytes -+ * 4K page: n = 8, spare = 32 bytes -+ * 8K page: n = 16, spare = 64 bytes -+ * -+ * the qcom nand controller operates at a sub page/codeword level. each -+ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. -+ * the number of ECC bytes vary based on the ECC strength and the bus width. -+ * -+ * the first n - 1 codewords contains 516 bytes of user data, the remaining -+ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains -+ * both user data and spare(oobavail) bytes that sum up to 516 bytes. -+ * -+ * the layout described above is used by the controller when the ECC block is -+ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are -+ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout -+ * layouts defined below doesn't consider the positions occupied by the reserved -+ * bytes -+ * -+ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width) -+ * in the last codeword is the position of bad block marker. the bad block -+ * marker cannot be accessed when ECC is enabled. -+ * -+ */ -+ -+/* -+ * Layouts for different page sizes and ecc modes. We skip the eccpos field -+ * since it isn't needed for this driver -+ */ -+ -+/* 2K page, 4 bit ECC */ -+static struct nand_ecclayout layout_oob_64 = { -+ .eccbytes = 40, -+ .oobfree = { -+ { 30, 16 }, -+ }, -+}; -+ -+/* 4K page, 4 bit ECC, 8/16 bit bus width */ -+static struct nand_ecclayout layout_oob_128 = { -+ .eccbytes = 80, -+ .oobfree = { -+ { 70, 32 }, -+ }, -+}; -+ -+/* 4K page, 8 bit ECC, 8 bit bus width */ -+static struct nand_ecclayout layout_oob_224_x8 = { -+ .eccbytes = 104, -+ .oobfree = { -+ { 91, 32 }, -+ }, -+}; -+ -+/* 4K page, 8 bit ECC, 16 bit bus width */ -+static struct nand_ecclayout layout_oob_224_x16 = { -+ .eccbytes = 112, -+ .oobfree = { -+ { 98, 32 }, -+ }, -+}; -+ -+/* 8K page, 4 bit ECC, 8/16 bit bus width */ -+static struct nand_ecclayout layout_oob_256 = { -+ .eccbytes = 160, -+ .oobfree = { -+ { 151, 64 }, -+ }, -+}; -+ -+/* -+ * this is called before scan_ident, we do some minimal configurations so -+ * that reading ID and ONFI params work -+ */ -+static void qcom_nandc_pre_init(struct qcom_nandc_data *this) -+{ -+ /* kill onenand */ -+ nandc_write(this, SFLASHC_BURST_CFG, 0); -+ -+ /* enable ADM DMA */ -+ nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN); -+ -+ /* save the original values of these registers */ -+ this->cmd1 = nandc_read(this, NAND_DEV_CMD1); -+ this->vld = nandc_read(this, NAND_DEV_CMD_VLD); -+ -+ /* initial status value */ -+ this->status = NAND_STATUS_READY | NAND_STATUS_WP; -+} -+ -+static int qcom_nandc_ecc_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage; -+ bool wide_bus; -+ -+ /* the nand controller fetches codewords/chunks of 512 bytes */ -+ cwperpage = mtd->writesize >> 9; -+ -+ ecc->strength = this->ecc_strength; -+ -+ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; -+ -+ if (ecc->strength >= 8) { -+ /* 8 bit ECC defaults to BCH ECC on all platforms */ -+ ecc->bytes = wide_bus ? 14 : 13; -+ } else { -+ /* -+ * if the controller supports BCH for 4 bit ECC, the controller -+ * uses lesser bytes for ECC. If RS is used, the ECC bytes is -+ * always 10 bytes -+ */ -+ if (this->ecc_modes & ECC_BCH_4BIT) -+ ecc->bytes = wide_bus ? 8 : 7; -+ else -+ ecc->bytes = 10; -+ } -+ -+ /* each step consists of 512 bytes of data */ -+ ecc->size = NANDC_STEP_SIZE; -+ -+ ecc->read_page = qcom_nandc_read_page; -+ ecc->read_oob = qcom_nandc_read_oob; -+ ecc->write_page = qcom_nandc_write_page; -+ ecc->write_oob = qcom_nandc_write_oob; -+ -+ /* -+ * the bad block marker is readable only when we read the page with ECC -+ * disabled. all the ops above run with ECC enabled. We need raw read -+ * and write function for oob in order to access bad block marker. -+ */ -+ ecc->read_oob_raw = qcom_nandc_read_oob_raw; -+ ecc->write_oob_raw = qcom_nandc_write_oob_raw; -+ -+ switch (mtd->oobsize) { -+ case 64: -+ ecc->layout = &layout_oob_64; -+ break; -+ case 128: -+ ecc->layout = &layout_oob_128; -+ break; -+ case 224: -+ if (wide_bus) -+ ecc->layout = &layout_oob_224_x16; -+ else -+ ecc->layout = &layout_oob_224_x8; -+ break; -+ case 256: -+ ecc->layout = &layout_oob_256; -+ break; -+ default: -+ dev_err(this->dev, "unsupported NAND device, oobsize %d\n", -+ mtd->oobsize); -+ return -ENODEV; -+ } -+ -+ ecc->mode = NAND_ECC_HW; -+ -+ /* enable ecc by default */ -+ this->use_ecc = true; -+ -+ return 0; -+} -+ -+static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = mtd->writesize / ecc->size; -+ int spare_bytes, bad_block_byte; -+ bool wide_bus; -+ int ecc_mode = 0; -+ -+ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; -+ -+ if (ecc->strength >= 8) { -+ this->cw_size = 532; -+ -+ spare_bytes = wide_bus ? 0 : 2; -+ -+ this->bch_enabled = true; -+ ecc_mode = 1; -+ } else { -+ this->cw_size = 528; -+ -+ if (this->ecc_modes & ECC_BCH_4BIT) { -+ spare_bytes = wide_bus ? 2 : 4; -+ -+ this->bch_enabled = true; -+ ecc_mode = 0; -+ } else { -+ spare_bytes = wide_bus ? 0 : 1; -+ } -+ } -+ -+ /* -+ * DATA_UD_BYTES varies based on whether the read/write command protects -+ * spare data with ECC too. We protect spare data by default, so we set -+ * it to main + spare data, which are 512 and 4 bytes respectively. -+ */ -+ this->cw_data = 516; -+ -+ bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1; -+ -+ this->cfg0 = (cwperpage - 1) << CW_PER_PAGE -+ | this->cw_data << UD_SIZE_BYTES -+ | 0 << DISABLE_STATUS_AFTER_WRITE -+ | 5 << NUM_ADDR_CYCLES -+ | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS -+ | 0 << STATUS_BFR_READ -+ | 1 << SET_RD_MODE_AFTER_STATUS -+ | spare_bytes << SPARE_SIZE_BYTES; -+ -+ this->cfg1 = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | bad_block_byte << BAD_BLOCK_BYTE_NUM -+ | 0 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | wide_bus << WIDE_FLASH -+ | this->bch_enabled << ENABLE_BCH_ECC; -+ -+ this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE -+ | this->cw_size << UD_SIZE_BYTES -+ | 5 << NUM_ADDR_CYCLES -+ | 0 << SPARE_SIZE_BYTES; -+ -+ this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | 17 << BAD_BLOCK_BYTE_NUM -+ | 1 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | wide_bus << WIDE_FLASH -+ | 1 << DEV0_CFG1_ECC_DISABLE; -+ -+ this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE -+ | 0 << ECC_SW_RESET -+ | this->cw_data << ECC_NUM_DATA_BYTES -+ | 1 << ECC_FORCE_CLK_OPEN -+ | ecc_mode << ECC_MODE -+ | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH; -+ -+ this->ecc_buf_cfg = 0x203 << NUM_STEPS; -+ -+ this->clrflashstatus = FS_READY_BSY_N; -+ this->clrreadstatus = 0xc0; -+ -+ dev_dbg(this->dev, -+ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", -+ this->cfg0, this->cfg1, this->ecc_buf_cfg, -+ this->ecc_bch_cfg, this->cw_size, this->cw_data, -+ ecc->strength, ecc->bytes, cwperpage); -+} -+ -+static int qcom_nandc_alloc(struct qcom_nandc_data *this) -+{ -+ int r; -+ -+ r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32)); -+ if (r) { -+ dev_err(this->dev, "failed to set DMA mask\n"); -+ return r; -+ } -+ -+ /* -+ * we use the internal buffer for reading ONFI params, reading small -+ * data like ID and status, and preforming read-copy-write operations -+ * when writing to a codeword partially. 532 is the maximum possible -+ * size of a codeword for our nand controller -+ */ -+ this->buf_size = 532; -+ -+ this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL); -+ if (!this->data_buffer) -+ return -ENOMEM; -+ -+ this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL); -+ if (!this->regs) -+ return -ENOMEM; -+ -+ this->reg_read_buf = devm_kzalloc(this->dev, -+ MAX_REG_RD * sizeof(*this->reg_read_buf), -+ GFP_KERNEL); -+ if (!this->reg_read_buf) -+ return -ENOMEM; -+ -+ INIT_LIST_HEAD(&this->list); -+ -+ this->chan = dma_request_slave_channel(this->dev, "rxtx"); -+ if (!this->chan) { -+ dev_err(this->dev, "failed to request slave channel\n"); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static void qcom_nandc_unalloc(struct qcom_nandc_data *this) -+{ -+ dma_release_channel(this->chan); -+} -+ -+static int qcom_nandc_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct device_node *np = this->dev->of_node; -+ struct mtd_part_parser_data ppdata = { .of_node = np }; -+ int r; -+ -+ mtd->priv = chip; -+ mtd->name = "qcom-nandc"; -+ mtd->owner = THIS_MODULE; -+ -+ chip->priv = this; -+ -+ chip->cmdfunc = qcom_nandc_command; -+ chip->select_chip = qcom_nandc_select_chip; -+ chip->read_byte = qcom_nandc_read_byte; -+ chip->read_buf = qcom_nandc_read_buf; -+ chip->write_buf = qcom_nandc_write_buf; -+ -+ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; -+ if (this->bus_width == 16) -+ chip->options |= NAND_BUSWIDTH_16; -+ -+ chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW; -+ if (of_get_nand_on_flash_bbt(np)) -+ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; -+ -+ qcom_nandc_pre_init(this); -+ -+ r = nand_scan_ident(mtd, 1, NULL); -+ if (r) -+ return r; -+ -+ r = qcom_nandc_ecc_init(this); -+ if (r) -+ return r; -+ -+ qcom_nandc_hw_post_init(this); -+ -+ r = nand_scan_tail(mtd); -+ if (r) -+ return r; -+ -+ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); -+} -+ -+static int qcom_nandc_parse_dt(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this = platform_get_drvdata(pdev); -+ struct device_node *np = this->dev->of_node; -+ int r; -+ -+ this->ecc_strength = of_get_nand_ecc_strength(np); -+ if (this->ecc_strength < 0) { -+ dev_warn(this->dev, -+ "incorrect ecc strength, setting to 4 bits/step\n"); -+ this->ecc_strength = 4; -+ } -+ -+ this->bus_width = of_get_nand_bus_width(np); -+ if (this->bus_width < 0) { -+ dev_warn(this->dev, "incorrect bus width, setting to 8\n"); -+ this->bus_width = 8; -+ } -+ -+ r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci); -+ if (r) { -+ dev_err(this->dev, "command CRCI unspecified\n"); -+ return r; -+ } -+ -+ r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci); -+ if (r) { -+ dev_err(this->dev, "data CRCI unspecified\n"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+static int qcom_nandc_probe(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this; -+ const struct of_device_id *match; -+ int r; -+ -+ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); -+ if (!this) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, this); -+ -+ this->pdev = pdev; -+ this->dev = &pdev->dev; -+ -+ match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); -+ if (!match) { -+ dev_err(&pdev->dev, "failed to match device\n"); -+ return -ENODEV; -+ } -+ -+ if (!match->data) { -+ dev_err(&pdev->dev, "failed to get device data\n"); -+ return -ENODEV; -+ } -+ -+ this->ecc_modes = (u32) match->data; -+ -+ this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ this->base = devm_ioremap_resource(&pdev->dev, this->res); -+ if (IS_ERR(this->base)) -+ return PTR_ERR(this->base); -+ -+ this->core_clk = devm_clk_get(&pdev->dev, "core"); -+ if (IS_ERR(this->core_clk)) -+ return PTR_ERR(this->core_clk); -+ -+ this->aon_clk = devm_clk_get(&pdev->dev, "aon"); -+ if (IS_ERR(this->aon_clk)) -+ return PTR_ERR(this->aon_clk); -+ -+ r = qcom_nandc_parse_dt(pdev); -+ if (r) -+ return r; -+ -+ r = qcom_nandc_alloc(this); -+ if (r) -+ return r; -+ -+ r = clk_prepare_enable(this->core_clk); -+ if (r) -+ goto err_core_clk; -+ -+ r = clk_prepare_enable(this->aon_clk); -+ if (r) -+ goto err_aon_clk; -+ -+ r = qcom_nandc_init(this); -+ if (r) -+ goto err_init; -+ -+ return 0; -+ -+err_init: -+ clk_disable_unprepare(this->aon_clk); -+err_aon_clk: -+ clk_disable_unprepare(this->core_clk); -+err_core_clk: -+ qcom_nandc_unalloc(this); -+ -+ return r; -+} -+ -+static int qcom_nandc_remove(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this = platform_get_drvdata(pdev); -+ -+ qcom_nandc_unalloc(this); -+ -+ clk_disable_unprepare(this->aon_clk); -+ clk_disable_unprepare(this->core_clk); -+ -+ return 0; -+} -+ -+#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT) -+ -+/* -+ * data will hold a struct pointer containing more differences once we support -+ * more IPs -+ */ -+static const struct of_device_id qcom_nandc_of_match[] = { -+ { .compatible = "qcom,ebi2-nandc", -+ .data = (void *) EBI2_NANDC_ECC_MODES, -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); -+ -+static struct platform_driver qcom_nandc_driver = { -+ .driver = { -+ .name = "qcom-nandc", -+ .of_match_table = qcom_nandc_of_match, -+ }, -+ .probe = qcom_nandc_probe, -+ .remove = qcom_nandc_remove, -+}; -+module_platform_driver(qcom_nandc_driver); -+ -+MODULE_AUTHOR("Archit Taneja "); -+MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/mtd/nand/Makefile -+++ b/drivers/mtd/nand/Makefile -@@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740 - obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ - obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o - obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ -+obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o - - nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch deleted file mode 100644 index 6530eb1c86..0000000000 --- a/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch +++ /dev/null @@ -1,82 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings -From: Archit Taneja -X-Patchwork-Id: 6927141 -Message-Id: <1438578498-32254-4-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:16 +0530 - -Add DT bindings document for the Qualcomm NAND controller driver. - -Cc: devicetree@vger.kernel.org - -v3: -- Don't use '0x' when specifying nand controller address space -- Add optional property for on-flash bbt usage - -Acked-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -.../devicetree/bindings/mtd/qcom_nandc.txt | 49 ++++++++++++++++++++++ - 1 file changed, 49 insertions(+) - create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt -@@ -0,0 +1,49 @@ -+* Qualcomm NAND controller -+ -+Required properties: -+- compatible: should be "qcom,ebi2-nand" for IPQ806x -+- reg: MMIO address range -+- clocks: must contain core clock and always on clock -+- clock-names: must contain "core" for the core clock and "aon" for the -+ always on clock -+- dmas: DMA specifier, consisting of a phandle to the ADM DMA -+ controller node and the channel number to be used for -+ NAND. Refer to dma.txt and qcom_adm.txt for more details -+- dma-names: must be "rxtx" -+- qcom,cmd-crci: must contain the ADM command type CRCI block instance -+ number specified for the NAND controller on the given -+ platform -+- qcom,data-crci: must contain the ADM data type CRCI block instance -+ number specified for the NAND controller on the given -+ platform -+ -+Optional properties: -+- nand-bus-width: bus width. Must be 8 or 16. If not present, 8 is chosen -+ as default -+ -+- nand-ecc-strength: number of bits to correct per ECC step. Must be 4 or 8 -+ bits. If not present, 4 is chosen as default -+- nand-on-flash-bbt: Create/use on-flash bad block table -+ -+The device tree may optionally contain sub-nodes describing partitions of the -+address space. See partition.txt for more detail. -+ -+Example: -+ -+nand@1ac00000 { -+ compatible = "qcom,ebi2-nandc"; -+ reg = <0x1ac00000 0x800>; -+ -+ clocks = <&gcc EBI2_CLK>, -+ <&gcc EBI2_AON_CLK>; -+ clock-names = "core", "aon"; -+ -+ dmas = <&adm_dma 3>; -+ dma-names = "rxtx"; -+ qcom,cmd-crci = <15>; -+ qcom,data-crci = <3>; -+ -+ partition@0 { -+ ... -+ }; -+}; diff --git a/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch deleted file mode 100644 index 6a8ec4ac4b..0000000000 --- a/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch +++ /dev/null @@ -1,51 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x -From: Archit Taneja -X-Patchwork-Id: 6927121 -Message-Id: <1438578498-32254-5-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:17 +0530 - -The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding -compatible string. - -Cc: devicetree@vger.kernel.org - -Reviewed-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -725,6 +725,22 @@ - status = "disabled"; - }; - -+ nand@1ac00000 { -+ compatible = "qcom,ebi2-nandc"; -+ reg = <0x1ac00000 0x800>; -+ -+ clocks = <&gcc EBI2_CLK>, -+ <&gcc EBI2_AON_CLK>; -+ clock-names = "core", "aon"; -+ -+ dmas = <&adm_dma 3>; -+ dma-names = "rxtx"; -+ qcom,cmd-crci = <15>; -+ qcom,data-crci = <3>; -+ -+ status = "disabled"; -+ }; -+ - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch deleted file mode 100644 index 9c7c3a3af0..0000000000 --- a/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch +++ /dev/null @@ -1,79 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform -From: Archit Taneja -X-Patchwork-Id: 6927091 -Message-Id: <1438578498-32254-6-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:18 +0530 - -Enable the NAND controller node on the AP148 platform. Provide pinmux -information. - -Cc: devicetree@vger.kernel.org - -Signed-off-by: Archit Taneja - ---- -arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++ - 1 file changed, 36 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -43,6 +43,31 @@ - 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; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -125,5 +150,19 @@ - status = "ok"; - phy-tx0-term-offset = <7>; - }; -+ -+ nand@1ac00000 { -+ status = "ok"; -+ -+ pinctrl-0 = <&nand_pins>; -+ pinctrl-names = "default"; -+ -+ nand-ecc-strength = <4>; -+ nand-bus-width = <8>; -+ }; - }; - }; -+ -+&adm_dma { -+ status = "ok"; -+}; diff --git a/target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch deleted file mode 100644 index 1db21e4edc..0000000000 --- a/target/linux/ipq806x/patches-3.18/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -159,6 +159,8 @@ - - nand-ecc-strength = <4>; - nand-bus-width = <8>; -+ -+ linux,part-probe = "qcom-smem"; - }; - }; - }; diff --git a/target/linux/ipq806x/patches-3.18/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-3.18/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch deleted file mode 100644 index 8669b0257c..0000000000 --- a/target/linux/ipq806x/patches-3.18/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch +++ /dev/null @@ -1,62 +0,0 @@ -From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Wed, 29 Apr 2015 15:21:46 -0700 -Subject: [PATCH] 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 ---- - 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 -@@ -311,7 +311,7 @@ config ARCH_MULTIPLATFORM - select ARCH_WANT_OPTIONAL_GPIOLIB - select ARM_HAS_SG_CHAIN - select ARM_PATCH_PHYS_VIRT -- select AUTO_ZRELADDR -+ select AUTO_ZRELADDR if !ARCH_QCOM - select CLKSRC_OF - select COMMON_CLK - select GENERIC_CLOCKEVENTS ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -248,9 +248,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 diff --git a/target/linux/ipq806x/patches-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch b/target/linux/ipq806x/patches-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch deleted file mode 100644 index abd7cf631d..0000000000 --- a/target/linux/ipq806x/patches-3.18/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch +++ /dev/null @@ -1,367 +0,0 @@ -From 7e77aa188a7a7c4391856a9e5ef5ef58f769e679 Mon Sep 17 00:00:00 2001 -From: Jonas Gorski -Date: Sun, 9 Aug 2015 13:02:38 +0200 -Subject: [PATCH] ARM: qcom: add Netgear Nighthawk X4 R7500 device tree - -Signed-off-by: Jonas Gorski ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 370 +++++++++++++++++++++++++++++++ - 2 files changed, 371 insertions(+) - create mode 100644 arch/arm/boot/dts/qcom-ipq8064-r7500.dts - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -361,6 +361,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-apq8084-mtp.dtb \ - qcom-ipq8064-ap148.dtb \ - qcom-ipq8064-db149.dtb \ -+ qcom-ipq8064-r7500.dtb \ - qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb \ - qcom-msm8974-sony-xperia-honami.dtb ---- /dev/null -+++ b/arch/arm/boot/dts/qcom-ipq8064-r7500.dts -@@ -0,0 +1,342 @@ -+#include "qcom-ipq8064-v1.0.dtsi" -+ -+#include -+ -+/ { -+ 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 = &uart4; -+ mdio-gpio0 = &mdio0; -+ }; -+ -+ chosen { -+ bootargs = "rootfstype=squashfs noinitrd"; -+ linux,stdout-path = "serial0:115200n8"; -+ }; -+ -+ soc { -+ pinmux@800000 { -+ i2c4_pins: i2c4_pinmux { -+ pins = "gpio12", "gpio13"; -+ function = "gsbi4"; -+ 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; -+ }; -+ }; -+ -+ 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 = ; -+ 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"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ }; -+ -+ nand@1ac00000 { -+ status = "ok"; -+ -+ pinctrl-0 = <&nand_pins>; -+ pinctrl-names = "default"; -+ -+ nand-ecc-strength = <4>; -+ nand-bus-width = <8>; -+ -+ #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 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 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; -+ }; -+ }; -+ }; -+ -+ gpio-keys { -+ compatible = "gpio-keys"; -+ -+ wifi { -+ label = "wifi"; -+ gpios = <&qcom_pinmux 6 1>; -+ linux,code = ; -+ }; -+ -+ reset { -+ label = "reset"; -+ gpios = <&qcom_pinmux 54 1>; -+ linux,code = ; -+ }; -+ -+ wps { -+ label = "wps"; -+ gpios = <&qcom_pinmux 65 1>; -+ linux,code = ; -+ }; -+ }; -+ -+ gpio-leds { -+ compatible = "gpio-leds"; -+ -+ usb1 { -+ label = "r7500:amber:usb1"; -+ gpios = <&qcom_pinmux 7 0>; -+ }; -+ -+ usb3 { -+ label = "r7500:amber:usb3"; -+ gpios = <&qcom_pinmux 8 0>; -+ }; -+ -+ status { -+ label = "r7500:amber:status"; -+ gpios = <&qcom_pinmux 9 0>; -+ }; -+ -+ internet { -+ label = "r7500:white:internet"; -+ gpios = <&qcom_pinmux 22 0>; -+ }; -+ -+ wan { -+ label = "r7500:white:wan"; -+ gpios = <&qcom_pinmux 23 0>; -+ }; -+ -+ wps { -+ label = "r7500:white:wps"; -+ gpios = <&qcom_pinmux 24 0>; -+ }; -+ -+ esata { -+ label = "r7500:white:esata"; -+ gpios = <&qcom_pinmux 26 0>; -+ }; -+ -+ power { -+ label = "r7500:white:power"; -+ gpios = <&qcom_pinmux 53 0>; -+ default-state = "on"; -+ }; -+ -+ rfkill { -+ label = "r7500:white:rfkill"; -+ gpios = <&qcom_pinmux 64 0>; -+ }; -+ -+ wifi5g { -+ label = "r7500:white:wifi5g"; -+ gpios = <&qcom_pinmux 67 0>; -+ }; -+ }; -+}; -+ -+&adm_dma { -+ status = "ok"; -+}; diff --git a/target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch deleted file mode 100644 index 471a87ba68..0000000000 --- a/target/linux/ipq806x/patches-3.18/302-mtd-qcom-smem-rename-rootfs-ubi.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- a/drivers/mtd/qcom_smem_part.c -+++ b/drivers/mtd/qcom_smem_part.c -@@ -192,6 +192,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 diff --git a/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch deleted file mode 100644 index 22d28ac638..0000000000 --- a/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch +++ /dev/null @@ -1,733 +0,0 @@ -From 2fbb18f85826a9ba308fedb2cf90d3a661a39fd7 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 27 Mar 2015 00:16:14 -0700 -Subject: [PATCH] clk: qcom: Add support for NSS/GMAC clocks and resets - -Add the NSS/GMAC clocks and the TCM clock and NSS resets. - -Signed-off-by: Stephen Boyd ---- - drivers/clk/qcom/gcc-ipq806x.c | 594 ++++++++++++++++++++++++++- - drivers/clk/qcom/gcc-ipq806x.c.rej | 50 +++ - include/dt-bindings/clock/qcom,gcc-ipq806x.h | 2 + - include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++ - 4 files changed, 688 insertions(+), 1 deletion(-) - create mode 100644 drivers/clk/qcom/gcc-ipq806x.c.rej - ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -209,11 +209,46 @@ static struct clk_regmap pll14_vote = { - }, - }; - -+#define NSS_PLL_RATE(f, _l, _m, _n, i) \ -+ { \ -+ .freq = f, \ -+ .l = _l, \ -+ .m = _m, \ -+ .n = _n, \ -+ .ibits = i, \ -+ } -+ -+static struct pll_freq_tbl pll18_freq_tbl[] = { -+ NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625), -+ NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625), -+}; -+ -+static struct clk_pll pll18 = { -+ .l_reg = 0x31a4, -+ .m_reg = 0x31a8, -+ .n_reg = 0x31ac, -+ .config_reg = 0x31b4, -+ .mode_reg = 0x31a0, -+ .status_reg = 0x31b8, -+ .status_bit = 16, -+ .post_div_shift = 16, -+ .post_div_width = 1, -+ .freq_tbl = pll18_freq_tbl, -+ .clkr.hw.init = &(struct clk_init_data){ -+ .name = "pll18", -+ .parent_names = (const char *[]){ "pxo" }, -+ .num_parents = 1, -+ .ops = &clk_pll_ops, -+ }, -+}; -+ - #define P_PXO 0 - #define P_PLL8 1 - #define P_PLL3 1 - #define P_PLL0 2 - #define P_CXO 2 -+#define P_PLL14 3 -+#define P_PLL18 4 - - static const u8 gcc_pxo_pll8_map[] = { - [P_PXO] = 0, -@@ -264,6 +299,22 @@ static const char *gcc_pxo_pll8_pll0_map - "pll0_vote", - }; - -+static const u8 gcc_pxo_pll8_pll14_pll18_pll0_map[] = { -+ [P_PXO] = 0 , -+ [P_PLL8] = 4, -+ [P_PLL0] = 2, -+ [P_PLL14] = 5, -+ [P_PLL18] = 1, -+}; -+ -+static const char *gcc_pxo_pll8_pll14_pll18_pll0[] = { -+ "pxo", -+ "pll8_vote", -+ "pll0_vote", -+ "pll14", -+ "pll18", -+}; -+ - static struct freq_tbl clk_tbl_gsbi_uart[] = { - { 1843200, P_PLL8, 2, 6, 625 }, - { 3686400, P_PLL8, 2, 12, 625 }, -@@ -2269,6 +2320,472 @@ static struct clk_branch ebi2_aon_clk = - }, - }; - -+static const struct freq_tbl clk_tbl_gmac[] = { -+ { 133000000, P_PLL0, 1, 50, 301 }, -+ { 266000000, P_PLL0, 1, 127, 382 }, -+ { } -+}; -+ -+static struct clk_dyn_rcg gmac_core1_src = { -+ .ns_reg[0] = 0x3cac, -+ .ns_reg[1] = 0x3cb0, -+ .md_reg[0] = 0x3ca4, -+ .md_reg[1] = 0x3ca8, -+ .bank_reg = 0x3ca0, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3ca0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core1_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core1_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 4, -+ .hwcg_reg = 0x3cb4, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3cb4, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core1_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core1_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg gmac_core2_src = { -+ .ns_reg[0] = 0x3ccc, -+ .ns_reg[1] = 0x3cd0, -+ .md_reg[0] = 0x3cc4, -+ .md_reg[1] = 0x3cc8, -+ .bank_reg = 0x3ca0, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3cc0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core2_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core2_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 5, -+ .hwcg_reg = 0x3cd4, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3cd4, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core2_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core2_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg gmac_core3_src = { -+ .ns_reg[0] = 0x3cec, -+ .ns_reg[1] = 0x3cf0, -+ .md_reg[0] = 0x3ce4, -+ .md_reg[1] = 0x3ce8, -+ .bank_reg = 0x3ce0, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3ce0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core3_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core3_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 6, -+ .hwcg_reg = 0x3cf4, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3cf4, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core3_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core3_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg gmac_core4_src = { -+ .ns_reg[0] = 0x3d0c, -+ .ns_reg[1] = 0x3d10, -+ .md_reg[0] = 0x3d04, -+ .md_reg[1] = 0x3d08, -+ .bank_reg = 0x3d00, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3d00, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core4_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core4_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 7, -+ .hwcg_reg = 0x3d14, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3d14, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core4_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core4_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static const struct freq_tbl clk_tbl_nss_tcm[] = { -+ { 266000000, P_PLL0, 3, 0, 0 }, -+ { 400000000, P_PLL0, 2, 0, 0 }, -+ { } -+}; -+ -+static struct clk_dyn_rcg nss_tcm_src = { -+ .ns_reg[0] = 0x3dc4, -+ .ns_reg[1] = 0x3dc8, -+ .bank_reg = 0x3dc0, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 4, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 4, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_nss_tcm, -+ .clkr = { -+ .enable_reg = 0x3dc0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "nss_tcm_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch nss_tcm_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 14, -+ .clkr = { -+ .enable_reg = 0x3dd0, -+ .enable_mask = BIT(6) | BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "nss_tcm_clk", -+ .parent_names = (const char *[]){ -+ "nss_tcm_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static const struct freq_tbl clk_tbl_nss[] = { -+ { 110000000, P_PLL18, 1, 1, 5 }, -+ { 275000000, P_PLL18, 2, 0, 0 }, -+ { 550000000, P_PLL18, 1, 0, 0 }, -+ { 733000000, P_PLL18, 1, 0, 0 }, -+ { } -+}; -+ -+static struct clk_dyn_rcg ubi32_core1_src_clk = { -+ .ns_reg[0] = 0x3d2c, -+ .ns_reg[1] = 0x3d30, -+ .md_reg[0] = 0x3d24, -+ .md_reg[1] = 0x3d28, -+ .bank_reg = 0x3d20, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_nss, -+ .clkr = { -+ .enable_reg = 0x3d20, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "ubi32_core1_src_clk", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg ubi32_core2_src_clk = { -+ .ns_reg[0] = 0x3d4c, -+ .ns_reg[1] = 0x3d50, -+ .md_reg[0] = 0x3d44, -+ .md_reg[1] = 0x3d48, -+ .bank_reg = 0x3d40, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_nss, -+ .clkr = { -+ .enable_reg = 0x3d40, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "ubi32_core2_src_clk", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, -+ }, -+ }, -+}; -+ - static struct clk_regmap *gcc_ipq806x_clks[] = { - [PLL0] = &pll0.clkr, - [PLL0_VOTE] = &pll0_vote, -@@ -2277,6 +2794,7 @@ static struct clk_regmap *gcc_ipq806x_cl - [PLL8_VOTE] = &pll8_vote, - [PLL14] = &pll14.clkr, - [PLL14_VOTE] = &pll14_vote, -+ [PLL18] = &pll18.clkr, - [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, - [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, - [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, -@@ -2376,6 +2894,18 @@ static struct clk_regmap *gcc_ipq806x_cl - [PLL9] = &hfpll0.clkr, - [PLL10] = &hfpll1.clkr, - [PLL12] = &hfpll_l2.clkr, -+ [GMAC_CORE1_CLK_SRC] = &gmac_core1_src.clkr, -+ [GMAC_CORE1_CLK] = &gmac_core1_clk.clkr, -+ [GMAC_CORE2_CLK_SRC] = &gmac_core2_src.clkr, -+ [GMAC_CORE2_CLK] = &gmac_core2_clk.clkr, -+ [GMAC_CORE3_CLK_SRC] = &gmac_core3_src.clkr, -+ [GMAC_CORE3_CLK] = &gmac_core3_clk.clkr, -+ [GMAC_CORE4_CLK_SRC] = &gmac_core4_src.clkr, -+ [GMAC_CORE4_CLK] = &gmac_core4_clk.clkr, -+ [UBI32_CORE1_CLK_SRC] = &ubi32_core1_src_clk.clkr, -+ [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, -+ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, -+ [NSSTCM_CLK] = &nss_tcm_clk.clkr, - }; - - static const struct qcom_reset_map gcc_ipq806x_resets[] = { -@@ -2494,6 +3024,48 @@ static const struct qcom_reset_map gcc_i - [USB30_1_PHY_RESET] = { 0x3b58, 0 }, - [NSSFB0_RESET] = { 0x3b60, 6 }, - [NSSFB1_RESET] = { 0x3b60, 7 }, -+ [UBI32_CORE1_CLKRST_CLAMP_RESET] = { 0x3d3c, 3}, -+ [UBI32_CORE1_CLAMP_RESET] = { 0x3d3c, 2 }, -+ [UBI32_CORE1_AHB_RESET] = { 0x3d3c, 1 }, -+ [UBI32_CORE1_AXI_RESET] = { 0x3d3c, 0 }, -+ [UBI32_CORE2_CLKRST_CLAMP_RESET] = { 0x3d5c, 3 }, -+ [UBI32_CORE2_CLAMP_RESET] = { 0x3d5c, 2 }, -+ [UBI32_CORE2_AHB_RESET] = { 0x3d5c, 1 }, -+ [UBI32_CORE2_AXI_RESET] = { 0x3d5c, 0 }, -+ [GMAC_CORE1_RESET] = { 0x3cbc, 0 }, -+ [GMAC_CORE2_RESET] = { 0x3cdc, 0 }, -+ [GMAC_CORE3_RESET] = { 0x3cfc, 0 }, -+ [GMAC_CORE4_RESET] = { 0x3d1c, 0 }, -+ [GMAC_AHB_RESET] = { 0x3e24, 0 }, -+ [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 }, -+ [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 }, -+ [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 }, -+ [NSS_CH0_HW_RST_RX_125M_N_RESET] = { 0x3b60, 3 }, -+ [NSS_CH0_RST_TX_125M_N_RESET] = { 0x3b60, 4 }, -+ [NSS_CH1_RST_RX_CLK_N_RESET] = { 0x3b60, 5 }, -+ [NSS_CH1_RST_TX_CLK_N_RESET] = { 0x3b60, 6 }, -+ [NSS_CH1_RST_RX_125M_N_RESET] = { 0x3b60, 7 }, -+ [NSS_CH1_HW_RST_RX_125M_N_RESET] = { 0x3b60, 8 }, -+ [NSS_CH1_RST_TX_125M_N_RESET] = { 0x3b60, 9 }, -+ [NSS_CH2_RST_RX_CLK_N_RESET] = { 0x3b60, 10 }, -+ [NSS_CH2_RST_TX_CLK_N_RESET] = { 0x3b60, 11 }, -+ [NSS_CH2_RST_RX_125M_N_RESET] = { 0x3b60, 12 }, -+ [NSS_CH2_HW_RST_RX_125M_N_RESET] = { 0x3b60, 13 }, -+ [NSS_CH2_RST_TX_125M_N_RESET] = { 0x3b60, 14 }, -+ [NSS_CH3_RST_RX_CLK_N_RESET] = { 0x3b60, 15 }, -+ [NSS_CH3_RST_TX_CLK_N_RESET] = { 0x3b60, 16 }, -+ [NSS_CH3_RST_RX_125M_N_RESET] = { 0x3b60, 17 }, -+ [NSS_CH3_HW_RST_RX_125M_N_RESET] = { 0x3b60, 18 }, -+ [NSS_CH3_RST_TX_125M_N_RESET] = { 0x3b60, 19 }, -+ [NSS_RST_RX_250M_125M_N_RESET] = { 0x3b60, 20 }, -+ [NSS_RST_TX_250M_125M_N_RESET] = { 0x3b60, 21 }, -+ [NSS_QSGMII_TXPI_RST_N_RESET] = { 0x3b60, 22 }, -+ [NSS_QSGMII_CDR_RST_N_RESET] = { 0x3b60, 23 }, -+ [NSS_SGMII2_CDR_RST_N_RESET] = { 0x3b60, 24 }, -+ [NSS_SGMII3_CDR_RST_N_RESET] = { 0x3b60, 25 }, -+ [NSS_CAL_PRBS_RST_N_RESET] = { 0x3b60, 26 }, -+ [NSS_LCKDT_RST_N_RESET] = { 0x3b60, 27 }, -+ [NSS_SRDS_N_RESET] = { 0x3b60, 28 }, - }; - - static const struct regmap_config gcc_ipq806x_regmap_config = { -@@ -2522,6 +3094,8 @@ static int gcc_ipq806x_probe(struct plat - { - struct clk *clk; - struct device *dev = &pdev->dev; -+ struct regmap *regmap; -+ int ret; - - /* Temporary until RPM clocks supported */ - clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); -@@ -2532,7 +3106,25 @@ static int gcc_ipq806x_probe(struct plat - if (IS_ERR(clk)) - return PTR_ERR(clk); - -- return qcom_cc_probe(pdev, &gcc_ipq806x_desc); -+ ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc); -+ if (ret) -+ return ret; -+ -+ regmap = dev_get_regmap(dev, NULL); -+ if (!regmap) -+ return -ENODEV; -+ -+ /* Setup PLL18 static bits */ -+ regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400); -+ regmap_write(regmap, 0x31b0, 0x3080); -+ -+ /* Set GMAC footswitch sleep/wakeup values */ -+ regmap_write(regmap, 0x3cb8, 8); -+ regmap_write(regmap, 0x3cd8, 8); -+ regmap_write(regmap, 0x3cf8, 8); -+ regmap_write(regmap, 0x3d18, 8); -+ -+ return 0; - } - - static int gcc_ipq806x_remove(struct platform_device *pdev) ---- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h -+++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h -@@ -290,5 +290,7 @@ - #define UBI32_CORE1_CLK 279 - #define UBI32_CORE2_CLK 280 - #define EBI2_AON_CLK 281 -+#define NSSTCM_CLK_SRC 282 -+#define NSSTCM_CLK 283 - - #endif ---- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h -+++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h -@@ -129,4 +129,47 @@ - #define USB30_1_PHY_RESET 112 - #define NSSFB0_RESET 113 - #define NSSFB1_RESET 114 -+#define UBI32_CORE1_CLKRST_CLAMP_RESET 115 -+#define UBI32_CORE1_CLAMP_RESET 116 -+#define UBI32_CORE1_AHB_RESET 117 -+#define UBI32_CORE1_AXI_RESET 118 -+#define UBI32_CORE2_CLKRST_CLAMP_RESET 119 -+#define UBI32_CORE2_CLAMP_RESET 120 -+#define UBI32_CORE2_AHB_RESET 121 -+#define UBI32_CORE2_AXI_RESET 122 -+#define GMAC_CORE1_RESET 123 -+#define GMAC_CORE2_RESET 124 -+#define GMAC_CORE3_RESET 125 -+#define GMAC_CORE4_RESET 126 -+#define GMAC_AHB_RESET 127 -+#define NSS_CH0_RST_RX_CLK_N_RESET 128 -+#define NSS_CH0_RST_TX_CLK_N_RESET 129 -+#define NSS_CH0_RST_RX_125M_N_RESET 130 -+#define NSS_CH0_HW_RST_RX_125M_N_RESET 131 -+#define NSS_CH0_RST_TX_125M_N_RESET 132 -+#define NSS_CH1_RST_RX_CLK_N_RESET 133 -+#define NSS_CH1_RST_TX_CLK_N_RESET 134 -+#define NSS_CH1_RST_RX_125M_N_RESET 135 -+#define NSS_CH1_HW_RST_RX_125M_N_RESET 136 -+#define NSS_CH1_RST_TX_125M_N_RESET 137 -+#define NSS_CH2_RST_RX_CLK_N_RESET 138 -+#define NSS_CH2_RST_TX_CLK_N_RESET 139 -+#define NSS_CH2_RST_RX_125M_N_RESET 140 -+#define NSS_CH2_HW_RST_RX_125M_N_RESET 141 -+#define NSS_CH2_RST_TX_125M_N_RESET 142 -+#define NSS_CH3_RST_RX_CLK_N_RESET 143 -+#define NSS_CH3_RST_TX_CLK_N_RESET 144 -+#define NSS_CH3_RST_RX_125M_N_RESET 145 -+#define NSS_CH3_HW_RST_RX_125M_N_RESET 146 -+#define NSS_CH3_RST_TX_125M_N_RESET 147 -+#define NSS_RST_RX_250M_125M_N_RESET 148 -+#define NSS_RST_TX_250M_125M_N_RESET 149 -+#define NSS_QSGMII_TXPI_RST_N_RESET 150 -+#define NSS_QSGMII_CDR_RST_N_RESET 151 -+#define NSS_SGMII2_CDR_RST_N_RESET 152 -+#define NSS_SGMII3_CDR_RST_N_RESET 153 -+#define NSS_CAL_PRBS_RST_N_RESET 154 -+#define NSS_LCKDT_RST_N_RESET 155 -+#define NSS_SRDS_N_RESET 156 -+ - #endif diff --git a/target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch b/target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch deleted file mode 100644 index 539d1a1c44..0000000000 --- a/target/linux/ipq806x/patches-3.18/701-stmmac_update_to_4.3.patch +++ /dev/null @@ -1,4258 +0,0 @@ ---- a/include/linux/stmmac.h -+++ b/include/linux/stmmac.h -@@ -99,6 +99,7 @@ struct plat_stmmacenet_data { - int phy_addr; - int interface; - struct stmmac_mdio_bus_data *mdio_bus_data; -+ struct device_node *phy_node; - struct stmmac_dma_cfg *dma_cfg; - int clk_csr; - int has_gmac; -@@ -114,32 +115,12 @@ struct plat_stmmacenet_data { - int maxmtu; - int multicast_filter_bins; - int unicast_filter_entries; -+ int tx_fifo_size; -+ int rx_fifo_size; - void (*fix_mac_speed)(void *priv, unsigned int speed); - void (*bus_setup)(void __iomem *ioaddr); -- void *(*setup)(struct platform_device *pdev); -- void (*free)(struct platform_device *pdev, void *priv); - int (*init)(struct platform_device *pdev, void *priv); - void (*exit)(struct platform_device *pdev, void *priv); -- void *custom_cfg; -- void *custom_data; - void *bsp_priv; - }; -- --/* of_data for SoC glue layer device tree bindings */ -- --struct stmmac_of_data { -- int has_gmac; -- int enh_desc; -- int tx_coe; -- int rx_coe; -- int bugged_jumbo; -- int pmt; -- int riwt_off; -- void (*fix_mac_speed)(void *priv, unsigned int speed); -- void (*bus_setup)(void __iomem *ioaddr); -- void *(*setup)(struct platform_device *pdev); -- void (*free)(struct platform_device *pdev, void *priv); -- int (*init)(struct platform_device *pdev, void *priv); -- void (*exit)(struct platform_device *pdev, void *priv); --}; - #endif ---- a/drivers/net/ethernet/stmicro/Kconfig -+++ b/drivers/net/ethernet/stmicro/Kconfig -@@ -7,9 +7,7 @@ config NET_VENDOR_STMICRO - default y - depends on HAS_IOMEM - ---help--- -- If you have a network (Ethernet) card belonging to this class, say Y -- and read the Ethernet-HOWTO, available from -- . -+ If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all ---- a/drivers/net/ethernet/stmicro/stmmac/Kconfig -+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig -@@ -14,21 +14,54 @@ config STMMAC_ETH - if STMMAC_ETH - - config STMMAC_PLATFORM -- bool "STMMAC Platform bus support" -+ tristate "STMMAC Platform bus support" - depends on STMMAC_ETH -+ select MFD_SYSCON - default y - ---help--- -- This selects the platform specific bus support for -- the stmmac device driver. This is the driver used -- on many embedded STM platforms based on ARM and SuperH -- processors. -+ This selects the platform specific bus support for the stmmac driver. -+ This is the driver used on several SoCs: -+ STi, Allwinner, Amlogic Meson, Altera SOCFPGA. -+ - If you have a controller with this interface, say Y or M here. - - If unsure, say N. - -+if STMMAC_PLATFORM -+ -+config DWMAC_GENERIC -+ tristate "Generic driver for DWMAC" -+ default STMMAC_PLATFORM -+ ---help--- -+ Generic DWMAC driver for platforms that don't require any -+ platform specific code to function or is using platform -+ data for setup. -+ -+config DWMAC_IPQ806X -+ tristate "QCA IPQ806x DWMAC support" -+ default ARCH_QCOM -+ depends on OF -+ select MFD_SYSCON -+ help -+ Support for QCA IPQ806X DWMAC Ethernet. -+ -+ This selects the IPQ806x SoC glue layer support for the stmmac -+ device driver. This driver does not use any of the hardware -+ acceleration features available on this SoC. Network devices -+ will behave like standard non-accelerated ethernet interfaces. -+ -+config DWMAC_LPC18XX -+ tristate "NXP LPC18xx/43xx DWMAC support" -+ default ARCH_LPC18XX -+ depends on OF -+ select MFD_SYSCON -+ ---help--- -+ Support for NXP LPC18xx/43xx DWMAC Ethernet. -+ - config DWMAC_MESON -- bool "Amlogic Meson dwmac support" -- depends on STMMAC_PLATFORM && ARCH_MESON -+ tristate "Amlogic Meson dwmac support" -+ default ARCH_MESON -+ depends on OF - help - Support for Ethernet controller on Amlogic Meson SoCs. - -@@ -36,9 +69,22 @@ config DWMAC_MESON - the stmmac device driver. This driver is used for Meson6 and - Meson8 SoCs. - -+config DWMAC_ROCKCHIP -+ tristate "Rockchip dwmac support" -+ default ARCH_ROCKCHIP -+ depends on OF -+ select MFD_SYSCON -+ help -+ Support for Ethernet controller on Rockchip RK3288 SoC. -+ -+ This selects the Rockchip RK3288 SoC glue layer support for -+ the stmmac device driver. -+ - config DWMAC_SOCFPGA -- bool "SOCFPGA dwmac support" -- depends on STMMAC_PLATFORM && MFD_SYSCON && (ARCH_SOCFPGA || COMPILE_TEST) -+ tristate "SOCFPGA dwmac support" -+ default ARCH_SOCFPGA -+ depends on OF -+ select MFD_SYSCON - help - Support for ethernet controller on Altera SOCFPGA - -@@ -46,21 +92,11 @@ config DWMAC_SOCFPGA - for the stmmac device driver. This driver is used for - arria5 and cyclone5 FPGA SoCs. - --config DWMAC_SUNXI -- bool "Allwinner GMAC support" -- depends on STMMAC_PLATFORM && ARCH_SUNXI -- default y -- ---help--- -- Support for Allwinner A20/A31 GMAC ethernet controllers. -- -- This selects Allwinner SoC glue layer support for the -- stmmac device driver. This driver is used for A20/A31 -- GMAC ethernet controller. -- - config DWMAC_STI -- bool "STi GMAC support" -- depends on STMMAC_PLATFORM && ARCH_STI -- default y -+ tristate "STi GMAC support" -+ default ARCH_STI -+ depends on OF -+ select MFD_SYSCON - ---help--- - Support for ethernet controller on STi SOCs. - -@@ -68,8 +104,20 @@ config DWMAC_STI - device driver. This driver is used on for the STi series - SOCs GMAC ethernet controller. - -+config DWMAC_SUNXI -+ tristate "Allwinner GMAC support" -+ default ARCH_SUNXI -+ depends on OF -+ ---help--- -+ Support for Allwinner A20/A31 GMAC ethernet controllers. -+ -+ This selects Allwinner SoC glue layer support for the -+ stmmac device driver. This driver is used for A20/A31 -+ GMAC ethernet controller. -+endif -+ - config STMMAC_PCI -- bool "STMMAC PCI bus support" -+ tristate "STMMAC PCI bus support" - depends on STMMAC_ETH && PCI - ---help--- - This is to select the Synopsys DWMAC available on PCI devices, -@@ -79,22 +127,4 @@ config STMMAC_PCI - D1215994A VIRTEX FPGA board. - - If unsure, say N. -- --config STMMAC_DEBUG_FS -- bool "Enable monitoring via sysFS " -- default n -- depends on STMMAC_ETH && DEBUG_FS -- ---help--- -- The stmmac entry in /sys reports DMA TX/RX rings -- or (if supported) the HW cap register. -- --config STMMAC_DA -- bool "STMMAC DMA arbitration scheme" -- default n -- ---help--- -- Selecting this option, rx has priority over Tx (only for Giga -- Ethernet device). -- By default, the DMA arbitration scheme is based on Round-robin -- (rx:tx priority is 1:1). -- - endif ---- a/drivers/net/ethernet/stmicro/stmmac/Makefile -+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile -@@ -1,11 +1,20 @@ - obj-$(CONFIG_STMMAC_ETH) += stmmac.o --stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o --stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o --stmmac-$(CONFIG_DWMAC_MESON) += dwmac-meson.o --stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o --stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o --stmmac-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o - stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ -- chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ -- dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ -+ chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \ -+ dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ - mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y) -+ -+# Ordering matters. Generic driver must be last. -+obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o -+obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o -+obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o -+obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o -+obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o -+obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o -+obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o -+obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o -+obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o -+stmmac-platform-objs:= stmmac_platform.o -+ -+obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o -+stmmac-pci-objs:= stmmac_pci.o ---- a/drivers/net/ethernet/stmicro/stmmac/common.h -+++ b/drivers/net/ethernet/stmicro/stmmac/common.h -@@ -44,6 +44,7 @@ - #undef FRAME_FILTER_DEBUG - /* #define FRAME_FILTER_DEBUG */ - -+/* Extra statistic and debug information exposed by ethtool */ - struct stmmac_extra_stats { - /* Transmit errors */ - unsigned long tx_underflow ____cacheline_aligned; -@@ -149,7 +150,7 @@ struct stmmac_extra_stats { - #define MAC_CSR_H_FRQ_MASK 0x20 - - #define HASH_TABLE_SIZE 64 --#define PAUSE_TIME 0x200 -+#define PAUSE_TIME 0xffff - - /* Flow Control defines */ - #define FLOW_OFF 0 -@@ -220,6 +221,7 @@ enum dma_irq_status { - handle_tx = 0x8, - }; - -+/* EEE and LPI defines */ - #define CORE_IRQ_TX_PATH_IN_LPI_MODE (1 << 0) - #define CORE_IRQ_TX_PATH_EXIT_LPI_MODE (1 << 1) - #define CORE_IRQ_RX_PATH_IN_LPI_MODE (1 << 2) -@@ -229,6 +231,7 @@ enum dma_irq_status { - #define CORE_PCS_LINK_STATUS (1 << 6) - #define CORE_RGMII_IRQ (1 << 7) - -+/* Physical Coding Sublayer */ - struct rgmii_adv { - unsigned int pause; - unsigned int duplex; -@@ -294,6 +297,7 @@ struct dma_features { - - #define JUMBO_LEN 9000 - -+/* Descriptors helpers */ - struct stmmac_desc_ops { - /* DMA RX descriptor ring initialization */ - void (*init_rx_desc) (struct dma_desc *p, int disable_rx_ic, int mode, -@@ -341,6 +345,10 @@ struct stmmac_desc_ops { - int (*get_rx_timestamp_status) (void *desc, u32 ats); - }; - -+extern const struct stmmac_desc_ops enh_desc_ops; -+extern const struct stmmac_desc_ops ndesc_ops; -+ -+/* Specific DMA helpers */ - struct stmmac_dma_ops { - /* DMA core initialization */ - int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb, -@@ -349,7 +357,8 @@ struct stmmac_dma_ops { - void (*dump_regs) (void __iomem *ioaddr); - /* Set tx/rx threshold in the csr6 register - * An invalid value enables the store-and-forward mode */ -- void (*dma_mode) (void __iomem *ioaddr, int txmode, int rxmode); -+ void (*dma_mode)(void __iomem *ioaddr, int txmode, int rxmode, -+ int rxfifosz); - /* To track extra statistic (if supported) */ - void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x, - void __iomem *ioaddr); -@@ -370,6 +379,7 @@ struct stmmac_dma_ops { - - struct mac_device_info; - -+/* Helpers to program the MAC core */ - struct stmmac_ops { - /* MAC core initialization */ - void (*core_init)(struct mac_device_info *hw, int mtu); -@@ -400,6 +410,7 @@ struct stmmac_ops { - void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv); - }; - -+/* PTP and HW Timer helpers */ - struct stmmac_hwtimestamp { - void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data); - void (*config_sub_second_increment) (void __iomem *ioaddr); -@@ -410,6 +421,8 @@ struct stmmac_hwtimestamp { - u64(*get_systime) (void __iomem *ioaddr); - }; - -+extern const struct stmmac_hwtimestamp stmmac_ptp; -+ - struct mac_link { - int port; - int duplex; -@@ -421,6 +434,7 @@ struct mii_regs { - unsigned int data; /* MII Data */ - }; - -+/* Helpers to manage the descriptors for chain and ring modes */ - struct stmmac_mode_ops { - void (*init) (void *des, dma_addr_t phy_addr, unsigned int size, - unsigned int extend_desc); ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c -@@ -0,0 +1,81 @@ -+/* -+ * Generic DWMAC platform driver -+ * -+ * Copyright (C) 2007-2011 STMicroelectronics Ltd -+ * Copyright (C) 2015 Joachim Eastwood -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+ -+#include "stmmac.h" -+#include "stmmac_platform.h" -+ -+static int dwmac_generic_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ if (pdev->dev.of_node) { -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) { -+ dev_err(&pdev->dev, "dt configuration failed\n"); -+ return PTR_ERR(plat_dat); -+ } -+ } else { -+ plat_dat = dev_get_platdata(&pdev->dev); -+ if (!plat_dat) { -+ dev_err(&pdev->dev, "no platform data provided\n"); -+ return -EINVAL; -+ } -+ -+ /* Set default value for multicast hash bins */ -+ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; -+ -+ /* Set default value for unicast filter entries */ -+ plat_dat->unicast_filter_entries = 1; -+ } -+ -+ /* Custom initialisation (if needed) */ -+ if (plat_dat->init) { -+ ret = plat_dat->init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ } -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id dwmac_generic_match[] = { -+ { .compatible = "st,spear600-gmac"}, -+ { .compatible = "snps,dwmac-3.610"}, -+ { .compatible = "snps,dwmac-3.70a"}, -+ { .compatible = "snps,dwmac-3.710"}, -+ { .compatible = "snps,dwmac"}, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, dwmac_generic_match); -+ -+static struct platform_driver dwmac_generic_driver = { -+ .probe = dwmac_generic_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = STMMAC_RESOURCE_NAME, -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = of_match_ptr(dwmac_generic_match), -+ }, -+}; -+module_platform_driver(dwmac_generic_driver); -+ -+MODULE_DESCRIPTION("Generic dwmac driver"); -+MODULE_LICENSE("GPL v2"); ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -@@ -0,0 +1,373 @@ -+/* -+ * Qualcomm Atheros IPQ806x GMAC glue layer -+ * -+ * Copyright (C) 2015 The Linux Foundation -+ * -+ * Permission to use, copy, modify, and/or distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "stmmac_platform.h" -+ -+#define NSS_COMMON_CLK_GATE 0x8 -+#define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x) -+#define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2)) -+#define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2)) -+#define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x) -+#define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x) -+ -+#define NSS_COMMON_CLK_DIV0 0xC -+#define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8) -+#define NSS_COMMON_CLK_DIV_MASK 0x7f -+ -+#define NSS_COMMON_CLK_SRC_CTRL 0x14 -+#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (x) -+/* Mode is coded on 1 bit but is different depending on the MAC ID: -+ * MAC0: QSGMII=0 RGMII=1 -+ * MAC1: QSGMII=0 SGMII=0 RGMII=1 -+ * MAC2 & MAC3: QSGMII=0 SGMII=1 -+ */ -+#define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1 -+#define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0) -+ -+#define NSS_COMMON_MACSEC_CTL 0x28 -+#define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x) -+ -+#define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4)) -+#define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19) -+#define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16) -+#define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8 -+#define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0 -+#define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f -+ -+#define NSS_COMMON_CLK_DIV_RGMII_1000 1 -+#define NSS_COMMON_CLK_DIV_RGMII_100 9 -+#define NSS_COMMON_CLK_DIV_RGMII_10 99 -+#define NSS_COMMON_CLK_DIV_SGMII_1000 0 -+#define NSS_COMMON_CLK_DIV_SGMII_100 4 -+#define NSS_COMMON_CLK_DIV_SGMII_10 49 -+ -+#define QSGMII_PCS_MODE_CTL 0x68 -+#define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7) -+ -+#define QSGMII_PCS_CAL_LCKDT_CTL 0x120 -+#define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19) -+ -+/* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */ -+#define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \ -+ (0x13c + (4 * (x - 2)))) -+#define QSGMII_PHY_CDR_EN BIT(0) -+#define QSGMII_PHY_RX_FRONT_EN BIT(1) -+#define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2) -+#define QSGMII_PHY_TX_DRIVER_EN BIT(3) -+#define QSGMII_PHY_QSGMII_EN BIT(7) -+#define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12 -+#define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7 -+#define QSGMII_PHY_RX_DC_BIAS_OFFSET 18 -+#define QSGMII_PHY_RX_DC_BIAS_MASK 0x3 -+#define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20 -+#define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3 -+#define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22 -+#define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3 -+#define QSGMII_PHY_TX_DRV_AMP_OFFSET 28 -+#define QSGMII_PHY_TX_DRV_AMP_MASK 0xf -+ -+struct ipq806x_gmac { -+ struct platform_device *pdev; -+ struct regmap *nss_common; -+ struct regmap *qsgmii_csr; -+ uint32_t id; -+ struct clk *core_clk; -+ phy_interface_t phy_mode; -+}; -+ -+static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed) -+{ -+ struct device *dev = &gmac->pdev->dev; -+ int div; -+ -+ switch (speed) { -+ case SPEED_1000: -+ div = NSS_COMMON_CLK_DIV_SGMII_1000; -+ break; -+ -+ case SPEED_100: -+ div = NSS_COMMON_CLK_DIV_SGMII_100; -+ break; -+ -+ case SPEED_10: -+ div = NSS_COMMON_CLK_DIV_SGMII_10; -+ break; -+ -+ default: -+ dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed); -+ return -EINVAL; -+ } -+ -+ return div; -+} -+ -+static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed) -+{ -+ struct device *dev = &gmac->pdev->dev; -+ int div; -+ -+ switch (speed) { -+ case SPEED_1000: -+ div = NSS_COMMON_CLK_DIV_RGMII_1000; -+ break; -+ -+ case SPEED_100: -+ div = NSS_COMMON_CLK_DIV_RGMII_100; -+ break; -+ -+ case SPEED_10: -+ div = NSS_COMMON_CLK_DIV_RGMII_10; -+ break; -+ -+ default: -+ dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed); -+ return -EINVAL; -+ } -+ -+ return div; -+} -+ -+static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed) -+{ -+ uint32_t clk_bits, val; -+ int div; -+ -+ switch (gmac->phy_mode) { -+ case PHY_INTERFACE_MODE_RGMII: -+ div = get_clk_div_rgmii(gmac, speed); -+ clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) | -+ NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id); -+ break; -+ -+ case PHY_INTERFACE_MODE_SGMII: -+ div = get_clk_div_sgmii(gmac, speed); -+ clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) | -+ NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id); -+ break; -+ -+ default: -+ dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n", -+ phy_modes(gmac->phy_mode)); -+ return -EINVAL; -+ } -+ -+ /* Disable the clocks */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); -+ val &= ~clk_bits; -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); -+ -+ /* Set the divider */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val); -+ val &= ~(NSS_COMMON_CLK_DIV_MASK -+ << NSS_COMMON_CLK_DIV_OFFSET(gmac->id)); -+ val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id); -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val); -+ -+ /* Enable the clock back */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); -+ val |= clk_bits; -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); -+ -+ return 0; -+} -+ -+static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) -+{ -+ struct device *dev = &gmac->pdev->dev; -+ -+ gmac->phy_mode = of_get_phy_mode(dev->of_node); -+ if (gmac->phy_mode < 0) { -+ dev_err(dev, "missing phy mode property\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) { -+ dev_err(dev, "missing qcom id property\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ /* The GMACs are called 1 to 4 in the documentation, but to simplify the -+ * code and keep it consistent with the Linux convention, we'll number -+ * them from 0 to 3 here. -+ */ -+ if (gmac->id < 0 || gmac->id > 3) { -+ dev_err(dev, "invalid gmac id\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ gmac->core_clk = devm_clk_get(dev, "stmmaceth"); -+ if (IS_ERR(gmac->core_clk)) { -+ dev_err(dev, "missing stmmaceth clk property\n"); -+ return gmac->core_clk; -+ } -+ clk_set_rate(gmac->core_clk, 266000000); -+ -+ /* Setup the register map for the nss common registers */ -+ gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "qcom,nss-common"); -+ if (IS_ERR(gmac->nss_common)) { -+ dev_err(dev, "missing nss-common node\n"); -+ return gmac->nss_common; -+ } -+ -+ /* Setup the register map for the qsgmii csr registers */ -+ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "qcom,qsgmii-csr"); -+ if (IS_ERR(gmac->qsgmii_csr)) { -+ dev_err(dev, "missing qsgmii-csr node\n"); -+ return gmac->qsgmii_csr; -+ } -+ -+ return NULL; -+} -+ -+static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed) -+{ -+ struct ipq806x_gmac *gmac = priv; -+ -+ ipq806x_gmac_set_speed(gmac, speed); -+} -+ -+static int ipq806x_gmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct device *dev = &pdev->dev; -+ struct ipq806x_gmac *gmac; -+ int val; -+ void *err; -+ -+ val = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (val) -+ return val; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); -+ if (!gmac) -+ return -ENOMEM; -+ -+ gmac->pdev = pdev; -+ -+ err = ipq806x_gmac_of_parse(gmac); -+ if (IS_ERR(err)) { -+ dev_err(dev, "device tree parsing error\n"); -+ return PTR_ERR(err); -+ } -+ -+ regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL, -+ QSGMII_PCS_CAL_LCKDT_CTL_RST); -+ -+ /* Inter frame gap is set to 12 */ -+ val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET | -+ 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET; -+ /* We also initiate an AXI low power exit request */ -+ val |= NSS_COMMON_GMAC_CTL_CSYS_REQ; -+ switch (gmac->phy_mode) { -+ case PHY_INTERFACE_MODE_RGMII: -+ val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; -+ break; -+ case PHY_INTERFACE_MODE_SGMII: -+ val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; -+ break; -+ default: -+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", -+ phy_modes(gmac->phy_mode)); -+ return -EINVAL; -+ } -+ regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val); -+ -+ /* Configure the clock src according to the mode */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val); -+ val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id)); -+ switch (gmac->phy_mode) { -+ case PHY_INTERFACE_MODE_RGMII: -+ val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) << -+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); -+ break; -+ case PHY_INTERFACE_MODE_SGMII: -+ val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) << -+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); -+ break; -+ default: -+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", -+ phy_modes(gmac->phy_mode)); -+ return -EINVAL; -+ } -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val); -+ -+ /* Enable PTP clock */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); -+ val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id); -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); -+ -+ if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) { -+ regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id), -+ QSGMII_PHY_CDR_EN | -+ QSGMII_PHY_RX_FRONT_EN | -+ QSGMII_PHY_RX_SIGNAL_DETECT_EN | -+ QSGMII_PHY_TX_DRIVER_EN | -+ QSGMII_PHY_QSGMII_EN | -+ 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET | -+ 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET | -+ 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET | -+ 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET | -+ 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET); -+ } -+ -+ plat_dat->has_gmac = true; -+ plat_dat->bsp_priv = gmac; -+ plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id ipq806x_gmac_dwmac_match[] = { -+ { .compatible = "qcom,ipq806x-gmac" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, ipq806x_gmac_dwmac_match); -+ -+static struct platform_driver ipq806x_gmac_dwmac_driver = { -+ .probe = ipq806x_gmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "ipq806x-gmac-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = ipq806x_gmac_dwmac_match, -+ }, -+}; -+module_platform_driver(ipq806x_gmac_dwmac_driver); -+ -+MODULE_AUTHOR("Mathieu Olivari "); -+MODULE_DESCRIPTION("Qualcomm Atheros IPQ806x DWMAC specific glue layer"); -+MODULE_LICENSE("Dual BSD/GPL"); ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c -@@ -0,0 +1,86 @@ -+/* -+ * DWMAC glue for NXP LPC18xx/LPC43xx Ethernet -+ * -+ * Copyright (C) 2015 Joachim Eastwood -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "stmmac_platform.h" -+ -+/* Register defines for CREG syscon */ -+#define LPC18XX_CREG_CREG6 0x12c -+# define LPC18XX_CREG_CREG6_ETHMODE_MASK 0x7 -+# define LPC18XX_CREG_CREG6_ETHMODE_MII 0x0 -+# define LPC18XX_CREG_CREG6_ETHMODE_RMII 0x4 -+ -+static int lpc18xx_dwmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct regmap *reg; -+ u8 ethmode; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ plat_dat->has_gmac = true; -+ -+ reg = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg"); -+ if (IS_ERR(reg)) { -+ dev_err(&pdev->dev, "syscon lookup failed\n"); -+ return PTR_ERR(reg); -+ } -+ -+ if (plat_dat->interface == PHY_INTERFACE_MODE_MII) { -+ ethmode = LPC18XX_CREG_CREG6_ETHMODE_MII; -+ } else if (plat_dat->interface == PHY_INTERFACE_MODE_RMII) { -+ ethmode = LPC18XX_CREG_CREG6_ETHMODE_RMII; -+ } else { -+ dev_err(&pdev->dev, "Only MII and RMII mode supported\n"); -+ return -EINVAL; -+ } -+ -+ regmap_update_bits(reg, LPC18XX_CREG_CREG6, -+ LPC18XX_CREG_CREG6_ETHMODE_MASK, ethmode); -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id lpc18xx_dwmac_match[] = { -+ { .compatible = "nxp,lpc1850-dwmac" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, lpc18xx_dwmac_match); -+ -+static struct platform_driver lpc18xx_dwmac_driver = { -+ .probe = lpc18xx_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "lpc18xx-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = lpc18xx_dwmac_match, -+ }, -+}; -+module_platform_driver(lpc18xx_dwmac_driver); -+ -+MODULE_AUTHOR("Joachim Eastwood "); -+MODULE_DESCRIPTION("DWMAC glue for LPC18xx/43xx Ethernet"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c -@@ -15,9 +15,12 @@ - #include - #include - #include -+#include - #include - #include - -+#include "stmmac_platform.h" -+ - #define ETHMAC_SPEED_100 BIT(1) - - struct meson_dwmac { -@@ -44,24 +47,54 @@ static void meson6_dwmac_fix_mac_speed(v - writel(val, dwmac->reg); - } - --static void *meson6_dwmac_setup(struct platform_device *pdev) -+static int meson6_dwmac_probe(struct platform_device *pdev) - { -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; - struct meson_dwmac *dwmac; - struct resource *res; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); - - dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); - if (!dwmac) -- return ERR_PTR(-ENOMEM); -+ return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - dwmac->reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dwmac->reg)) -- return dwmac->reg; -+ return PTR_ERR(dwmac->reg); -+ -+ plat_dat->bsp_priv = dwmac; -+ plat_dat->fix_mac_speed = meson6_dwmac_fix_mac_speed; - -- return dwmac; -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - } - --const struct stmmac_of_data meson6_dwmac_data = { -- .setup = meson6_dwmac_setup, -- .fix_mac_speed = meson6_dwmac_fix_mac_speed, -+static const struct of_device_id meson6_dwmac_match[] = { -+ { .compatible = "amlogic,meson6-dwmac" }, -+ { } - }; -+MODULE_DEVICE_TABLE(of, meson6_dwmac_match); -+ -+static struct platform_driver meson6_dwmac_driver = { -+ .probe = meson6_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "meson6-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = meson6_dwmac_match, -+ }, -+}; -+module_platform_driver(meson6_dwmac_driver); -+ -+MODULE_AUTHOR("Beniamino Galvani "); -+MODULE_DESCRIPTION("Amlogic Meson DWMAC glue layer"); -+MODULE_LICENSE("GPL v2"); ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c -@@ -0,0 +1,626 @@ -+/** -+ * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer -+ * -+ * Copyright (C) 2014 Chen-Zhi (Roger Chen) -+ * -+ * Chen-Zhi (Roger Chen) -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "stmmac_platform.h" -+ -+struct rk_priv_data; -+struct rk_gmac_ops { -+ void (*set_to_rgmii)(struct rk_priv_data *bsp_priv, -+ int tx_delay, int rx_delay); -+ void (*set_to_rmii)(struct rk_priv_data *bsp_priv); -+ void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed); -+ void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed); -+}; -+ -+struct rk_priv_data { -+ struct platform_device *pdev; -+ int phy_iface; -+ struct regulator *regulator; -+ const struct rk_gmac_ops *ops; -+ -+ bool clk_enabled; -+ bool clock_input; -+ -+ struct clk *clk_mac; -+ struct clk *gmac_clkin; -+ struct clk *mac_clk_rx; -+ struct clk *mac_clk_tx; -+ struct clk *clk_mac_ref; -+ struct clk *clk_mac_refout; -+ struct clk *aclk_mac; -+ struct clk *pclk_mac; -+ -+ int tx_delay; -+ int rx_delay; -+ -+ struct regmap *grf; -+}; -+ -+#define HIWORD_UPDATE(val, mask, shift) \ -+ ((val) << (shift) | (mask) << ((shift) + 16)) -+ -+#define GRF_BIT(nr) (BIT(nr) | BIT(nr+16)) -+#define GRF_CLR_BIT(nr) (BIT(nr+16)) -+ -+#define RK3288_GRF_SOC_CON1 0x0248 -+#define RK3288_GRF_SOC_CON3 0x0250 -+ -+/*RK3288_GRF_SOC_CON1*/ -+#define RK3288_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | \ -+ GRF_CLR_BIT(8)) -+#define RK3288_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | \ -+ GRF_BIT(8)) -+#define RK3288_GMAC_FLOW_CTRL GRF_BIT(9) -+#define RK3288_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9) -+#define RK3288_GMAC_SPEED_10M GRF_CLR_BIT(10) -+#define RK3288_GMAC_SPEED_100M GRF_BIT(10) -+#define RK3288_GMAC_RMII_CLK_25M GRF_BIT(11) -+#define RK3288_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11) -+#define RK3288_GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13)) -+#define RK3288_GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13)) -+#define RK3288_GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13)) -+#define RK3288_GMAC_RMII_MODE GRF_BIT(14) -+#define RK3288_GMAC_RMII_MODE_CLR GRF_CLR_BIT(14) -+ -+/*RK3288_GRF_SOC_CON3*/ -+#define RK3288_GMAC_TXCLK_DLY_ENABLE GRF_BIT(14) -+#define RK3288_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14) -+#define RK3288_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) -+#define RK3288_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) -+#define RK3288_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) -+#define RK3288_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) -+ -+static void rk3288_set_to_rgmii(struct rk_priv_data *bsp_priv, -+ int tx_delay, int rx_delay) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "Missing rockchip,grf property\n"); -+ return; -+ } -+ -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_PHY_INTF_SEL_RGMII | -+ RK3288_GMAC_RMII_MODE_CLR); -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3, -+ RK3288_GMAC_RXCLK_DLY_ENABLE | -+ RK3288_GMAC_TXCLK_DLY_ENABLE | -+ RK3288_GMAC_CLK_RX_DL_CFG(rx_delay) | -+ RK3288_GMAC_CLK_TX_DL_CFG(tx_delay)); -+} -+ -+static void rk3288_set_to_rmii(struct rk_priv_data *bsp_priv) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "Missing rockchip,grf property\n"); -+ return; -+ } -+ -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_PHY_INTF_SEL_RMII | RK3288_GMAC_RMII_MODE); -+} -+ -+static void rk3288_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "Missing rockchip,grf property\n"); -+ return; -+ } -+ -+ if (speed == 10) -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_CLK_2_5M); -+ else if (speed == 100) -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_CLK_25M); -+ else if (speed == 1000) -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_CLK_125M); -+ else -+ dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); -+} -+ -+static void rk3288_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "Missing rockchip,grf property\n"); -+ return; -+ } -+ -+ if (speed == 10) { -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_RMII_CLK_2_5M | -+ RK3288_GMAC_SPEED_10M); -+ } else if (speed == 100) { -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_RMII_CLK_25M | -+ RK3288_GMAC_SPEED_100M); -+ } else { -+ dev_err(dev, "unknown speed value for RMII! speed=%d", speed); -+ } -+} -+ -+static const struct rk_gmac_ops rk3288_ops = { -+ .set_to_rgmii = rk3288_set_to_rgmii, -+ .set_to_rmii = rk3288_set_to_rmii, -+ .set_rgmii_speed = rk3288_set_rgmii_speed, -+ .set_rmii_speed = rk3288_set_rmii_speed, -+}; -+ -+#define RK3368_GRF_SOC_CON15 0x043c -+#define RK3368_GRF_SOC_CON16 0x0440 -+ -+/* RK3368_GRF_SOC_CON15 */ -+#define RK3368_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(9) | GRF_CLR_BIT(10) | \ -+ GRF_CLR_BIT(11)) -+#define RK3368_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \ -+ GRF_BIT(11)) -+#define RK3368_GMAC_FLOW_CTRL GRF_BIT(8) -+#define RK3368_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(8) -+#define RK3368_GMAC_SPEED_10M GRF_CLR_BIT(7) -+#define RK3368_GMAC_SPEED_100M GRF_BIT(7) -+#define RK3368_GMAC_RMII_CLK_25M GRF_BIT(3) -+#define RK3368_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(3) -+#define RK3368_GMAC_CLK_125M (GRF_CLR_BIT(4) | GRF_CLR_BIT(5)) -+#define RK3368_GMAC_CLK_25M (GRF_BIT(4) | GRF_BIT(5)) -+#define RK3368_GMAC_CLK_2_5M (GRF_CLR_BIT(4) | GRF_BIT(5)) -+#define RK3368_GMAC_RMII_MODE GRF_BIT(6) -+#define RK3368_GMAC_RMII_MODE_CLR GRF_CLR_BIT(6) -+ -+/* RK3368_GRF_SOC_CON16 */ -+#define RK3368_GMAC_TXCLK_DLY_ENABLE GRF_BIT(7) -+#define RK3368_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7) -+#define RK3368_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) -+#define RK3368_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) -+#define RK3368_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) -+#define RK3368_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) -+ -+static void rk3368_set_to_rgmii(struct rk_priv_data *bsp_priv, -+ int tx_delay, int rx_delay) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_PHY_INTF_SEL_RGMII | -+ RK3368_GMAC_RMII_MODE_CLR); -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON16, -+ RK3368_GMAC_RXCLK_DLY_ENABLE | -+ RK3368_GMAC_TXCLK_DLY_ENABLE | -+ RK3368_GMAC_CLK_RX_DL_CFG(rx_delay) | -+ RK3368_GMAC_CLK_TX_DL_CFG(tx_delay)); -+} -+ -+static void rk3368_set_to_rmii(struct rk_priv_data *bsp_priv) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_PHY_INTF_SEL_RMII | RK3368_GMAC_RMII_MODE); -+} -+ -+static void rk3368_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ if (speed == 10) -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_CLK_2_5M); -+ else if (speed == 100) -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_CLK_25M); -+ else if (speed == 1000) -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_CLK_125M); -+ else -+ dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); -+} -+ -+static void rk3368_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ if (speed == 10) { -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_RMII_CLK_2_5M | -+ RK3368_GMAC_SPEED_10M); -+ } else if (speed == 100) { -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_RMII_CLK_25M | -+ RK3368_GMAC_SPEED_100M); -+ } else { -+ dev_err(dev, "unknown speed value for RMII! speed=%d", speed); -+ } -+} -+ -+static const struct rk_gmac_ops rk3368_ops = { -+ .set_to_rgmii = rk3368_set_to_rgmii, -+ .set_to_rmii = rk3368_set_to_rmii, -+ .set_rgmii_speed = rk3368_set_rgmii_speed, -+ .set_rmii_speed = rk3368_set_rmii_speed, -+}; -+ -+static int gmac_clk_init(struct rk_priv_data *bsp_priv) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ bsp_priv->clk_enabled = false; -+ -+ bsp_priv->mac_clk_rx = devm_clk_get(dev, "mac_clk_rx"); -+ if (IS_ERR(bsp_priv->mac_clk_rx)) -+ dev_err(dev, "cannot get clock %s\n", -+ "mac_clk_rx"); -+ -+ bsp_priv->mac_clk_tx = devm_clk_get(dev, "mac_clk_tx"); -+ if (IS_ERR(bsp_priv->mac_clk_tx)) -+ dev_err(dev, "cannot get clock %s\n", -+ "mac_clk_tx"); -+ -+ bsp_priv->aclk_mac = devm_clk_get(dev, "aclk_mac"); -+ if (IS_ERR(bsp_priv->aclk_mac)) -+ dev_err(dev, "cannot get clock %s\n", -+ "aclk_mac"); -+ -+ bsp_priv->pclk_mac = devm_clk_get(dev, "pclk_mac"); -+ if (IS_ERR(bsp_priv->pclk_mac)) -+ dev_err(dev, "cannot get clock %s\n", -+ "pclk_mac"); -+ -+ bsp_priv->clk_mac = devm_clk_get(dev, "stmmaceth"); -+ if (IS_ERR(bsp_priv->clk_mac)) -+ dev_err(dev, "cannot get clock %s\n", -+ "stmmaceth"); -+ -+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { -+ bsp_priv->clk_mac_ref = devm_clk_get(dev, "clk_mac_ref"); -+ if (IS_ERR(bsp_priv->clk_mac_ref)) -+ dev_err(dev, "cannot get clock %s\n", -+ "clk_mac_ref"); -+ -+ if (!bsp_priv->clock_input) { -+ bsp_priv->clk_mac_refout = -+ devm_clk_get(dev, "clk_mac_refout"); -+ if (IS_ERR(bsp_priv->clk_mac_refout)) -+ dev_err(dev, "cannot get clock %s\n", -+ "clk_mac_refout"); -+ } -+ } -+ -+ if (bsp_priv->clock_input) { -+ dev_info(dev, "clock input from PHY\n"); -+ } else { -+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) -+ clk_set_rate(bsp_priv->clk_mac, 50000000); -+ } -+ -+ return 0; -+} -+ -+static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) -+{ -+ int phy_iface = phy_iface = bsp_priv->phy_iface; -+ -+ if (enable) { -+ if (!bsp_priv->clk_enabled) { -+ if (phy_iface == PHY_INTERFACE_MODE_RMII) { -+ if (!IS_ERR(bsp_priv->mac_clk_rx)) -+ clk_prepare_enable( -+ bsp_priv->mac_clk_rx); -+ -+ if (!IS_ERR(bsp_priv->clk_mac_ref)) -+ clk_prepare_enable( -+ bsp_priv->clk_mac_ref); -+ -+ if (!IS_ERR(bsp_priv->clk_mac_refout)) -+ clk_prepare_enable( -+ bsp_priv->clk_mac_refout); -+ } -+ -+ if (!IS_ERR(bsp_priv->aclk_mac)) -+ clk_prepare_enable(bsp_priv->aclk_mac); -+ -+ if (!IS_ERR(bsp_priv->pclk_mac)) -+ clk_prepare_enable(bsp_priv->pclk_mac); -+ -+ if (!IS_ERR(bsp_priv->mac_clk_tx)) -+ clk_prepare_enable(bsp_priv->mac_clk_tx); -+ -+ /** -+ * if (!IS_ERR(bsp_priv->clk_mac)) -+ * clk_prepare_enable(bsp_priv->clk_mac); -+ */ -+ mdelay(5); -+ bsp_priv->clk_enabled = true; -+ } -+ } else { -+ if (bsp_priv->clk_enabled) { -+ if (phy_iface == PHY_INTERFACE_MODE_RMII) { -+ if (!IS_ERR(bsp_priv->mac_clk_rx)) -+ clk_disable_unprepare( -+ bsp_priv->mac_clk_rx); -+ -+ if (!IS_ERR(bsp_priv->clk_mac_ref)) -+ clk_disable_unprepare( -+ bsp_priv->clk_mac_ref); -+ -+ if (!IS_ERR(bsp_priv->clk_mac_refout)) -+ clk_disable_unprepare( -+ bsp_priv->clk_mac_refout); -+ } -+ -+ if (!IS_ERR(bsp_priv->aclk_mac)) -+ clk_disable_unprepare(bsp_priv->aclk_mac); -+ -+ if (!IS_ERR(bsp_priv->pclk_mac)) -+ clk_disable_unprepare(bsp_priv->pclk_mac); -+ -+ if (!IS_ERR(bsp_priv->mac_clk_tx)) -+ clk_disable_unprepare(bsp_priv->mac_clk_tx); -+ /** -+ * if (!IS_ERR(bsp_priv->clk_mac)) -+ * clk_disable_unprepare(bsp_priv->clk_mac); -+ */ -+ bsp_priv->clk_enabled = false; -+ } -+ } -+ -+ return 0; -+} -+ -+static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable) -+{ -+ struct regulator *ldo = bsp_priv->regulator; -+ int ret; -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (!ldo) { -+ dev_err(dev, "no regulator found\n"); -+ return -1; -+ } -+ -+ if (enable) { -+ ret = regulator_enable(ldo); -+ if (ret) -+ dev_err(dev, "fail to enable phy-supply\n"); -+ } else { -+ ret = regulator_disable(ldo); -+ if (ret) -+ dev_err(dev, "fail to disable phy-supply\n"); -+ } -+ -+ return 0; -+} -+ -+static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, -+ const struct rk_gmac_ops *ops) -+{ -+ struct rk_priv_data *bsp_priv; -+ struct device *dev = &pdev->dev; -+ int ret; -+ const char *strings = NULL; -+ int value; -+ -+ bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL); -+ if (!bsp_priv) -+ return ERR_PTR(-ENOMEM); -+ -+ bsp_priv->phy_iface = of_get_phy_mode(dev->of_node); -+ bsp_priv->ops = ops; -+ -+ bsp_priv->regulator = devm_regulator_get_optional(dev, "phy"); -+ if (IS_ERR(bsp_priv->regulator)) { -+ if (PTR_ERR(bsp_priv->regulator) == -EPROBE_DEFER) { -+ dev_err(dev, "phy regulator is not available yet, deferred probing\n"); -+ return ERR_PTR(-EPROBE_DEFER); -+ } -+ dev_err(dev, "no regulator found\n"); -+ bsp_priv->regulator = NULL; -+ } -+ -+ ret = of_property_read_string(dev->of_node, "clock_in_out", &strings); -+ if (ret) { -+ dev_err(dev, "Can not read property: clock_in_out.\n"); -+ bsp_priv->clock_input = true; -+ } else { -+ dev_info(dev, "clock input or output? (%s).\n", -+ strings); -+ if (!strcmp(strings, "input")) -+ bsp_priv->clock_input = true; -+ else -+ bsp_priv->clock_input = false; -+ } -+ -+ ret = of_property_read_u32(dev->of_node, "tx_delay", &value); -+ if (ret) { -+ bsp_priv->tx_delay = 0x30; -+ dev_err(dev, "Can not read property: tx_delay."); -+ dev_err(dev, "set tx_delay to 0x%x\n", -+ bsp_priv->tx_delay); -+ } else { -+ dev_info(dev, "TX delay(0x%x).\n", value); -+ bsp_priv->tx_delay = value; -+ } -+ -+ ret = of_property_read_u32(dev->of_node, "rx_delay", &value); -+ if (ret) { -+ bsp_priv->rx_delay = 0x10; -+ dev_err(dev, "Can not read property: rx_delay."); -+ dev_err(dev, "set rx_delay to 0x%x\n", -+ bsp_priv->rx_delay); -+ } else { -+ dev_info(dev, "RX delay(0x%x).\n", value); -+ bsp_priv->rx_delay = value; -+ } -+ -+ bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "rockchip,grf"); -+ bsp_priv->pdev = pdev; -+ -+ /*rmii or rgmii*/ -+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) { -+ dev_info(dev, "init for RGMII\n"); -+ bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay, -+ bsp_priv->rx_delay); -+ } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { -+ dev_info(dev, "init for RMII\n"); -+ bsp_priv->ops->set_to_rmii(bsp_priv); -+ } else { -+ dev_err(dev, "NO interface defined!\n"); -+ } -+ -+ gmac_clk_init(bsp_priv); -+ -+ return bsp_priv; -+} -+ -+static int rk_gmac_init(struct platform_device *pdev, void *priv) -+{ -+ struct rk_priv_data *bsp_priv = priv; -+ int ret; -+ -+ ret = phy_power_on(bsp_priv, true); -+ if (ret) -+ return ret; -+ -+ ret = gmac_clk_enable(bsp_priv, true); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static void rk_gmac_exit(struct platform_device *pdev, void *priv) -+{ -+ struct rk_priv_data *gmac = priv; -+ -+ phy_power_on(gmac, false); -+ gmac_clk_enable(gmac, false); -+} -+ -+static void rk_fix_speed(void *priv, unsigned int speed) -+{ -+ struct rk_priv_data *bsp_priv = priv; -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) -+ bsp_priv->ops->set_rgmii_speed(bsp_priv, speed); -+ else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) -+ bsp_priv->ops->set_rmii_speed(bsp_priv, speed); -+ else -+ dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface); -+} -+ -+static int rk_gmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ const struct rk_gmac_ops *data; -+ int ret; -+ -+ data = of_device_get_match_data(&pdev->dev); -+ if (!data) { -+ dev_err(&pdev->dev, "no of match data provided\n"); -+ return -EINVAL; -+ } -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ plat_dat->has_gmac = true; -+ plat_dat->init = rk_gmac_init; -+ plat_dat->exit = rk_gmac_exit; -+ plat_dat->fix_mac_speed = rk_fix_speed; -+ -+ plat_dat->bsp_priv = rk_gmac_setup(pdev, data); -+ if (IS_ERR(plat_dat->bsp_priv)) -+ return PTR_ERR(plat_dat->bsp_priv); -+ -+ ret = rk_gmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id rk_gmac_dwmac_match[] = { -+ { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops }, -+ { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); -+ -+static struct platform_driver rk_gmac_dwmac_driver = { -+ .probe = rk_gmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "rk_gmac-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = rk_gmac_dwmac_match, -+ }, -+}; -+module_platform_driver(rk_gmac_dwmac_driver); -+ -+MODULE_AUTHOR("Chen-Zhi (Roger Chen) "); -+MODULE_DESCRIPTION("Rockchip RK3288 DWMAC specific glue layer"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c -@@ -23,7 +23,9 @@ - #include - #include - #include -+ - #include "stmmac.h" -+#include "stmmac_platform.h" - - #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 - #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 -@@ -89,7 +91,9 @@ static int socfpga_dwmac_parse_data(stru - STMMAC_RESOURCE_NAME); - if (IS_ERR(dwmac->stmmac_rst)) { - dev_info(dev, "Could not get reset control!\n"); -- return -EINVAL; -+ if (PTR_ERR(dwmac->stmmac_rst) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ dwmac->stmmac_rst = NULL; - } - - dwmac->interface = of_get_phy_mode(np); -@@ -171,31 +175,6 @@ static int socfpga_dwmac_setup(struct so - return 0; - } - --static void *socfpga_dwmac_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- int ret; -- struct socfpga_dwmac *dwmac; -- -- dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL); -- if (!dwmac) -- return ERR_PTR(-ENOMEM); -- -- ret = socfpga_dwmac_parse_data(dwmac, dev); -- if (ret) { -- dev_err(dev, "Unable to parse OF data\n"); -- return ERR_PTR(ret); -- } -- -- ret = socfpga_dwmac_setup(dwmac); -- if (ret) { -- dev_err(dev, "couldn't setup SoC glue (%d)\n", ret); -- return ERR_PTR(ret); -- } -- -- return dwmac; --} -- - static void socfpga_dwmac_exit(struct platform_device *pdev, void *priv) - { - struct socfpga_dwmac *dwmac = priv; -@@ -253,9 +232,65 @@ static int socfpga_dwmac_init(struct pla - return ret; - } - --const struct stmmac_of_data socfpga_gmac_data = { -- .setup = socfpga_dwmac_probe, -- .init = socfpga_dwmac_init, -- .exit = socfpga_dwmac_exit, -- .fix_mac_speed = socfpga_dwmac_fix_mac_speed, -+static int socfpga_dwmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct device *dev = &pdev->dev; -+ int ret; -+ struct socfpga_dwmac *dwmac; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL); -+ if (!dwmac) -+ return -ENOMEM; -+ -+ ret = socfpga_dwmac_parse_data(dwmac, dev); -+ if (ret) { -+ dev_err(dev, "Unable to parse OF data\n"); -+ return ret; -+ } -+ -+ ret = socfpga_dwmac_setup(dwmac); -+ if (ret) { -+ dev_err(dev, "couldn't setup SoC glue (%d)\n", ret); -+ return ret; -+ } -+ -+ plat_dat->bsp_priv = dwmac; -+ plat_dat->init = socfpga_dwmac_init; -+ plat_dat->exit = socfpga_dwmac_exit; -+ plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; -+ -+ ret = socfpga_dwmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id socfpga_dwmac_match[] = { -+ { .compatible = "altr,socfpga-stmmac" }, -+ { } - }; -+MODULE_DEVICE_TABLE(of, socfpga_dwmac_match); -+ -+static struct platform_driver socfpga_dwmac_driver = { -+ .probe = socfpga_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "socfpga-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = socfpga_dwmac_match, -+ }, -+}; -+module_platform_driver(socfpga_dwmac_driver); -+ -+MODULE_LICENSE("GPL v2"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c -@@ -1,4 +1,4 @@ --/** -+/* - * dwmac-sti.c - STMicroelectronics DWMAC Specific Glue layer - * - * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited -@@ -17,11 +17,15 @@ - #include - #include - #include -+#include - #include - #include - #include -+#include - #include - -+#include "stmmac_platform.h" -+ - #define DWMAC_125MHZ 125000000 - #define DWMAC_50MHZ 50000000 - #define DWMAC_25MHZ 25000000 -@@ -35,9 +39,8 @@ - #define IS_PHY_IF_MODE_GBIT(iface) (IS_PHY_IF_MODE_RGMII(iface) || \ - iface == PHY_INTERFACE_MODE_GMII) - --/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families) */ -- --/** -+/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families) -+ * - * Below table summarizes the clock requirement and clock sources for - * supported phy interface modes with link speeds. - * ________________________________________________ -@@ -76,9 +79,7 @@ - #define STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7) - #define STIH4XX_ETH_SEL_TXCLK_NOT_CLK125 BIT(6) - --/* STiD127 register definitions */ -- --/** -+/* STiD127 register definitions - *----------------------- - * src |BIT(6)| BIT(7)| - *----------------------- -@@ -104,13 +105,13 @@ - #define EN_MASK GENMASK(1, 1) - #define EN BIT(1) - --/** -+/* - * 3 bits [4:2] - * 000-GMII/MII - * 001-RGMII - * 010-SGMII - * 100-RMII --*/ -+ */ - #define MII_PHY_SEL_MASK GENMASK(4, 2) - #define ETH_PHY_SEL_RMII BIT(4) - #define ETH_PHY_SEL_SGMII BIT(3) -@@ -123,11 +124,16 @@ struct sti_dwmac { - bool ext_phyclk; /* Clock from external PHY */ - u32 tx_retime_src; /* TXCLK Retiming*/ - struct clk *clk; /* PHY clock */ -- int ctrl_reg; /* GMAC glue-logic control register */ -+ u32 ctrl_reg; /* GMAC glue-logic control register */ - int clk_sel_reg; /* GMAC ext clk selection register */ - struct device *dev; - struct regmap *regmap; - u32 speed; -+ void (*fix_retime_src)(void *priv, unsigned int speed); -+}; -+ -+struct sti_dwmac_of_data { -+ void (*fix_retime_src)(void *priv, unsigned int speed); - }; - - static u32 phy_intf_sels[] = { -@@ -222,8 +228,9 @@ static void stid127_fix_retime_src(void - regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val); - } - --static void sti_dwmac_ctrl_init(struct sti_dwmac *dwmac) -+static int sti_dwmac_init(struct platform_device *pdev, void *priv) - { -+ struct sti_dwmac *dwmac = priv; - struct regmap *regmap = dwmac->regmap; - int iface = dwmac->interface; - struct device *dev = dwmac->dev; -@@ -241,28 +248,8 @@ static void sti_dwmac_ctrl_init(struct s - - val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII; - regmap_update_bits(regmap, reg, ENMII_MASK, val); --} - --static int stix4xx_init(struct platform_device *pdev, void *priv) --{ -- struct sti_dwmac *dwmac = priv; -- u32 spd = dwmac->speed; -- -- sti_dwmac_ctrl_init(dwmac); -- -- stih4xx_fix_retime_src(priv, spd); -- -- return 0; --} -- --static int stid127_init(struct platform_device *pdev, void *priv) --{ -- struct sti_dwmac *dwmac = priv; -- u32 spd = dwmac->speed; -- -- sti_dwmac_ctrl_init(dwmac); -- -- stid127_fix_retime_src(priv, spd); -+ dwmac->fix_retime_src(priv, dwmac->speed); - - return 0; - } -@@ -286,11 +273,6 @@ static int sti_dwmac_parse_data(struct s - if (!np) - return -EINVAL; - -- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-ethconf"); -- if (!res) -- return -ENODATA; -- dwmac->ctrl_reg = res->start; -- - /* clk selection from extra syscfg register */ - dwmac->clk_sel_reg = -ENXIO; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf"); -@@ -301,6 +283,12 @@ static int sti_dwmac_parse_data(struct s - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - -+ err = of_property_read_u32_index(np, "st,syscon", 1, &dwmac->ctrl_reg); -+ if (err) { -+ dev_err(dev, "Can't get sysconfig ctrl offset (%d)\n", err); -+ return err; -+ } -+ - dwmac->dev = dev; - dwmac->interface = of_get_phy_mode(np); - dwmac->regmap = regmap; -@@ -310,16 +298,16 @@ static int sti_dwmac_parse_data(struct s - - if (IS_PHY_IF_MODE_GBIT(dwmac->interface)) { - const char *rs; -- dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN; - - err = of_property_read_string(np, "st,tx-retime-src", &rs); -- if (err < 0) -+ if (err < 0) { - dev_warn(dev, "Use internal clock source\n"); -- -- if (!strcasecmp(rs, "clk_125")) -+ dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN; -+ } else if (!strcasecmp(rs, "clk_125")) { - dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125; -- else if (!strcasecmp(rs, "txclk")) -+ } else if (!strcasecmp(rs, "txclk")) { - dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK; -+ } - - dwmac->speed = SPEED_1000; - } -@@ -333,34 +321,80 @@ static int sti_dwmac_parse_data(struct s - return 0; - } - --static void *sti_dwmac_setup(struct platform_device *pdev) -+static int sti_dwmac_probe(struct platform_device *pdev) - { -+ struct plat_stmmacenet_data *plat_dat; -+ const struct sti_dwmac_of_data *data; -+ struct stmmac_resources stmmac_res; - struct sti_dwmac *dwmac; - int ret; - -+ data = of_device_get_match_data(&pdev->dev); -+ if (!data) { -+ dev_err(&pdev->dev, "No OF match data provided\n"); -+ return -EINVAL; -+ } -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ - dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); - if (!dwmac) -- return ERR_PTR(-ENOMEM); -+ return -ENOMEM; - - ret = sti_dwmac_parse_data(dwmac, pdev); - if (ret) { - dev_err(&pdev->dev, "Unable to parse OF data\n"); -- return ERR_PTR(ret); -+ return ret; - } - -- return dwmac; -+ dwmac->fix_retime_src = data->fix_retime_src; -+ -+ plat_dat->bsp_priv = dwmac; -+ plat_dat->init = sti_dwmac_init; -+ plat_dat->exit = sti_dwmac_exit; -+ plat_dat->fix_mac_speed = data->fix_retime_src; -+ -+ ret = sti_dwmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - } - --const struct stmmac_of_data stih4xx_dwmac_data = { -- .fix_mac_speed = stih4xx_fix_retime_src, -- .setup = sti_dwmac_setup, -- .init = stix4xx_init, -- .exit = sti_dwmac_exit, -+static const struct sti_dwmac_of_data stih4xx_dwmac_data = { -+ .fix_retime_src = stih4xx_fix_retime_src, - }; - --const struct stmmac_of_data stid127_dwmac_data = { -- .fix_mac_speed = stid127_fix_retime_src, -- .setup = sti_dwmac_setup, -- .init = stid127_init, -- .exit = sti_dwmac_exit, -+static const struct sti_dwmac_of_data stid127_dwmac_data = { -+ .fix_retime_src = stid127_fix_retime_src, - }; -+ -+static const struct of_device_id sti_dwmac_match[] = { -+ { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, -+ { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data}, -+ { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, -+ { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, sti_dwmac_match); -+ -+static struct platform_driver sti_dwmac_driver = { -+ .probe = sti_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "sti-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = sti_dwmac_match, -+ }, -+}; -+module_platform_driver(sti_dwmac_driver); -+ -+MODULE_AUTHOR("Srinivas Kandagatla "); -+MODULE_DESCRIPTION("STMicroelectronics DWMAC Specific Glue layer"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c -@@ -1,4 +1,4 @@ --/** -+/* - * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer - * - * Copyright (C) 2013 Chen-Yu Tsai -@@ -18,10 +18,14 @@ - - #include - #include -+#include - #include -+#include - #include - #include - -+#include "stmmac_platform.h" -+ - struct sunxi_priv_data { - int interface; - int clk_enabled; -@@ -29,35 +33,6 @@ struct sunxi_priv_data { - struct regulator *regulator; - }; - --static void *sun7i_gmac_setup(struct platform_device *pdev) --{ -- struct sunxi_priv_data *gmac; -- struct device *dev = &pdev->dev; -- -- gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); -- if (!gmac) -- return ERR_PTR(-ENOMEM); -- -- gmac->interface = of_get_phy_mode(dev->of_node); -- -- gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); -- if (IS_ERR(gmac->tx_clk)) { -- dev_err(dev, "could not get tx clock\n"); -- return gmac->tx_clk; -- } -- -- /* Optional regulator for PHY */ -- gmac->regulator = devm_regulator_get_optional(dev, "phy"); -- if (IS_ERR(gmac->regulator)) { -- if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) -- return ERR_PTR(-EPROBE_DEFER); -- dev_info(dev, "no regulator found\n"); -- gmac->regulator = NULL; -- } -- -- return gmac; --} -- - #define SUN7I_GMAC_GMII_RGMII_RATE 125000000 - #define SUN7I_GMAC_MII_RATE 25000000 - -@@ -128,13 +103,76 @@ static void sun7i_fix_speed(void *priv, - } - } - --/* of_data specifying hardware features and callbacks. -- * hardware features were copied from Allwinner drivers. */ --const struct stmmac_of_data sun7i_gmac_data = { -- .has_gmac = 1, -- .tx_coe = 1, -- .fix_mac_speed = sun7i_fix_speed, -- .setup = sun7i_gmac_setup, -- .init = sun7i_gmac_init, -- .exit = sun7i_gmac_exit, -+static int sun7i_gmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct sunxi_priv_data *gmac; -+ struct device *dev = &pdev->dev; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); -+ if (!gmac) -+ return -ENOMEM; -+ -+ gmac->interface = of_get_phy_mode(dev->of_node); -+ -+ gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); -+ if (IS_ERR(gmac->tx_clk)) { -+ dev_err(dev, "could not get tx clock\n"); -+ return PTR_ERR(gmac->tx_clk); -+ } -+ -+ /* Optional regulator for PHY */ -+ gmac->regulator = devm_regulator_get_optional(dev, "phy"); -+ if (IS_ERR(gmac->regulator)) { -+ if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ dev_info(dev, "no regulator found\n"); -+ gmac->regulator = NULL; -+ } -+ -+ /* platform data specifying hardware features and callbacks. -+ * hardware features were copied from Allwinner drivers. */ -+ plat_dat->tx_coe = 1; -+ plat_dat->has_gmac = true; -+ plat_dat->bsp_priv = gmac; -+ plat_dat->init = sun7i_gmac_init; -+ plat_dat->exit = sun7i_gmac_exit; -+ plat_dat->fix_mac_speed = sun7i_fix_speed; -+ -+ ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id sun7i_dwmac_match[] = { -+ { .compatible = "allwinner,sun7i-a20-gmac" }, -+ { } - }; -+MODULE_DEVICE_TABLE(of, sun7i_dwmac_match); -+ -+static struct platform_driver sun7i_dwmac_driver = { -+ .probe = sun7i_gmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "sun7i-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = sun7i_dwmac_match, -+ }, -+}; -+module_platform_driver(sun7i_dwmac_driver); -+ -+MODULE_AUTHOR("Chen-Yu Tsai "); -+MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h -@@ -172,6 +172,7 @@ enum inter_frame_gap { - /* GMAC FLOW CTRL defines */ - #define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ - #define GMAC_FLOW_CTRL_PT_SHIFT 16 -+#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */ - #define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ - #define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ - #define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ -@@ -246,6 +247,56 @@ enum ttc_control { - #define DMA_CONTROL_FEF 0x00000080 - #define DMA_CONTROL_FUF 0x00000040 - -+/* Receive flow control activation field -+ * RFA field in DMA control register, bits 23,10:9 -+ */ -+#define DMA_CONTROL_RFA_MASK 0x00800600 -+ -+/* Receive flow control deactivation field -+ * RFD field in DMA control register, bits 22,12:11 -+ */ -+#define DMA_CONTROL_RFD_MASK 0x00401800 -+ -+/* RFD and RFA fields are encoded as follows -+ * -+ * Bit Field -+ * 0,00 - Full minus 1KB (only valid when rxfifo >= 4KB and EFC enabled) -+ * 0,01 - Full minus 2KB (only valid when rxfifo >= 4KB and EFC enabled) -+ * 0,10 - Full minus 3KB (only valid when rxfifo >= 4KB and EFC enabled) -+ * 0,11 - Full minus 4KB (only valid when rxfifo > 4KB and EFC enabled) -+ * 1,00 - Full minus 5KB (only valid when rxfifo > 8KB and EFC enabled) -+ * 1,01 - Full minus 6KB (only valid when rxfifo > 8KB and EFC enabled) -+ * 1,10 - Full minus 7KB (only valid when rxfifo > 8KB and EFC enabled) -+ * 1,11 - Reserved -+ * -+ * RFD should always be > RFA for a given FIFO size. RFD == RFA may work, -+ * but packet throughput performance may not be as expected. -+ * -+ * Be sure that bit 3 in GMAC Register 6 is set for Unicast Pause frame -+ * detection (IEEE Specification Requirement, Annex 31B, 31B.1, Pause -+ * Description). -+ * -+ * Be sure that DZPA (bit 7 in Flow Control Register, GMAC Register 6), -+ * is set to 0. This allows pause frames with a quanta of 0 to be sent -+ * as an XOFF message to the link peer. -+ */ -+ -+#define RFA_FULL_MINUS_1K 0x00000000 -+#define RFA_FULL_MINUS_2K 0x00000200 -+#define RFA_FULL_MINUS_3K 0x00000400 -+#define RFA_FULL_MINUS_4K 0x00000600 -+#define RFA_FULL_MINUS_5K 0x00800000 -+#define RFA_FULL_MINUS_6K 0x00800200 -+#define RFA_FULL_MINUS_7K 0x00800400 -+ -+#define RFD_FULL_MINUS_1K 0x00000000 -+#define RFD_FULL_MINUS_2K 0x00000800 -+#define RFD_FULL_MINUS_3K 0x00001000 -+#define RFD_FULL_MINUS_4K 0x00001800 -+#define RFD_FULL_MINUS_5K 0x00400000 -+#define RFD_FULL_MINUS_6K 0x00400800 -+#define RFD_FULL_MINUS_7K 0x00401000 -+ - enum rtc_control { - DMA_CONTROL_RTC_64 = 0x00000000, - DMA_CONTROL_RTC_32 = 0x00000008, ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c -@@ -201,7 +201,10 @@ static void dwmac1000_flow_ctrl(struct m - unsigned int fc, unsigned int pause_time) - { - void __iomem *ioaddr = hw->pcsr; -- unsigned int flow = 0; -+ /* Set flow such that DZPQ in Mac Register 6 is 0, -+ * and unicast pause detect is enabled. -+ */ -+ unsigned int flow = GMAC_FLOW_CTRL_UP; - - pr_debug("GMAC Flow-Control:\n"); - if (fc & FLOW_RX) { ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -@@ -70,10 +70,6 @@ static int dwmac1000_dma_init(void __iom - if (mb) - value |= DMA_BUS_MODE_MB; - --#ifdef CONFIG_STMMAC_DA -- value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */ --#endif -- - if (atds) - value |= DMA_BUS_MODE_ATDS; - -@@ -110,8 +106,29 @@ static int dwmac1000_dma_init(void __iom - return 0; - } - -+static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz) -+{ -+ csr6 &= ~DMA_CONTROL_RFA_MASK; -+ csr6 &= ~DMA_CONTROL_RFD_MASK; -+ -+ /* Leave flow control disabled if receive fifo size is less than -+ * 4K or 0. Otherwise, send XOFF when fifo is 1K less than full, -+ * and send XON when 2K less than full. -+ */ -+ if (rxfifosz < 4096) { -+ csr6 &= ~DMA_CONTROL_EFC; -+ pr_debug("GMAC: disabling flow control, rxfifo too small(%d)\n", -+ rxfifosz); -+ } else { -+ csr6 |= DMA_CONTROL_EFC; -+ csr6 |= RFA_FULL_MINUS_1K; -+ csr6 |= RFD_FULL_MINUS_2K; -+ } -+ return csr6; -+} -+ - static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, -- int rxmode) -+ int rxmode, int rxfifosz) - { - u32 csr6 = readl(ioaddr + DMA_CONTROL); - -@@ -157,6 +174,9 @@ static void dwmac1000_dma_operation_mode - csr6 |= DMA_CONTROL_RTC_128; - } - -+ /* Configure flow control based on rx fifo size */ -+ csr6 = dwmac1000_configure_fc(csr6, rxfifosz); -+ - writel(csr6, ioaddr + DMA_CONTROL); - } - ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -@@ -72,7 +72,7 @@ static int dwmac100_dma_init(void __iome - * control register. - */ - static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode, -- int rxmode) -+ int rxmode, int rxfifosz) - { - u32 csr6 = readl(ioaddr + DMA_CONTROL); - ---- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c -+++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c -@@ -73,7 +73,7 @@ - #define MMC_RX_OCTETCOUNT_G 0x00000188 - #define MMC_RX_BROADCASTFRAME_G 0x0000018c - #define MMC_RX_MULTICASTFRAME_G 0x00000190 --#define MMC_RX_CRC_ERRROR 0x00000194 -+#define MMC_RX_CRC_ERROR 0x00000194 - #define MMC_RX_ALIGN_ERROR 0x00000198 - #define MMC_RX_RUN_ERROR 0x0000019C - #define MMC_RX_JABBER_ERROR 0x000001A0 -@@ -196,7 +196,7 @@ void dwmac_mmc_read(void __iomem *ioaddr - mmc->mmc_rx_octetcount_g += readl(ioaddr + MMC_RX_OCTETCOUNT_G); - mmc->mmc_rx_broadcastframe_g += readl(ioaddr + MMC_RX_BROADCASTFRAME_G); - mmc->mmc_rx_multicastframe_g += readl(ioaddr + MMC_RX_MULTICASTFRAME_G); -- mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERRROR); -+ mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERROR); - mmc->mmc_rx_align_error += readl(ioaddr + MMC_RX_ALIGN_ERROR); - mmc->mmc_rx_run_error += readl(ioaddr + MMC_RX_RUN_ERROR); - mmc->mmc_rx_jabber_error += readl(ioaddr + MMC_RX_JABBER_ERROR); ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h -@@ -34,6 +34,14 @@ - #include - #include - -+struct stmmac_resources { -+ void __iomem *addr; -+ const char *mac; -+ int wol_irq; -+ int lpi_irq; -+ int irq; -+}; -+ - struct stmmac_tx_info { - dma_addr_t buf; - bool map_as_page; -@@ -97,6 +105,7 @@ struct stmmac_priv { - int wolopts; - int wol_irq; - struct clk *stmmac_clk; -+ struct clk *pclk; - struct reset_control *stmmac_rst; - int clk_csr; - struct timer_list eee_ctrl_timer; -@@ -116,97 +125,28 @@ struct stmmac_priv { - int use_riwt; - int irq_wake; - spinlock_t ptp_lock; -+ -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *dbgfs_dir; -+ struct dentry *dbgfs_rings_status; -+ struct dentry *dbgfs_dma_cap; -+#endif - }; - - int stmmac_mdio_unregister(struct net_device *ndev); - int stmmac_mdio_register(struct net_device *ndev); - int stmmac_mdio_reset(struct mii_bus *mii); - void stmmac_set_ethtool_ops(struct net_device *netdev); --extern const struct stmmac_desc_ops enh_desc_ops; --extern const struct stmmac_desc_ops ndesc_ops; --extern const struct stmmac_hwtimestamp stmmac_ptp; -+ - int stmmac_ptp_register(struct stmmac_priv *priv); - void stmmac_ptp_unregister(struct stmmac_priv *priv); - int stmmac_resume(struct net_device *ndev); - int stmmac_suspend(struct net_device *ndev); - int stmmac_dvr_remove(struct net_device *ndev); --struct stmmac_priv *stmmac_dvr_probe(struct device *device, -- struct plat_stmmacenet_data *plat_dat, -- void __iomem *addr); -+int stmmac_dvr_probe(struct device *device, -+ struct plat_stmmacenet_data *plat_dat, -+ struct stmmac_resources *res); - void stmmac_disable_eee_mode(struct stmmac_priv *priv); - bool stmmac_eee_init(struct stmmac_priv *priv); - --#ifdef CONFIG_STMMAC_PLATFORM --#ifdef CONFIG_DWMAC_MESON --extern const struct stmmac_of_data meson6_dwmac_data; --#endif --#ifdef CONFIG_DWMAC_SUNXI --extern const struct stmmac_of_data sun7i_gmac_data; --#endif --#ifdef CONFIG_DWMAC_STI --extern const struct stmmac_of_data stih4xx_dwmac_data; --extern const struct stmmac_of_data stid127_dwmac_data; --#endif --#ifdef CONFIG_DWMAC_SOCFPGA --extern const struct stmmac_of_data socfpga_gmac_data; --#endif --extern struct platform_driver stmmac_pltfr_driver; --static inline int stmmac_register_platform(void) --{ -- int err; -- -- err = platform_driver_register(&stmmac_pltfr_driver); -- if (err) -- pr_err("stmmac: failed to register the platform driver\n"); -- -- return err; --} -- --static inline void stmmac_unregister_platform(void) --{ -- platform_driver_unregister(&stmmac_pltfr_driver); --} --#else --static inline int stmmac_register_platform(void) --{ -- pr_debug("stmmac: do not register the platf driver\n"); -- -- return 0; --} -- --static inline void stmmac_unregister_platform(void) --{ --} --#endif /* CONFIG_STMMAC_PLATFORM */ -- --#ifdef CONFIG_STMMAC_PCI --extern struct pci_driver stmmac_pci_driver; --static inline int stmmac_register_pci(void) --{ -- int err; -- -- err = pci_register_driver(&stmmac_pci_driver); -- if (err) -- pr_err("stmmac: failed to register the PCI driver\n"); -- -- return err; --} -- --static inline void stmmac_unregister_pci(void) --{ -- pci_unregister_driver(&stmmac_pci_driver); --} --#else --static inline int stmmac_register_pci(void) --{ -- pr_debug("stmmac: do not register the PCI driver\n"); -- -- return 0; --} -- --static inline void stmmac_unregister_pci(void) --{ --} --#endif /* CONFIG_STMMAC_PCI */ -- - #endif /* __STMMAC_H__ */ ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c -@@ -696,7 +696,7 @@ static int stmmac_set_coalesce(struct ne - (ec->tx_max_coalesced_frames == 0)) - return -EINVAL; - -- if ((ec->tx_coalesce_usecs > STMMAC_COAL_TX_TIMER) || -+ if ((ec->tx_coalesce_usecs > STMMAC_MAX_COAL_TX_TICK) || - (ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES)) - return -EINVAL; - ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -44,14 +44,15 @@ - #include - #include - #include --#ifdef CONFIG_STMMAC_DEBUG_FS -+#ifdef CONFIG_DEBUG_FS - #include - #include --#endif /* CONFIG_STMMAC_DEBUG_FS */ -+#endif /* CONFIG_DEBUG_FS */ - #include - #include "stmmac_ptp.h" - #include "stmmac.h" - #include -+#include - - #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) - -@@ -116,17 +117,17 @@ MODULE_PARM_DESC(chain_mode, "To use cha - - static irqreturn_t stmmac_interrupt(int irq, void *dev_id); - --#ifdef CONFIG_STMMAC_DEBUG_FS -+#ifdef CONFIG_DEBUG_FS - static int stmmac_init_fs(struct net_device *dev); --static void stmmac_exit_fs(void); -+static void stmmac_exit_fs(struct net_device *dev); - #endif - - #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) - - /** - * stmmac_verify_args - verify the driver parameters. -- * Description: it verifies if some wrong parameter is passed to the driver. -- * Note that wrong parameters are replaced with the default values. -+ * Description: it checks the driver parameters and set a default in case of -+ * errors. - */ - static void stmmac_verify_args(void) - { -@@ -191,14 +192,8 @@ static void stmmac_clk_csr_set(struct st - - static void print_pkt(unsigned char *buf, int len) - { -- int j; -- pr_debug("len = %d byte, buf addr: 0x%p", len, buf); -- for (j = 0; j < len; j++) { -- if ((j % 16) == 0) -- pr_debug("\n %03x:", j); -- pr_debug(" %02x", buf[j]); -- } -- pr_debug("\n"); -+ pr_debug("len = %d byte, buf addr: 0x%p\n", len, buf); -+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); - } - - /* minimum number of free TX descriptors required to wake up TX process */ -@@ -210,7 +205,7 @@ static inline u32 stmmac_tx_avail(struct - } - - /** -- * stmmac_hw_fix_mac_speed: callback for speed selection -+ * stmmac_hw_fix_mac_speed - callback for speed selection - * @priv: driver private structure - * Description: on some platforms (e.g. ST), some HW system configuraton - * registers have to be set according to the link speed negotiated. -@@ -224,9 +219,10 @@ static inline void stmmac_hw_fix_mac_spe - } - - /** -- * stmmac_enable_eee_mode: Check and enter in LPI mode -+ * stmmac_enable_eee_mode - check and enter in LPI mode - * @priv: driver private structure -- * Description: this function is to verify and enter in LPI mode for EEE. -+ * Description: this function is to verify and enter in LPI mode in case of -+ * EEE. - */ - static void stmmac_enable_eee_mode(struct stmmac_priv *priv) - { -@@ -237,7 +233,7 @@ static void stmmac_enable_eee_mode(struc - } - - /** -- * stmmac_disable_eee_mode: disable/exit from EEE -+ * stmmac_disable_eee_mode - disable and exit from LPI mode - * @priv: driver private structure - * Description: this function is to exit and disable EEE in case of - * LPI state is true. This is called by the xmit. -@@ -250,7 +246,7 @@ void stmmac_disable_eee_mode(struct stmm - } - - /** -- * stmmac_eee_ctrl_timer: EEE TX SW timer. -+ * stmmac_eee_ctrl_timer - EEE TX SW timer. - * @arg : data hook - * Description: - * if there is no data transfer and if we are not in LPI state, -@@ -265,13 +261,12 @@ static void stmmac_eee_ctrl_timer(unsign - } - - /** -- * stmmac_eee_init: init EEE -+ * stmmac_eee_init - init EEE - * @priv: driver private structure - * Description: -- * If the EEE support has been enabled while configuring the driver, -- * if the GMAC actually supports the EEE (from the HW cap reg) and the -- * phy can also manage EEE, so enable the LPI state and start the timer -- * to verify if the tx path can enter in LPI state. -+ * if the GMAC supports the EEE (from the HW cap reg) and the phy device -+ * can also manage EEE, this function enable the LPI state and start related -+ * timer. - */ - bool stmmac_eee_init(struct stmmac_priv *priv) - { -@@ -316,11 +311,11 @@ bool stmmac_eee_init(struct stmmac_priv - spin_lock_irqsave(&priv->lock, flags); - if (!priv->eee_active) { - priv->eee_active = 1; -- init_timer(&priv->eee_ctrl_timer); -- priv->eee_ctrl_timer.function = stmmac_eee_ctrl_timer; -- priv->eee_ctrl_timer.data = (unsigned long)priv; -- priv->eee_ctrl_timer.expires = STMMAC_LPI_T(eee_timer); -- add_timer(&priv->eee_ctrl_timer); -+ setup_timer(&priv->eee_ctrl_timer, -+ stmmac_eee_ctrl_timer, -+ (unsigned long)priv); -+ mod_timer(&priv->eee_ctrl_timer, -+ STMMAC_LPI_T(eee_timer)); - - priv->hw->mac->set_eee_timer(priv->hw, - STMMAC_DEFAULT_LIT_LS, -@@ -338,7 +333,7 @@ out: - return ret; - } - --/* stmmac_get_tx_hwtstamp: get HW TX timestamps -+/* stmmac_get_tx_hwtstamp - get HW TX timestamps - * @priv: driver private structure - * @entry : descriptor index to be used. - * @skb : the socket buffer -@@ -380,7 +375,7 @@ static void stmmac_get_tx_hwtstamp(struc - return; - } - --/* stmmac_get_rx_hwtstamp: get HW RX timestamps -+/* stmmac_get_rx_hwtstamp - get HW RX timestamps - * @priv: driver private structure - * @entry : descriptor index to be used. - * @skb : the socket buffer -@@ -615,7 +610,7 @@ static int stmmac_hwtstamp_ioctl(struct - * where, freq_div_ratio = clk_ptp_ref_i/50MHz - * hence, addend = ((2^32) * 50MHz)/clk_ptp_ref_i; - * NOTE: clk_ptp_ref_i should be >= 50MHz to -- * achive 20ns accuracy. -+ * achieve 20ns accuracy. - * - * 2^x * y == (y << x), hence - * 2^32 * 50000000 ==> (50000000 << 32) -@@ -636,11 +631,11 @@ static int stmmac_hwtstamp_ioctl(struct - } - - /** -- * stmmac_init_ptp: init PTP -+ * stmmac_init_ptp - init PTP - * @priv: driver private structure -- * Description: this is to verify if the HW supports the PTPv1 or v2. -+ * Description: this is to verify if the HW supports the PTPv1 or PTPv2. - * This is done by looking at the HW cap. register. -- * Also it registers the ptp driver. -+ * This function also registers the ptp driver. - */ - static int stmmac_init_ptp(struct stmmac_priv *priv) - { -@@ -682,9 +677,13 @@ static void stmmac_release_ptp(struct st - } - - /** -- * stmmac_adjust_link -+ * stmmac_adjust_link - adjusts the link parameters - * @dev: net device structure -- * Description: it adjusts the link parameters. -+ * Description: this is the helper called by the physical abstraction layer -+ * drivers to communicate the phy link status. According the speed and duplex -+ * this driver can invoke registered glue-logic as well. -+ * It also invoke the eee initialization because it could happen when switch -+ * on different networks (that are eee capable). - */ - static void stmmac_adjust_link(struct net_device *dev) - { -@@ -774,7 +773,7 @@ static void stmmac_adjust_link(struct ne - } - - /** -- * stmmac_check_pcs_mode: verify if RGMII/SGMII is supported -+ * stmmac_check_pcs_mode - verify if RGMII/SGMII is supported - * @priv: driver private structure - * Description: this is to verify if the HW supports the PCS. - * Physical Coding Sublayer (PCS) interface that can be used when the MAC is -@@ -818,21 +817,31 @@ static int stmmac_init_phy(struct net_de - priv->speed = 0; - priv->oldduplex = -1; - -- if (priv->plat->phy_bus_name) -- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", -- priv->plat->phy_bus_name, priv->plat->bus_id); -- else -- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", -- priv->plat->bus_id); -+ if (priv->plat->phy_node) { -+ phydev = of_phy_connect(dev, priv->plat->phy_node, -+ &stmmac_adjust_link, 0, interface); -+ } else { -+ if (priv->plat->phy_bus_name) -+ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", -+ priv->plat->phy_bus_name, priv->plat->bus_id); -+ else -+ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", -+ priv->plat->bus_id); - -- snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, -- priv->plat->phy_addr); -- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt); -+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, -+ priv->plat->phy_addr); -+ pr_debug("stmmac_init_phy: trying to attach to %s\n", -+ phy_id_fmt); - -- phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface); -+ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, -+ interface); -+ } - -- if (IS_ERR(phydev)) { -+ if (IS_ERR_OR_NULL(phydev)) { - pr_err("%s: Could not attach to PHY\n", dev->name); -+ if (!phydev) -+ return -ENODEV; -+ - return PTR_ERR(phydev); - } - -@@ -850,7 +859,7 @@ static int stmmac_init_phy(struct net_de - * device as well. - * Note: phydev->phy_id is the result of reading the UID PHY registers. - */ -- if (phydev->phy_id == 0) { -+ if (!priv->plat->phy_node && phydev->phy_id == 0) { - phy_disconnect(phydev); - return -ENODEV; - } -@@ -863,7 +872,7 @@ static int stmmac_init_phy(struct net_de - } - - /** -- * stmmac_display_ring: display ring -+ * stmmac_display_ring - display ring - * @head: pointer to the head of the ring passed. - * @size: size of the ring. - * @extend_desc: to verify if extended descriptors are used. -@@ -931,7 +940,7 @@ static int stmmac_set_bfsize(int mtu, in - } - - /** -- * stmmac_clear_descriptors: clear descriptors -+ * stmmac_clear_descriptors - clear descriptors - * @priv: driver private structure - * Description: this function is called to clear the tx and rx descriptors - * in case of both basic and extended descriptors are used. -@@ -963,18 +972,25 @@ static void stmmac_clear_descriptors(str - (i == txsize - 1)); - } - -+/** -+ * stmmac_init_rx_buffers - init the RX descriptor buffer. -+ * @priv: driver private structure -+ * @p: descriptor pointer -+ * @i: descriptor index -+ * @flags: gfp flag. -+ * Description: this function is called to allocate a receive buffer, perform -+ * the DMA mapping and init the descriptor. -+ */ - static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, - int i, gfp_t flags) - { - struct sk_buff *skb; - -- skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN, -- flags); -+ skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); - if (!skb) { - pr_err("%s: Rx init fails; skb is NULL\n", __func__); - return -ENOMEM; - } -- skb_reserve(skb, NET_IP_ALIGN); - priv->rx_skbuff[i] = skb; - priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, - priv->dma_buf_sz, -@@ -1007,7 +1023,8 @@ static void stmmac_free_rx_buffers(struc - /** - * init_dma_desc_rings - init the RX/TX descriptor rings - * @dev: net device structure -- * Description: this function initializes the DMA RX/TX descriptors -+ * @flags: gfp flag. -+ * Description: this function initializes the DMA RX/TX descriptors - * and allocates the socket buffers. It suppors the chained and ring - * modes. - */ -@@ -1089,6 +1106,7 @@ static int init_dma_desc_rings(struct ne - - priv->dirty_tx = 0; - priv->cur_tx = 0; -+ netdev_reset_queue(priv->dev); - - stmmac_clear_descriptors(priv); - -@@ -1144,6 +1162,14 @@ static void dma_free_tx_skbufs(struct st - } - } - -+/** -+ * alloc_dma_desc_resources - alloc TX/RX resources. -+ * @priv: private structure -+ * Description: according to which descriptor can be used (extend or basic) -+ * this function allocates the resources for TX and RX paths. In case of -+ * reception, for example, it pre-allocated the RX socket buffer in order to -+ * allow zero-copy mechanism. -+ */ - static int alloc_dma_desc_resources(struct stmmac_priv *priv) - { - unsigned int txsize = priv->dma_tx_size; -@@ -1255,13 +1281,15 @@ static void free_dma_desc_resources(stru - /** - * stmmac_dma_operation_mode - HW DMA operation mode - * @priv: driver private structure -- * Description: it sets the DMA operation mode: tx/rx DMA thresholds -- * or Store-And-Forward capability. -+ * Description: it is used for configuring the DMA operation mode register in -+ * order to program the tx/rx DMA thresholds or Store-And-Forward mode. - */ - static void stmmac_dma_operation_mode(struct stmmac_priv *priv) - { -+ int rxfifosz = priv->plat->rx_fifo_size; -+ - if (priv->plat->force_thresh_dma_mode) -- priv->hw->dma->dma_mode(priv->ioaddr, tc, tc); -+ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz); - else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) { - /* - * In case of GMAC, SF mode can be enabled -@@ -1270,20 +1298,23 @@ static void stmmac_dma_operation_mode(st - * 2) There is no bugged Jumbo frame support - * that needs to not insert csum in the TDES. - */ -- priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE); -- tc = SF_DMA_MODE; -+ priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE, -+ rxfifosz); -+ priv->xstats.threshold = SF_DMA_MODE; - } else -- priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE); -+ priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE, -+ rxfifosz); - } - - /** -- * stmmac_tx_clean: -+ * stmmac_tx_clean - to manage the transmission completion - * @priv: driver private structure -- * Description: it reclaims resources after transmission completes. -+ * Description: it reclaims the transmit resources after transmission completes. - */ - static void stmmac_tx_clean(struct stmmac_priv *priv) - { - unsigned int txsize = priv->dma_tx_size; -+ unsigned int bytes_compl = 0, pkts_compl = 0; - - spin_lock(&priv->tx_lock); - -@@ -1340,6 +1371,8 @@ static void stmmac_tx_clean(struct stmma - priv->hw->mode->clean_desc3(priv, p); - - if (likely(skb != NULL)) { -+ pkts_compl++; -+ bytes_compl += skb->len; - dev_consume_skb_any(skb); - priv->tx_skbuff[entry] = NULL; - } -@@ -1348,6 +1381,9 @@ static void stmmac_tx_clean(struct stmma - - priv->dirty_tx++; - } -+ -+ netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); -+ - if (unlikely(netif_queue_stopped(priv->dev) && - stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) { - netif_tx_lock(priv->dev); -@@ -1378,10 +1414,10 @@ static inline void stmmac_disable_dma_ir - } - - /** -- * stmmac_tx_err: irq tx error mng function -+ * stmmac_tx_err - to manage the tx error - * @priv: driver private structure - * Description: it cleans the descriptors and restarts the transmission -- * in case of errors. -+ * in case of transmission errors. - */ - static void stmmac_tx_err(struct stmmac_priv *priv) - { -@@ -1402,6 +1438,7 @@ static void stmmac_tx_err(struct stmmac_ - (i == txsize - 1)); - priv->dirty_tx = 0; - priv->cur_tx = 0; -+ netdev_reset_queue(priv->dev); - priv->hw->dma->start_tx(priv->ioaddr); - - priv->dev->stats.tx_errors++; -@@ -1409,16 +1446,16 @@ static void stmmac_tx_err(struct stmmac_ - } - - /** -- * stmmac_dma_interrupt: DMA ISR -+ * stmmac_dma_interrupt - DMA ISR - * @priv: driver private structure - * Description: this is the DMA ISR. It is called by the main ISR. -- * It calls the dwmac dma routine to understand which type of interrupt -- * happened. In case of there is a Normal interrupt and either TX or RX -- * interrupt happened so the NAPI is scheduled. -+ * It calls the dwmac dma routine and schedule poll method in case of some -+ * work can be done. - */ - static void stmmac_dma_interrupt(struct stmmac_priv *priv) - { - int status; -+ int rxfifosz = priv->plat->rx_fifo_size; - - status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); - if (likely((status & handle_rx)) || (status & handle_tx)) { -@@ -1429,9 +1466,15 @@ static void stmmac_dma_interrupt(struct - } - if (unlikely(status & tx_hard_error_bump_tc)) { - /* Try to bump up the dma threshold on this failure */ -- if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) { -+ if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && -+ (tc <= 256)) { - tc += 64; -- priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE); -+ if (priv->plat->force_thresh_dma_mode) -+ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, -+ rxfifosz); -+ else -+ priv->hw->dma->dma_mode(priv->ioaddr, tc, -+ SF_DMA_MODE, rxfifosz); - priv->xstats.threshold = tc; - } - } else if (unlikely(status == tx_hard_error)) -@@ -1457,6 +1500,12 @@ static void stmmac_mmc_setup(struct stmm - pr_info(" No MAC Management Counters available\n"); - } - -+/** -+ * stmmac_get_synopsys_id - return the SYINID. -+ * @priv: driver private structure -+ * Description: this simple function is to decode and return the SYINID -+ * starting from the HW core register. -+ */ - static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv) - { - u32 hwid = priv->hw->synopsys_uid; -@@ -1475,11 +1524,11 @@ static u32 stmmac_get_synopsys_id(struct - } - - /** -- * stmmac_selec_desc_mode: to select among: normal/alternate/extend descriptors -+ * stmmac_selec_desc_mode - to select among: normal/alternate/extend descriptors - * @priv: driver private structure - * Description: select the Enhanced/Alternate or Normal descriptors. -- * In case of Enhanced/Alternate, it looks at the extended descriptors are -- * supported by the HW cap. register. -+ * In case of Enhanced/Alternate, it checks if the extended descriptors are -+ * supported by the HW capability register. - */ - static void stmmac_selec_desc_mode(struct stmmac_priv *priv) - { -@@ -1501,7 +1550,7 @@ static void stmmac_selec_desc_mode(struc - } - - /** -- * stmmac_get_hw_features: get MAC capabilities from the HW cap. register. -+ * stmmac_get_hw_features - get MAC capabilities from the HW cap. register. - * @priv: driver private structure - * Description: - * new GMAC chip generations have a new register to indicate the -@@ -1559,7 +1608,7 @@ static int stmmac_get_hw_features(struct - } - - /** -- * stmmac_check_ether_addr: check if the MAC addr is valid -+ * stmmac_check_ether_addr - check if the MAC addr is valid - * @priv: driver private structure - * Description: - * it is to verify if the MAC address is valid, in case of failures it -@@ -1578,7 +1627,7 @@ static void stmmac_check_ether_addr(stru - } - - /** -- * stmmac_init_dma_engine: DMA init. -+ * stmmac_init_dma_engine - DMA init. - * @priv: driver private structure - * Description: - * It inits the DMA invoking the specific MAC/GMAC callback. -@@ -1607,7 +1656,7 @@ static int stmmac_init_dma_engine(struct - } - - /** -- * stmmac_tx_timer: mitigation sw timer for tx. -+ * stmmac_tx_timer - mitigation sw timer for tx. - * @data: data pointer - * Description: - * This is the timer handler to directly invoke the stmmac_tx_clean. -@@ -1620,7 +1669,7 @@ static void stmmac_tx_timer(unsigned lon - } - - /** -- * stmmac_init_tx_coalesce: init tx mitigation options. -+ * stmmac_init_tx_coalesce - init tx mitigation options. - * @priv: driver private structure - * Description: - * This inits the transmit coalesce parameters: i.e. timer rate, -@@ -1639,15 +1688,18 @@ static void stmmac_init_tx_coalesce(stru - } - - /** -- * stmmac_hw_setup: setup mac in a usable state. -+ * stmmac_hw_setup - setup mac in a usable state. - * @dev : pointer to the device structure. - * Description: -- * This function sets up the ip in a usable state. -+ * this is the main function to setup the HW in a usable state because the -+ * dma engine is reset, the core registers are configured (e.g. AXI, -+ * Checksum features, timers). The DMA is ready to start receiving and -+ * transmitting. - * Return value: - * 0 on success and an appropriate (-)ve integer as defined in errno.h - * file on failure. - */ --static int stmmac_hw_setup(struct net_device *dev) -+static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) - { - struct stmmac_priv *priv = netdev_priv(dev); - int ret; -@@ -1684,11 +1736,13 @@ static int stmmac_hw_setup(struct net_de - - stmmac_mmc_setup(priv); - -- ret = stmmac_init_ptp(priv); -- if (ret && ret != -EOPNOTSUPP) -- pr_warn("%s: failed PTP initialisation\n", __func__); -+ if (init_ptp) { -+ ret = stmmac_init_ptp(priv); -+ if (ret && ret != -EOPNOTSUPP) -+ pr_warn("%s: failed PTP initialisation\n", __func__); -+ } - --#ifdef CONFIG_STMMAC_DEBUG_FS -+#ifdef CONFIG_DEBUG_FS - ret = stmmac_init_fs(dev); - if (ret < 0) - pr_warn("%s: failed debugFS registration\n", __func__); -@@ -1763,7 +1817,7 @@ static int stmmac_open(struct net_device - goto init_error; - } - -- ret = stmmac_hw_setup(dev); -+ ret = stmmac_hw_setup(dev, true); - if (ret < 0) { - pr_err("%s: Hw setup failed\n", __func__); - goto init_error; -@@ -1870,8 +1924,8 @@ static int stmmac_release(struct net_dev - - netif_carrier_off(dev); - --#ifdef CONFIG_STMMAC_DEBUG_FS -- stmmac_exit_fs(); -+#ifdef CONFIG_DEBUG_FS -+ stmmac_exit_fs(dev); - #endif - - stmmac_release_ptp(priv); -@@ -1880,7 +1934,7 @@ static int stmmac_release(struct net_dev - } - - /** -- * stmmac_xmit: Tx entry point of the driver -+ * stmmac_xmit - Tx entry point of the driver - * @skb : the socket buffer - * @dev : device pointer - * Description : this is the tx entry point of the driver. -@@ -2024,6 +2078,7 @@ static netdev_tx_t stmmac_xmit(struct sk - if (!priv->hwts_tx_en) - skb_tx_timestamp(skb); - -+ netdev_sent_queue(dev, skb->len); - priv->hw->dma->enable_dma_transmission(priv->ioaddr); - - spin_unlock(&priv->tx_lock); -@@ -2055,7 +2110,7 @@ static void stmmac_rx_vlan(struct net_de - - - /** -- * stmmac_rx_refill: refill used skb preallocated buffers -+ * stmmac_rx_refill - refill used skb preallocated buffers - * @priv: driver private structure - * Description : this is to reallocate the skb for the reception process - * that is based on zero-copy. -@@ -2106,7 +2161,7 @@ static inline void stmmac_rx_refill(stru - } - - /** -- * stmmac_rx_refill: refill used skb preallocated buffers -+ * stmmac_rx - manage the receive process - * @priv: driver private structure - * @limit: napi bugget. - * Description : this the function called by the napi poll method. -@@ -2375,8 +2430,11 @@ static int stmmac_set_features(struct ne - * @irq: interrupt number. - * @dev_id: to pass the net device pointer. - * Description: this is the main driver interrupt service routine. -- * It calls the DMA ISR and also the core ISR to manage PMT, MMC, LPI -- * interrupts. -+ * It can call: -+ * o DMA service routine (to manage incoming frame reception and transmission -+ * status) -+ * o Core interrupts to manage: remote wake-up, management counter, LPI -+ * interrupts. - */ - static irqreturn_t stmmac_interrupt(int irq, void *dev_id) - { -@@ -2457,10 +2515,8 @@ static int stmmac_ioctl(struct net_devic - return ret; - } - --#ifdef CONFIG_STMMAC_DEBUG_FS -+#ifdef CONFIG_DEBUG_FS - static struct dentry *stmmac_fs_dir; --static struct dentry *stmmac_rings_status; --static struct dentry *stmmac_dma_cap; - - static void sysfs_display_ring(void *head, int size, int extend_desc, - struct seq_file *seq) -@@ -2599,36 +2655,39 @@ static const struct file_operations stmm - - static int stmmac_init_fs(struct net_device *dev) - { -- /* Create debugfs entries */ -- stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL); -+ struct stmmac_priv *priv = netdev_priv(dev); - -- if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) { -- pr_err("ERROR %s, debugfs create directory failed\n", -- STMMAC_RESOURCE_NAME); -+ /* Create per netdev entries */ -+ priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir); -+ -+ if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) { -+ pr_err("ERROR %s/%s, debugfs create directory failed\n", -+ STMMAC_RESOURCE_NAME, dev->name); - - return -ENOMEM; - } - - /* Entry to report DMA RX/TX rings */ -- stmmac_rings_status = debugfs_create_file("descriptors_status", -- S_IRUGO, stmmac_fs_dir, dev, -- &stmmac_rings_status_fops); -+ priv->dbgfs_rings_status = -+ debugfs_create_file("descriptors_status", S_IRUGO, -+ priv->dbgfs_dir, dev, -+ &stmmac_rings_status_fops); - -- if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) { -+ if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) { - pr_info("ERROR creating stmmac ring debugfs file\n"); -- debugfs_remove(stmmac_fs_dir); -+ debugfs_remove_recursive(priv->dbgfs_dir); - - return -ENOMEM; - } - - /* Entry to report the DMA HW features */ -- stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir, -- dev, &stmmac_dma_cap_fops); -+ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, -+ priv->dbgfs_dir, -+ dev, &stmmac_dma_cap_fops); - -- if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) { -+ if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) { - pr_info("ERROR creating stmmac MMC debugfs file\n"); -- debugfs_remove(stmmac_rings_status); -- debugfs_remove(stmmac_fs_dir); -+ debugfs_remove_recursive(priv->dbgfs_dir); - - return -ENOMEM; - } -@@ -2636,13 +2695,13 @@ static int stmmac_init_fs(struct net_dev - return 0; - } - --static void stmmac_exit_fs(void) -+static void stmmac_exit_fs(struct net_device *dev) - { -- debugfs_remove(stmmac_rings_status); -- debugfs_remove(stmmac_dma_cap); -- debugfs_remove(stmmac_fs_dir); -+ struct stmmac_priv *priv = netdev_priv(dev); -+ -+ debugfs_remove_recursive(priv->dbgfs_dir); - } --#endif /* CONFIG_STMMAC_DEBUG_FS */ -+#endif /* CONFIG_DEBUG_FS */ - - static const struct net_device_ops stmmac_netdev_ops = { - .ndo_open = stmmac_open, -@@ -2663,11 +2722,10 @@ static const struct net_device_ops stmma - /** - * stmmac_hw_init - Init the MAC device - * @priv: driver private structure -- * Description: this function detects which MAC device -- * (GMAC/MAC10-100) has to attached, checks the HW capability -- * (if supported) and sets the driver's features (for example -- * to use the ring or chaine mode or support the normal/enh -- * descriptor structure). -+ * Description: this function is to configure the MAC device according to -+ * some platform parameters or the HW capability register. It prepares the -+ * driver to use either ring or chain modes and to setup either enhanced or -+ * normal descriptors. - */ - static int stmmac_hw_init(struct stmmac_priv *priv) - { -@@ -2714,7 +2772,11 @@ static int stmmac_hw_init(struct stmmac_ - priv->plat->enh_desc = priv->dma_cap.enh_desc; - priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up; - -- priv->plat->tx_coe = priv->dma_cap.tx_coe; -+ /* TXCOE doesn't work in thresh DMA mode */ -+ if (priv->plat->force_thresh_dma_mode) -+ priv->plat->tx_coe = 0; -+ else -+ priv->plat->tx_coe = priv->dma_cap.tx_coe; - - if (priv->dma_cap.rx_coe_type2) - priv->plat->rx_coe = STMMAC_RX_COE_TYPE2; -@@ -2747,13 +2809,15 @@ static int stmmac_hw_init(struct stmmac_ - * stmmac_dvr_probe - * @device: device pointer - * @plat_dat: platform data pointer -- * @addr: iobase memory address -+ * @res: stmmac resource pointer - * Description: this is the main probe function used to - * call the alloc_etherdev, allocate the priv structure. -+ * Return: -+ * returns 0 on success, otherwise errno. - */ --struct stmmac_priv *stmmac_dvr_probe(struct device *device, -- struct plat_stmmacenet_data *plat_dat, -- void __iomem *addr) -+int stmmac_dvr_probe(struct device *device, -+ struct plat_stmmacenet_data *plat_dat, -+ struct stmmac_resources *res) - { - int ret = 0; - struct net_device *ndev = NULL; -@@ -2761,7 +2825,7 @@ struct stmmac_priv *stmmac_dvr_probe(str - - ndev = alloc_etherdev(sizeof(struct stmmac_priv)); - if (!ndev) -- return NULL; -+ return -ENOMEM; - - SET_NETDEV_DEV(ndev, device); - -@@ -2772,8 +2836,17 @@ struct stmmac_priv *stmmac_dvr_probe(str - stmmac_set_ethtool_ops(ndev); - priv->pause = pause; - priv->plat = plat_dat; -- priv->ioaddr = addr; -- priv->dev->base_addr = (unsigned long)addr; -+ priv->ioaddr = res->addr; -+ priv->dev->base_addr = (unsigned long)res->addr; -+ -+ priv->dev->irq = res->irq; -+ priv->wol_irq = res->wol_irq; -+ priv->lpi_irq = res->lpi_irq; -+ -+ if (res->mac) -+ memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN); -+ -+ dev_set_drvdata(device, priv->dev); - - /* Verify driver arguments */ - stmmac_verify_args(); -@@ -2800,6 +2873,16 @@ struct stmmac_priv *stmmac_dvr_probe(str - } - clk_prepare_enable(priv->stmmac_clk); - -+ priv->pclk = devm_clk_get(priv->device, "pclk"); -+ if (IS_ERR(priv->pclk)) { -+ if (PTR_ERR(priv->pclk) == -EPROBE_DEFER) { -+ ret = -EPROBE_DEFER; -+ goto error_pclk_get; -+ } -+ priv->pclk = NULL; -+ } -+ clk_prepare_enable(priv->pclk); -+ - priv->stmmac_rst = devm_reset_control_get(priv->device, - STMMAC_RESOURCE_NAME); - if (IS_ERR(priv->stmmac_rst)) { -@@ -2878,19 +2961,22 @@ struct stmmac_priv *stmmac_dvr_probe(str - } - } - -- return priv; -+ return 0; - - error_mdio_register: - unregister_netdev(ndev); - error_netdev_register: - netif_napi_del(&priv->napi); - error_hw_init: -+ clk_disable_unprepare(priv->pclk); -+error_pclk_get: - clk_disable_unprepare(priv->stmmac_clk); - error_clk_get: - free_netdev(ndev); - -- return ERR_PTR(ret); -+ return ret; - } -+EXPORT_SYMBOL_GPL(stmmac_dvr_probe); - - /** - * stmmac_dvr_remove -@@ -2908,20 +2994,28 @@ int stmmac_dvr_remove(struct net_device - priv->hw->dma->stop_tx(priv->ioaddr); - - stmmac_set_mac(priv->ioaddr, false); -- if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && -- priv->pcs != STMMAC_PCS_RTBI) -- stmmac_mdio_unregister(ndev); - netif_carrier_off(ndev); - unregister_netdev(ndev); - if (priv->stmmac_rst) - reset_control_assert(priv->stmmac_rst); -+ clk_disable_unprepare(priv->pclk); - clk_disable_unprepare(priv->stmmac_clk); -+ if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && -+ priv->pcs != STMMAC_PCS_RTBI) -+ stmmac_mdio_unregister(ndev); - free_netdev(ndev); - - return 0; - } -+EXPORT_SYMBOL_GPL(stmmac_dvr_remove); - --#ifdef CONFIG_PM -+/** -+ * stmmac_suspend - suspend callback -+ * @ndev: net device pointer -+ * Description: this is the function to suspend the device and it is called -+ * by the platform driver to stop the network queue, release the resources, -+ * program the PMT register (for WoL), clean and release driver resources. -+ */ - int stmmac_suspend(struct net_device *ndev) - { - struct stmmac_priv *priv = netdev_priv(ndev); -@@ -2954,6 +3048,7 @@ int stmmac_suspend(struct net_device *nd - stmmac_set_mac(priv->ioaddr, false); - pinctrl_pm_select_sleep_state(priv->device); - /* Disable clock in case of PWM is off */ -+ clk_disable(priv->pclk); - clk_disable(priv->stmmac_clk); - } - spin_unlock_irqrestore(&priv->lock, flags); -@@ -2963,7 +3058,14 @@ int stmmac_suspend(struct net_device *nd - priv->oldduplex = -1; - return 0; - } -+EXPORT_SYMBOL_GPL(stmmac_suspend); - -+/** -+ * stmmac_resume - resume callback -+ * @ndev: net device pointer -+ * Description: when resume this function is invoked to setup the DMA and CORE -+ * in a usable state. -+ */ - int stmmac_resume(struct net_device *ndev) - { - struct stmmac_priv *priv = netdev_priv(ndev); -@@ -2987,6 +3089,7 @@ int stmmac_resume(struct net_device *nde - pinctrl_pm_select_default_state(priv->device); - /* enable the clk prevously disabled */ - clk_enable(priv->stmmac_clk); -+ clk_enable(priv->pclk); - /* reset the phy so that it's ready */ - if (priv->mii) - stmmac_mdio_reset(priv->mii); -@@ -2995,7 +3098,7 @@ int stmmac_resume(struct net_device *nde - netif_device_attach(ndev); - - init_dma_desc_rings(ndev, GFP_ATOMIC); -- stmmac_hw_setup(ndev); -+ stmmac_hw_setup(ndev, false); - stmmac_init_tx_coalesce(priv); - - napi_enable(&priv->napi); -@@ -3009,37 +3112,7 @@ int stmmac_resume(struct net_device *nde - - return 0; - } --#endif /* CONFIG_PM */ -- --/* Driver can be configured w/ and w/ both PCI and Platf drivers -- * depending on the configuration selected. -- */ --static int __init stmmac_init(void) --{ -- int ret; -- -- ret = stmmac_register_platform(); -- if (ret) -- goto err; -- ret = stmmac_register_pci(); -- if (ret) -- goto err_pci; -- return 0; --err_pci: -- stmmac_unregister_platform(); --err: -- pr_err("stmmac: driver registration failed\n"); -- return ret; --} -- --static void __exit stmmac_exit(void) --{ -- stmmac_unregister_platform(); -- stmmac_unregister_pci(); --} -- --module_init(stmmac_init); --module_exit(stmmac_exit); -+EXPORT_SYMBOL_GPL(stmmac_resume); - - #ifndef MODULE - static int __init stmmac_cmdline_opt(char *str) -@@ -3094,6 +3167,35 @@ err: - __setup("stmmaceth=", stmmac_cmdline_opt); - #endif /* MODULE */ - -+static int __init stmmac_init(void) -+{ -+#ifdef CONFIG_DEBUG_FS -+ /* Create debugfs main directory if it doesn't exist yet */ -+ if (!stmmac_fs_dir) { -+ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL); -+ -+ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) { -+ pr_err("ERROR %s, debugfs create directory failed\n", -+ STMMAC_RESOURCE_NAME); -+ -+ return -ENOMEM; -+ } -+ } -+#endif -+ -+ return 0; -+} -+ -+static void __exit stmmac_exit(void) -+{ -+#ifdef CONFIG_DEBUG_FS -+ debugfs_remove_recursive(stmmac_fs_dir); -+#endif -+} -+ -+module_init(stmmac_init) -+module_exit(stmmac_exit) -+ - MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver"); - MODULE_AUTHOR("Giuseppe Cavallaro "); - MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c -@@ -161,11 +161,16 @@ int stmmac_mdio_reset(struct mii_bus *bu - - if (!gpio_request(reset_gpio, "mdio-reset")) { - gpio_direction_output(reset_gpio, active_low ? 1 : 0); -- udelay(data->delays[0]); -+ if (data->delays[0]) -+ msleep(DIV_ROUND_UP(data->delays[0], 1000)); -+ - gpio_set_value(reset_gpio, active_low ? 0 : 1); -- udelay(data->delays[1]); -+ if (data->delays[1]) -+ msleep(DIV_ROUND_UP(data->delays[1], 1000)); -+ - gpio_set_value(reset_gpio, active_low ? 1 : 0); -- udelay(data->delays[2]); -+ if (data->delays[2]) -+ msleep(DIV_ROUND_UP(data->delays[2], 1000)); - } - } - #endif ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c -@@ -24,38 +24,128 @@ - *******************************************************************************/ - - #include -+#include -+ - #include "stmmac.h" - --static struct plat_stmmacenet_data plat_dat; --static struct stmmac_mdio_bus_data mdio_data; --static struct stmmac_dma_cfg dma_cfg; -- --static void stmmac_default_data(void) --{ -- memset(&plat_dat, 0, sizeof(struct plat_stmmacenet_data)); -- -- plat_dat.bus_id = 1; -- plat_dat.phy_addr = 0; -- plat_dat.interface = PHY_INTERFACE_MODE_GMII; -- plat_dat.clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ -- plat_dat.has_gmac = 1; -- plat_dat.force_sf_dma_mode = 1; -- -- mdio_data.phy_reset = NULL; -- mdio_data.phy_mask = 0; -- plat_dat.mdio_bus_data = &mdio_data; -- -- dma_cfg.pbl = 32; -- dma_cfg.burst_len = DMA_AXI_BLEN_256; -- plat_dat.dma_cfg = &dma_cfg; -+/* -+ * This struct is used to associate PCI Function of MAC controller on a board, -+ * discovered via DMI, with the address of PHY connected to the MAC. The -+ * negative value of the address means that MAC controller is not connected -+ * with PHY. -+ */ -+struct stmmac_pci_dmi_data { -+ const char *name; -+ unsigned int func; -+ int phy_addr; -+}; -+ -+struct stmmac_pci_info { -+ struct pci_dev *pdev; -+ int (*setup)(struct plat_stmmacenet_data *plat, -+ struct stmmac_pci_info *info); -+ struct stmmac_pci_dmi_data *dmi; -+}; -+ -+static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info) -+{ -+ const char *name = dmi_get_system_info(DMI_BOARD_NAME); -+ unsigned int func = PCI_FUNC(info->pdev->devfn); -+ struct stmmac_pci_dmi_data *dmi; -+ -+ /* -+ * Galileo boards with old firmware don't support DMI. We always return -+ * 1 here, so at least first found MAC controller would be probed. -+ */ -+ if (!name) -+ return 1; -+ -+ for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) { -+ if (!strcmp(dmi->name, name) && dmi->func == func) -+ return dmi->phy_addr; -+ } -+ -+ return -ENODEV; -+} -+ -+static void stmmac_default_data(struct plat_stmmacenet_data *plat) -+{ -+ plat->bus_id = 1; -+ plat->phy_addr = 0; -+ plat->interface = PHY_INTERFACE_MODE_GMII; -+ plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ -+ plat->has_gmac = 1; -+ plat->force_sf_dma_mode = 1; -+ -+ plat->mdio_bus_data->phy_reset = NULL; -+ plat->mdio_bus_data->phy_mask = 0; -+ -+ plat->dma_cfg->pbl = 32; -+ plat->dma_cfg->burst_len = DMA_AXI_BLEN_256; -+ -+ /* Set default value for multicast hash bins */ -+ plat->multicast_filter_bins = HASH_TABLE_SIZE; -+ -+ /* Set default value for unicast filter entries */ -+ plat->unicast_filter_entries = 1; -+} -+ -+static int quark_default_data(struct plat_stmmacenet_data *plat, -+ struct stmmac_pci_info *info) -+{ -+ struct pci_dev *pdev = info->pdev; -+ int ret; -+ -+ /* -+ * Refuse to load the driver and register net device if MAC controller -+ * does not connect to any PHY interface. -+ */ -+ ret = stmmac_pci_find_phy_addr(info); -+ if (ret < 0) -+ return ret; -+ -+ plat->bus_id = PCI_DEVID(pdev->bus->number, pdev->devfn); -+ plat->phy_addr = ret; -+ plat->interface = PHY_INTERFACE_MODE_RMII; -+ plat->clk_csr = 2; -+ plat->has_gmac = 1; -+ plat->force_sf_dma_mode = 1; -+ -+ plat->mdio_bus_data->phy_reset = NULL; -+ plat->mdio_bus_data->phy_mask = 0; -+ -+ plat->dma_cfg->pbl = 16; -+ plat->dma_cfg->burst_len = DMA_AXI_BLEN_256; -+ plat->dma_cfg->fixed_burst = 1; - - /* Set default value for multicast hash bins */ -- plat_dat.multicast_filter_bins = HASH_TABLE_SIZE; -+ plat->multicast_filter_bins = HASH_TABLE_SIZE; - - /* Set default value for unicast filter entries */ -- plat_dat.unicast_filter_entries = 1; -+ plat->unicast_filter_entries = 1; -+ -+ return 0; - } - -+static struct stmmac_pci_dmi_data quark_pci_dmi_data[] = { -+ { -+ .name = "Galileo", -+ .func = 6, -+ .phy_addr = 1, -+ }, -+ { -+ .name = "GalileoGen2", -+ .func = 6, -+ .phy_addr = 1, -+ }, -+ {} -+}; -+ -+static struct stmmac_pci_info quark_pci_info = { -+ .setup = quark_default_data, -+ .dmi = quark_pci_dmi_data, -+}; -+ - /** - * stmmac_pci_probe - * -@@ -71,64 +161,65 @@ static void stmmac_default_data(void) - static int stmmac_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) - { -- int ret = 0; -- void __iomem *addr = NULL; -- struct stmmac_priv *priv = NULL; -+ struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data; -+ struct plat_stmmacenet_data *plat; -+ struct stmmac_resources res; - int i; -+ int ret; -+ -+ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); -+ if (!plat) -+ return -ENOMEM; -+ -+ plat->mdio_bus_data = devm_kzalloc(&pdev->dev, -+ sizeof(*plat->mdio_bus_data), -+ GFP_KERNEL); -+ if (!plat->mdio_bus_data) -+ return -ENOMEM; -+ -+ plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), -+ GFP_KERNEL); -+ if (!plat->dma_cfg) -+ return -ENOMEM; - - /* Enable pci device */ -- ret = pci_enable_device(pdev); -+ ret = pcim_enable_device(pdev); - if (ret) { -- pr_err("%s : ERROR: failed to enable %s device\n", __func__, -- pci_name(pdev)); -+ dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", -+ __func__); - return ret; - } -- if (pci_request_regions(pdev, STMMAC_RESOURCE_NAME)) { -- pr_err("%s: ERROR: failed to get PCI region\n", __func__); -- ret = -ENODEV; -- goto err_out_req_reg_failed; -- } - - /* Get the base address of device */ -- for (i = 0; i <= 5; i++) { -+ for (i = 0; i <= PCI_STD_RESOURCE_END; i++) { - if (pci_resource_len(pdev, i) == 0) - continue; -- addr = pci_iomap(pdev, i, 0); -- if (addr == NULL) { -- pr_err("%s: ERROR: cannot map register memory aborting", -- __func__); -- ret = -EIO; -- goto err_out_map_failed; -- } -+ ret = pcim_iomap_regions(pdev, BIT(i), pci_name(pdev)); -+ if (ret) -+ return ret; - break; - } -- pci_set_master(pdev); -- -- stmmac_default_data(); - -- priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat, addr); -- if (IS_ERR(priv)) { -- pr_err("%s: main driver probe failed", __func__); -- ret = PTR_ERR(priv); -- goto err_out; -- } -- priv->dev->irq = pdev->irq; -- priv->wol_irq = pdev->irq; -- -- pci_set_drvdata(pdev, priv->dev); -+ pci_set_master(pdev); - -- pr_debug("STMMAC platform driver registration completed"); -+ if (info) { -+ info->pdev = pdev; -+ if (info->setup) { -+ ret = info->setup(plat, info); -+ if (ret) -+ return ret; -+ } -+ } else -+ stmmac_default_data(plat); - -- return 0; -+ pci_enable_msi(pdev); - --err_out: -- pci_clear_master(pdev); --err_out_map_failed: -- pci_release_regions(pdev); --err_out_req_reg_failed: -- pci_disable_device(pdev); -+ memset(&res, 0, sizeof(res)); -+ res.addr = pcim_iomap_table(pdev)[i]; -+ res.wol_irq = pdev->irq; -+ res.irq = pdev->irq; - -- return ret; -+ return stmmac_dvr_probe(&pdev->dev, plat, &res); - } - - /** -@@ -141,61 +232,55 @@ err_out_req_reg_failed: - static void stmmac_pci_remove(struct pci_dev *pdev) - { - struct net_device *ndev = pci_get_drvdata(pdev); -- struct stmmac_priv *priv = netdev_priv(ndev); - - stmmac_dvr_remove(ndev); -- -- pci_iounmap(pdev, priv->ioaddr); -- pci_release_regions(pdev); -- pci_disable_device(pdev); - } - --#ifdef CONFIG_PM --static int stmmac_pci_suspend(struct pci_dev *pdev, pm_message_t state) -+#ifdef CONFIG_PM_SLEEP -+static int stmmac_pci_suspend(struct device *dev) - { -+ struct pci_dev *pdev = to_pci_dev(dev); - struct net_device *ndev = pci_get_drvdata(pdev); -- int ret; -- -- ret = stmmac_suspend(ndev); -- pci_save_state(pdev); -- pci_set_power_state(pdev, pci_choose_state(pdev, state)); - -- return ret; -+ return stmmac_suspend(ndev); - } - --static int stmmac_pci_resume(struct pci_dev *pdev) -+static int stmmac_pci_resume(struct device *dev) - { -+ struct pci_dev *pdev = to_pci_dev(dev); - struct net_device *ndev = pci_get_drvdata(pdev); - -- pci_set_power_state(pdev, PCI_D0); -- pci_restore_state(pdev); -- - return stmmac_resume(ndev); - } - #endif - -+static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_pci_suspend, stmmac_pci_resume); -+ - #define STMMAC_VENDOR_ID 0x700 -+#define STMMAC_QUARK_ID 0x0937 - #define STMMAC_DEVICE_ID 0x1108 - - static const struct pci_device_id stmmac_id_table[] = { - {PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)}, -+ {PCI_VDEVICE(INTEL, STMMAC_QUARK_ID), (kernel_ulong_t)&quark_pci_info}, - {} - }; - - MODULE_DEVICE_TABLE(pci, stmmac_id_table); - --struct pci_driver stmmac_pci_driver = { -+static struct pci_driver stmmac_pci_driver = { - .name = STMMAC_RESOURCE_NAME, - .id_table = stmmac_id_table, - .probe = stmmac_pci_probe, - .remove = stmmac_pci_remove, --#ifdef CONFIG_PM -- .suspend = stmmac_pci_suspend, -- .resume = stmmac_pci_resume, --#endif -+ .driver = { -+ .pm = &stmmac_pm_ops, -+ }, - }; - -+module_pci_driver(stmmac_pci_driver); -+ - MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PCI driver"); - MODULE_AUTHOR("Rayagond Kokatanur "); - MODULE_AUTHOR("Giuseppe Cavallaro "); ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -@@ -23,41 +23,23 @@ - *******************************************************************************/ - - #include -+#include - #include - #include - #include - #include --#include "stmmac.h" -+#include - --static const struct of_device_id stmmac_dt_ids[] = { --#ifdef CONFIG_DWMAC_MESON -- { .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data}, --#endif --#ifdef CONFIG_DWMAC_SUNXI -- { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data}, --#endif --#ifdef CONFIG_DWMAC_STI -- { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, -- { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data}, -- { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, -- { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, --#endif --#ifdef CONFIG_DWMAC_SOCFPGA -- { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data }, --#endif -- /* SoC specific glue layers should come before generic bindings */ -- { .compatible = "st,spear600-gmac"}, -- { .compatible = "snps,dwmac-3.610"}, -- { .compatible = "snps,dwmac-3.70a"}, -- { .compatible = "snps,dwmac-3.710"}, -- { .compatible = "snps,dwmac"}, -- { /* sentinel */ } --}; --MODULE_DEVICE_TABLE(of, stmmac_dt_ids); -+#include "stmmac.h" -+#include "stmmac_platform.h" - - #ifdef CONFIG_OF - --/* This function validates the number of Multicast filtering bins specified -+/** -+ * dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins -+ * @mcast_bins: Multicast filtering bins -+ * Description: -+ * this function validates the number of Multicast filtering bins specified - * by the configuration through the device tree. The Synopsys GMAC supports - * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC - * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds -@@ -83,7 +65,11 @@ static int dwmac1000_validate_mcast_bins - return x; - } - --/* This function validates the number of Unicast address entries supported -+/** -+ * dwmac1000_validate_ucast_entries - validate the Unicast address entries -+ * @ucast_entries: number of Unicast address entries -+ * Description: -+ * This function validates the number of Unicast address entries supported - * by a particular Synopsys 10/100/1000 controller. The Synopsys controller - * supports 1, 32, 64, or 128 Unicast filter entries for it's Unicast filter - * logic. This function validates a valid, supported configuration is -@@ -109,37 +95,25 @@ static int dwmac1000_validate_ucast_entr - return x; - } - --static int stmmac_probe_config_dt(struct platform_device *pdev, -- struct plat_stmmacenet_data *plat, -- const char **mac) -+/** -+ * stmmac_probe_config_dt - parse device-tree driver parameters -+ * @pdev: platform_device structure -+ * @plat: driver data platform structure -+ * @mac: MAC address to use -+ * Description: -+ * this function is to read the driver parameters from device-tree and -+ * set some private fields that will be used by the main at runtime. -+ */ -+struct plat_stmmacenet_data * -+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) - { - struct device_node *np = pdev->dev.of_node; -+ struct plat_stmmacenet_data *plat; - struct stmmac_dma_cfg *dma_cfg; -- const struct of_device_id *device; - -- if (!np) -- return -ENODEV; -- -- device = of_match_device(stmmac_dt_ids, &pdev->dev); -- if (!device) -- return -ENODEV; -- -- if (device->data) { -- const struct stmmac_of_data *data = device->data; -- plat->has_gmac = data->has_gmac; -- plat->enh_desc = data->enh_desc; -- plat->tx_coe = data->tx_coe; -- plat->rx_coe = data->rx_coe; -- plat->bugged_jumbo = data->bugged_jumbo; -- plat->pmt = data->pmt; -- plat->riwt_off = data->riwt_off; -- plat->fix_mac_speed = data->fix_mac_speed; -- plat->bus_setup = data->bus_setup; -- plat->setup = data->setup; -- plat->free = data->free; -- plat->init = data->init; -- plat->exit = data->exit; -- } -+ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); -+ if (!plat) -+ return ERR_PTR(-ENOMEM); - - *mac = of_get_mac_address(np); - plat->interface = of_get_phy_mode(np); -@@ -155,13 +129,24 @@ static int stmmac_probe_config_dt(struct - /* Default to phy auto-detection */ - plat->phy_addr = -1; - -+ /* If we find a phy-handle property, use it as the PHY */ -+ plat->phy_node = of_parse_phandle(np, "phy-handle", 0); -+ -+ /* If phy-handle is not specified, check if we have a fixed-phy */ -+ if (!plat->phy_node && of_phy_is_fixed_link(np)) { -+ if ((of_phy_register_fixed_link(np) < 0)) -+ return ERR_PTR(-ENODEV); -+ -+ plat->phy_node = of_node_get(np); -+ } -+ - /* "snps,phy-addr" is not a standard property. Mark it as deprecated - * and warn of its use. Remove this when phy node support is added. - */ - if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0) - dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n"); - -- if (plat->phy_bus_name) -+ if (plat->phy_node || plat->phy_bus_name) - plat->mdio_bus_data = NULL; - else - plat->mdio_bus_data = -@@ -169,6 +154,10 @@ static int stmmac_probe_config_dt(struct - sizeof(struct stmmac_mdio_bus_data), - GFP_KERNEL); - -+ of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size); -+ -+ of_property_read_u32(np, "rx-fifo-depth", &plat->rx_fifo_size); -+ - plat->force_sf_dma_mode = - of_property_read_bool(np, "snps,force_sf_dma_mode"); - -@@ -177,6 +166,12 @@ static int stmmac_probe_config_dt(struct - */ - plat->maxmtu = JUMBO_LEN; - -+ /* Set default value for multicast hash bins */ -+ plat->multicast_filter_bins = HASH_TABLE_SIZE; -+ -+ /* Set default value for unicast filter entries */ -+ plat->unicast_filter_entries = 1; -+ - /* - * Currently only the properties needed on SPEAr600 - * are provided. All other properties should be added -@@ -215,14 +210,19 @@ static int stmmac_probe_config_dt(struct - if (of_find_property(np, "snps,pbl", NULL)) { - dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), - GFP_KERNEL); -- if (!dma_cfg) -- return -ENOMEM; -+ if (!dma_cfg) { -+ of_node_put(np); -+ return ERR_PTR(-ENOMEM); -+ } - plat->dma_cfg = dma_cfg; - of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); - dma_cfg->fixed_burst = - of_property_read_bool(np, "snps,fixed-burst"); - dma_cfg->mixed_burst = - of_property_read_bool(np, "snps,mixed-burst"); -+ of_property_read_u32(np, "snps,burst_len", &dma_cfg->burst_len); -+ if (dma_cfg->burst_len < 0 || dma_cfg->burst_len > 256) -+ dma_cfg->burst_len = 0; - } - plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode"); - if (plat->force_thresh_dma_mode) { -@@ -230,123 +230,60 @@ static int stmmac_probe_config_dt(struct - pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set."); - } - -- return 0; -+ return plat; - } - #else --static int stmmac_probe_config_dt(struct platform_device *pdev, -- struct plat_stmmacenet_data *plat, -- const char **mac) -+struct plat_stmmacenet_data * -+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) - { -- return -ENOSYS; -+ return ERR_PTR(-ENOSYS); - } - #endif /* CONFIG_OF */ -+EXPORT_SYMBOL_GPL(stmmac_probe_config_dt); - --/** -- * stmmac_pltfr_probe -- * @pdev: platform device pointer -- * Description: platform_device probe function. It allocates -- * the necessary resources and invokes the main to init -- * the net device, register the mdio bus etc. -- */ --static int stmmac_pltfr_probe(struct platform_device *pdev) -+int stmmac_get_platform_resources(struct platform_device *pdev, -+ struct stmmac_resources *stmmac_res) - { -- int ret = 0; - struct resource *res; -- struct device *dev = &pdev->dev; -- void __iomem *addr = NULL; -- struct stmmac_priv *priv = NULL; -- struct plat_stmmacenet_data *plat_dat = NULL; -- const char *mac = NULL; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- addr = devm_ioremap_resource(dev, res); -- if (IS_ERR(addr)) -- return PTR_ERR(addr); -- -- plat_dat = dev_get_platdata(&pdev->dev); -- -- if (!plat_dat) -- plat_dat = devm_kzalloc(&pdev->dev, -- sizeof(struct plat_stmmacenet_data), -- GFP_KERNEL); -- if (!plat_dat) { -- pr_err("%s: ERROR: no memory", __func__); -- return -ENOMEM; -- } -- -- /* Set default value for multicast hash bins */ -- plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; -- -- /* Set default value for unicast filter entries */ -- plat_dat->unicast_filter_entries = 1; -- -- if (pdev->dev.of_node) { -- ret = stmmac_probe_config_dt(pdev, plat_dat, &mac); -- if (ret) { -- pr_err("%s: main dt probe failed", __func__); -- return ret; -- } -- } -- -- /* Custom setup (if needed) */ -- if (plat_dat->setup) { -- plat_dat->bsp_priv = plat_dat->setup(pdev); -- if (IS_ERR(plat_dat->bsp_priv)) -- return PTR_ERR(plat_dat->bsp_priv); -- } -- -- /* Custom initialisation (if needed)*/ -- if (plat_dat->init) { -- ret = plat_dat->init(pdev, plat_dat->bsp_priv); -- if (unlikely(ret)) -- return ret; -- } - -- priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr); -- if (IS_ERR(priv)) { -- pr_err("%s: main driver probe failed", __func__); -- return PTR_ERR(priv); -- } -+ memset(stmmac_res, 0, sizeof(*stmmac_res)); - -- /* Get MAC address if available (DT) */ -- if (mac) -- memcpy(priv->dev->dev_addr, mac, ETH_ALEN); -- -- /* Get the MAC information */ -- priv->dev->irq = platform_get_irq_byname(pdev, "macirq"); -- if (priv->dev->irq < 0) { -- if (priv->dev->irq != -EPROBE_DEFER) { -- netdev_err(priv->dev, -- "MAC IRQ configuration information not found\n"); -+ /* Get IRQ information early to have an ability to ask for deferred -+ * probe if needed before we went too far with resource allocation. -+ */ -+ stmmac_res->irq = platform_get_irq_byname(pdev, "macirq"); -+ if (stmmac_res->irq < 0) { -+ if (stmmac_res->irq != -EPROBE_DEFER) { -+ dev_err(&pdev->dev, -+ "MAC IRQ configuration information not found\n"); - } -- return priv->dev->irq; -+ return stmmac_res->irq; - } - -- /* -- * On some platforms e.g. SPEAr the wake up irq differs from the mac irq -+ /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq - * The external wake up irq can be passed through the platform code - * named as "eth_wake_irq" - * - * In case the wake up interrupt is not passed from the platform - * so the driver will continue to use the mac irq (ndev->irq) - */ -- priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); -- if (priv->wol_irq < 0) { -- if (priv->wol_irq == -EPROBE_DEFER) -+ stmmac_res->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); -+ if (stmmac_res->wol_irq < 0) { -+ if (stmmac_res->wol_irq == -EPROBE_DEFER) - return -EPROBE_DEFER; -- priv->wol_irq = priv->dev->irq; -+ stmmac_res->wol_irq = stmmac_res->irq; - } - -- priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); -- if (priv->lpi_irq == -EPROBE_DEFER) -+ stmmac_res->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); -+ if (stmmac_res->lpi_irq == -EPROBE_DEFER) - return -EPROBE_DEFER; - -- platform_set_drvdata(pdev, priv->dev); -- -- pr_debug("STMMAC platform driver registration completed"); -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ stmmac_res->addr = devm_ioremap_resource(&pdev->dev, res); - -- return 0; -+ return PTR_ERR_OR_ZERO(stmmac_res->addr); - } -+EXPORT_SYMBOL_GPL(stmmac_get_platform_resources); - - /** - * stmmac_pltfr_remove -@@ -354,7 +291,7 @@ static int stmmac_pltfr_probe(struct pla - * Description: this function calls the main to free the net resources - * and calls the platforms hook and release the resources (e.g. mem). - */ --static int stmmac_pltfr_remove(struct platform_device *pdev) -+int stmmac_pltfr_remove(struct platform_device *pdev) - { - struct net_device *ndev = platform_get_drvdata(pdev); - struct stmmac_priv *priv = netdev_priv(ndev); -@@ -363,13 +300,18 @@ static int stmmac_pltfr_remove(struct pl - if (priv->plat->exit) - priv->plat->exit(pdev, priv->plat->bsp_priv); - -- if (priv->plat->free) -- priv->plat->free(pdev, priv->plat->bsp_priv); -- - return ret; - } -+EXPORT_SYMBOL_GPL(stmmac_pltfr_remove); - --#ifdef CONFIG_PM -+#ifdef CONFIG_PM_SLEEP -+/** -+ * stmmac_pltfr_suspend -+ * @dev: device pointer -+ * Description: this function is invoked when suspend the driver and it direcly -+ * call the main suspend function and then, if required, on some platform, it -+ * can call an exit helper. -+ */ - static int stmmac_pltfr_suspend(struct device *dev) - { - int ret; -@@ -384,6 +326,13 @@ static int stmmac_pltfr_suspend(struct d - return ret; - } - -+/** -+ * stmmac_pltfr_resume -+ * @dev: device pointer -+ * Description: this function is invoked when resume the driver before calling -+ * the main resume function, on some platforms, it can call own init helper -+ * if required. -+ */ - static int stmmac_pltfr_resume(struct device *dev) - { - struct net_device *ndev = dev_get_drvdata(dev); -@@ -395,23 +344,12 @@ static int stmmac_pltfr_resume(struct de - - return stmmac_resume(ndev); - } -+#endif /* CONFIG_PM_SLEEP */ - --#endif /* CONFIG_PM */ -- --static SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, -- stmmac_pltfr_suspend, stmmac_pltfr_resume); -- --struct platform_driver stmmac_pltfr_driver = { -- .probe = stmmac_pltfr_probe, -- .remove = stmmac_pltfr_remove, -- .driver = { -- .name = STMMAC_RESOURCE_NAME, -- .owner = THIS_MODULE, -- .pm = &stmmac_pltfr_pm_ops, -- .of_match_table = of_match_ptr(stmmac_dt_ids), -- }, --}; -+SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, stmmac_pltfr_suspend, -+ stmmac_pltfr_resume); -+EXPORT_SYMBOL_GPL(stmmac_pltfr_pm_ops); - --MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver"); -+MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet platform support"); - MODULE_AUTHOR("Giuseppe Cavallaro "); - MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h -@@ -0,0 +1,33 @@ -+/******************************************************************************* -+ Copyright (C) 2007-2009 STMicroelectronics Ltd -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms and conditions of the GNU General Public License, -+ version 2, as published by the Free Software Foundation. -+ -+ This program is distributed in the hope it will be useful, but WITHOUT -+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ more details. -+ -+ The full GNU General Public License is included in this distribution in -+ the file called "COPYING". -+ -+ Author: Giuseppe Cavallaro -+*******************************************************************************/ -+ -+#ifndef __STMMAC_PLATFORM_H__ -+#define __STMMAC_PLATFORM_H__ -+ -+#include "stmmac.h" -+ -+struct plat_stmmacenet_data * -+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac); -+ -+int stmmac_get_platform_resources(struct platform_device *pdev, -+ struct stmmac_resources *stmmac_res); -+ -+int stmmac_pltfr_remove(struct platform_device *pdev); -+extern const struct dev_pm_ops stmmac_pltfr_pm_ops; -+ -+#endif /* __STMMAC_PLATFORM_H__ */ diff --git a/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch deleted file mode 100644 index 0f2aebe5d4..0000000000 --- a/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch +++ /dev/null @@ -1,146 +0,0 @@ -From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Mon, 11 May 2015 16:32:09 -0700 -Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149 - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++- - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++ - 2 files changed, 85 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -19,8 +19,9 @@ - }; - }; - -- alias { -+ aliases { - serial0 = &uart4; -+ mdio-gpio0 = &mdio0; - }; - - chosen { -@@ -68,6 +69,15 @@ - bias-bus-hold; - }; - }; -+ -+ mdio0_pins: mdio0_pins { -+ mux { -+ pins = "gpio0", "gpio1"; -+ function = "gpio"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -162,6 +172,34 @@ - - linux,part-probe = "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 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>; -+ }; -+ }; - }; - }; - ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -16,6 +16,7 @@ - - alias { - serial0 = &uart2; -+ mdio-gpio0 = &mdio0; - }; - - chosen { -@@ -38,6 +39,15 @@ - bias-none; - }; - }; -+ -+ mdio0_pins: mdio0_pins { -+ mux { -+ pins = "gpio0", "gpio1"; -+ function = "gpio"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi2: gsbi@12480000 { -@@ -140,5 +150,44 @@ - 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 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>; -+ }; -+ -+ phy6: ethernet-phy@6 { -+ device_type = "ethernet-phy"; -+ reg = <6>; -+ }; -+ -+ phy7: ethernet-phy@7 { -+ device_type = "ethernet-phy"; -+ reg = <7>; -+ }; -+ }; - }; - }; diff --git a/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch deleted file mode 100644 index 49203f1cea..0000000000 --- a/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch +++ /dev/null @@ -1,216 +0,0 @@ -From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Mon, 11 May 2015 12:29:18 -0700 -Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++ - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++ - 3 files changed, 160 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -78,6 +78,16 @@ - 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 { -@@ -200,6 +210,31 @@ - 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; -+ }; -+ }; - }; - }; - ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -48,6 +48,14 @@ - bias-disable; - }; - }; -+ -+ rgmii0_pins: rgmii0_pins { -+ mux { -+ pins = "gpio2", "gpio66"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi2: gsbi@12480000 { -@@ -189,5 +197,40 @@ - 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>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -741,6 +741,92 @@ - status = "disabled"; - }; - -+ nss_common: syscon@03000000 { -+ compatible = "syscon"; -+ reg = <0x03000000 0x0000FFFF>; -+ }; -+ -+ qsgmii_csr: syscon@1bb00000 { -+ compatible = "syscon"; -+ reg = <0x1bb00000 0x000001FF>; -+ }; -+ -+ gmac0: ethernet@37000000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37000000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE1_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE1_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac1: ethernet@37200000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37200000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE2_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE2_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac2: ethernet@37400000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37400000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE3_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE3_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac3: ethernet@37600000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37600000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE4_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE4_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch b/target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch deleted file mode 100644 index d385c9a36c..0000000000 --- a/target/linux/ipq806x/patches-3.18/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 5bf2dabde1fa3af0c9082b42b6847ef3fd198b13 Mon Sep 17 00:00:00 2001 -From: Jonas Gorski -Date: Sun, 9 Aug 2015 12:53:55 +0200 -Subject: [PATCH] stmac: platform: add support for retreiving mac from mtd - ---- - drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -@@ -116,6 +116,19 @@ stmmac_probe_config_dt(struct platform_d - return ERR_PTR(-ENOMEM); - - *mac = of_get_mac_address(np); -+ if (!*mac) { -+ u8 mtd_mac[ETH_ALEN]; -+ int ret; -+ -+ ret = of_get_mac_address_mtd(np, mtd_mac); -+ if (ret == -EPROBE_DEFER) -+ return ERR_PTR(ret); -+ -+ if (is_valid_ether_addr(mtd_mac)) -+ *mac = devm_kmemdup(&pdev->dev, mtd_mac, ETH_ALEN, -+ GFP_KERNEL); -+ } -+ - plat->interface = of_get_phy_mode(np); - - /* Get max speed of operation from device tree */ diff --git a/target/linux/ipq806x/patches-3.18/710-stmmac-fix-ipq806x-DMA-configuration.patch b/target/linux/ipq806x/patches-3.18/710-stmmac-fix-ipq806x-DMA-configuration.patch deleted file mode 100644 index c99f60768f..0000000000 --- a/target/linux/ipq806x/patches-3.18/710-stmmac-fix-ipq806x-DMA-configuration.patch +++ /dev/null @@ -1,117 +0,0 @@ ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -@@ -259,6 +259,7 @@ static int ipq806x_gmac_probe(struct pla - { - struct plat_stmmacenet_data *plat_dat; - struct stmmac_resources stmmac_res; -+ struct stmmac_dma_cfg *dma_cfg; - struct device *dev = &pdev->dev; - struct ipq806x_gmac *gmac; - int val; -@@ -348,6 +349,17 @@ static int ipq806x_gmac_probe(struct pla - plat_dat->bsp_priv = gmac; - plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; - -+ dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), -+ GFP_KERNEL); -+ -+ dma_cfg->pbl = 32; -+ dma_cfg->aal = 1; -+ dma_cfg->burst_len = DMA_AXI_BLEN_16 | -+ (7 << DMA_AXI_RD_OSR_LMT_SHIFT) | -+ (7 << DMA_AXI_WR_OSR_LMT_SHIFT); -+ -+ plat_dat->dma_cfg = dma_cfg; -+ - return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - } - ---- a/include/linux/stmmac.h -+++ b/include/linux/stmmac.h -@@ -73,6 +73,9 @@ - | DMA_AXI_BLEN_32 | DMA_AXI_BLEN_64 \ - | DMA_AXI_BLEN_128 | DMA_AXI_BLEN_256) - -+#define DMA_AXI_RD_OSR_LMT_SHIFT 16 -+#define DMA_AXI_WR_OSR_LMT_SHIFT 20 -+ - /* Platfrom data for platform device structure's platform_data field */ - - struct stmmac_mdio_bus_data { -@@ -88,6 +91,7 @@ struct stmmac_mdio_bus_data { - - struct stmmac_dma_cfg { - int pbl; -+ int aal; - int fixed_burst; - int mixed_burst; - int burst_len; ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -@@ -31,7 +31,8 @@ - #include "dwmac_dma.h" - - static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds) -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, -+ int aal) - { - u32 value = readl(ioaddr + DMA_BUS_MODE); - int limit; -@@ -62,6 +63,10 @@ static int dwmac1000_dma_init(void __iom - value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) | - (pbl << DMA_BUS_MODE_RPBL_SHIFT)); - -+ /* Address Aligned Beats */ -+ if (aal) -+ value |= DMA_BUS_MODE_AAL; -+ - /* Set the Fixed burst mode */ - if (fb) - value |= DMA_BUS_MODE_FB; ---- a/drivers/net/ethernet/stmicro/stmmac/common.h -+++ b/drivers/net/ethernet/stmicro/stmmac/common.h -@@ -352,7 +352,7 @@ extern const struct stmmac_desc_ops ndes - struct stmmac_dma_ops { - /* DMA core initialization */ - int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds); -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, int aal); - /* Dump DMA registers */ - void (*dump_regs) (void __iomem *ioaddr); - /* Set tx/rx threshold in the csr6 register ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -@@ -33,7 +33,8 @@ - #include "dwmac_dma.h" - - static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds) -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, -+ int aal) - { - u32 value = readl(ioaddr + DMA_BUS_MODE); - int limit; ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -1639,9 +1639,11 @@ static int stmmac_init_dma_engine(struct - int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, burst_len = 0; - int mixed_burst = 0; - int atds = 0; -+ int aal = 0; - - if (priv->plat->dma_cfg) { - pbl = priv->plat->dma_cfg->pbl; -+ aal = priv->plat->dma_cfg->aal; - fixed_burst = priv->plat->dma_cfg->fixed_burst; - mixed_burst = priv->plat->dma_cfg->mixed_burst; - burst_len = priv->plat->dma_cfg->burst_len; -@@ -1652,7 +1654,7 @@ static int stmmac_init_dma_engine(struct - - return priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst, - burst_len, priv->dma_tx_phy, -- priv->dma_rx_phy, atds); -+ priv->dma_rx_phy, atds, aal); - } - - /** diff --git a/target/linux/ipq806x/patches-3.18/801-ARM-qcom-add-Netgear-Nighthawk-X4-D7800-device-tree.patch b/target/linux/ipq806x/patches-3.18/801-ARM-qcom-add-Netgear-Nighthawk-X4-D7800-device-tree.patch deleted file mode 100644 index eaf037f57e..0000000000 --- a/target/linux/ipq806x/patches-3.18/801-ARM-qcom-add-Netgear-Nighthawk-X4-D7800-device-tree.patch +++ /dev/null @@ -1,381 +0,0 @@ ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -362,6 +362,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-ipq8064-ap148.dtb \ - qcom-ipq8064-db149.dtb \ - qcom-ipq8064-r7500.dtb \ -+ qcom-ipq8064-d7800.dtb \ - qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb \ - qcom-msm8974-sony-xperia-honami.dtb ---- /dev/null -+++ b/arch/arm/boot/dts/qcom-ipq8064-d7800.dts -@@ -0,0 +1,368 @@ -+#include "qcom-ipq8064-v1.0.dtsi" -+ -+#include -+ -+/ { -+ 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 = &uart4; -+ mdio-gpio0 = &mdio0; -+ }; -+ -+ chosen { -+ bootargs = "rootfstype=squashfs noinitrd"; -+ linux,stdout-path = "serial0:115200n8"; -+ }; -+ -+ soc { -+ pinmux@800000 { -+ i2c4_pins: i2c4_pinmux { -+ pins = "gpio12", "gpio13"; -+ function = "gsbi4"; -+ bias-disable; -+ }; -+ -+ pcie0_pins: pcie0_pinmux { -+ mux { -+ pins = "gpio3"; -+ function = "pcie1_rst"; -+ drive-strength = <12>; -+ bias-disable; -+ }; -+ }; -+ -+ pcie1_pins: pcie1_pinmux { -+ mux { -+ pins = "gpio48"; -+ function = "pcie2_rst"; -+ drive-strength = <12>; -+ 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; -+ }; -+ }; -+ -+ 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 = ; -+ 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"; -+ reset-gpio = <&qcom_pinmux 3 0>; -+ pinctrl-0 = <&pcie0_pins>; -+ pinctrl-names = "default"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ reset-gpio = <&qcom_pinmux 48 0>; -+ pinctrl-0 = <&pcie1_pins>; -+ pinctrl-names = "default"; -+ }; -+ -+ nand@1ac00000 { -+ status = "ok"; -+ -+ pinctrl-0 = <&nand_pins>; -+ pinctrl-names = "default"; -+ -+ nand-ecc-strength = <4>; -+ nand-bus-width = <8>; -+ -+ #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 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 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"; -+ 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; -+ }; -+ }; -+ }; -+ -+ gpio-keys { -+ compatible = "gpio-keys"; -+ -+ wifi { -+ label = "wifi"; -+ gpios = <&qcom_pinmux 6 1>; -+ linux,code = ; -+ }; -+ -+ reset { -+ label = "reset"; -+ gpios = <&qcom_pinmux 54 1>; -+ linux,code = ; -+ }; -+ -+ wps { -+ label = "wps"; -+ gpios = <&qcom_pinmux 65 1>; -+ linux,code = ; -+ }; -+ }; -+ -+ gpio-leds { -+ compatible = "gpio-leds"; -+ -+ usb1 { -+ label = "d7800:amber:usb1"; -+ gpios = <&qcom_pinmux 7 0>; -+ }; -+ -+ usb3 { -+ label = "d7800:amber:usb3"; -+ gpios = <&qcom_pinmux 8 0>; -+ }; -+ -+ status { -+ label = "d7800:amber:status"; -+ gpios = <&qcom_pinmux 9 0>; -+ }; -+ -+ internet { -+ label = "d7800:white:internet"; -+ gpios = <&qcom_pinmux 22 0>; -+ }; -+ -+ wan { -+ label = "d7800:white:wan"; -+ gpios = <&qcom_pinmux 23 0>; -+ }; -+ -+ wps { -+ label = "d7800:white:wps"; -+ gpios = <&qcom_pinmux 24 0>; -+ }; -+ -+ esata { -+ label = "d7800:white:esata"; -+ gpios = <&qcom_pinmux 26 0>; -+ }; -+ -+ power { -+ label = "d7800:white:power"; -+ gpios = <&qcom_pinmux 53 0>; -+ default-state = "on"; -+ }; -+ -+ rfkill { -+ label = "d7800:white:rfkill"; -+ gpios = <&qcom_pinmux 64 0>; -+ }; -+ -+ wifi5g { -+ label = "d7800:white:wifi5g"; -+ gpios = <&qcom_pinmux 67 0>; -+ }; -+ }; -+}; -+ -+&adm_dma { -+ status = "ok"; -+}; diff --git a/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch deleted file mode 100644 index c0ad74c492..0000000000 --- a/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -14,6 +14,14 @@ - }; - }; - -+ alias { -+ serial0 = &uart4; -+ }; -+ -+ chosen { -+ linux,stdout-path = "serial0:115200n8"; -+ }; -+ - soc { - pinmux@800000 { - i2c4_pins: i2c4_pinmux { ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -159,7 +159,7 @@ - - syscon-tcsr = <&tcsr>; - -- serial@12490000 { -+ uart2: serial@12490000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x12490000 0x1000>, - <0x12480000 0x1000>; -@@ -197,7 +197,7 @@ - - syscon-tcsr = <&tcsr>; - -- serial@16340000 { -+ uart4: serial@16340000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x16340000 0x1000>, - <0x16300000 0x1000>; -@@ -234,7 +234,7 @@ - - syscon-tcsr = <&tcsr>; - -- serial@1a240000 { -+ uart5: serial@1a240000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x1a240000 0x1000>, - <0x1a200000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch deleted file mode 100644 index bfdb30fe14..0000000000 --- a/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -77,15 +77,7 @@ - spi-max-frequency = <50000000>; - reg = <0>; - -- partition@0 { -- label = "rootfs"; -- reg = <0x0 0x1000000>; -- }; -- -- partition@1 { -- label = "scratch"; -- reg = <0x1000000 0x1000000>; -- }; -+ linux,part-probe = "qcom-smem"; - }; - }; - }; diff --git a/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch deleted file mode 100644 index 7d8c8e8a81..0000000000 --- a/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch +++ /dev/null @@ -1,160 +0,0 @@ -From f26cc3733bdd697bd81ae505fc133fa7c9b6ea19 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 7 Apr 2015 19:58:58 -0700 -Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree - -Add basic DB149 (IPQ806x based platform) device-tree. It supports UART, -SATA, USB2, USB3 and NOR flash. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 132 +++++++++++++++++++++++++++++++ - 2 files changed, 133 insertions(+) - create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -451,6 +451,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-apq8084-ifc6540.dtb \ - qcom-apq8084-mtp.dtb \ - qcom-ipq8064-ap148.dtb \ -+ qcom-ipq8064-db149.dtb \ - qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb \ - qcom-msm8974-sony-xperia-honami.dtb ---- /dev/null -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -0,0 +1,132 @@ -+#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; -+ }; -+ -+ 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; -+ }; -+ }; -+ }; -+ -+ gsbi2: gsbi@12480000 { -+ qcom,mode = ; -+ status = "ok"; -+ uart2: serial@12490000 { -+ status = "ok"; -+ }; -+ }; -+ -+ gsbi5: gsbi@1a200000 { -+ qcom,mode = ; -+ 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"; -+ }; -+ }; -+}; diff --git a/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch deleted file mode 100644 index 6b296377b6..0000000000 --- a/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch +++ /dev/null @@ -1,53 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -46,15 +46,12 @@ - serial@16340000 { - status = "ok"; - }; -- -- i2c4: i2c@16380000 { -- status = "ok"; -- -- clock-frequency = <200000>; -- -- pinctrl-0 = <&i2c4_pins>; -- pinctrl-names = "default"; -- }; -+ /* -+ * 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 { ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -807,7 +807,7 @@ static struct clk_rcg gsbi7_qup_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, - }, - }, - }; -@@ -823,7 +823,7 @@ static struct clk_branch gsbi7_qup_clk = - .parent_names = (const char *[]){ "gsbi7_qup_src" }, - .num_parents = 1, - .ops = &clk_branch_ops, -- .flags = CLK_SET_RATE_PARENT, -+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, - }, - }, - }; -@@ -871,7 +871,7 @@ static struct clk_branch gsbi4_h_clk = { - .hw.init = &(struct clk_init_data){ - .name = "gsbi4_h_clk", - .ops = &clk_branch_ops, -- .flags = CLK_IS_ROOT, -+ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, - }, - }, - }; diff --git a/target/linux/ipq806x/patches-4.1/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-4.1/024-ap148-add-memory-node.patch deleted file mode 100644 index f026ed9394..0000000000 --- a/target/linux/ipq806x/patches-4.1/024-ap148-add-memory-node.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -4,6 +4,11 @@ - 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>; diff --git a/target/linux/ipq806x/patches-4.1/030-hwspinlock-core-add-device-tree-support.patch b/target/linux/ipq806x/patches-4.1/030-hwspinlock-core-add-device-tree-support.patch deleted file mode 100644 index 04f35b7ec7..0000000000 --- a/target/linux/ipq806x/patches-4.1/030-hwspinlock-core-add-device-tree-support.patch +++ /dev/null @@ -1,167 +0,0 @@ -From fb7737e949e31d8a71acee6bbb670f32dbd2a2c0 Mon Sep 17 00:00:00 2001 -From: Suman Anna -Date: Wed, 4 Mar 2015 20:01:14 -0600 -Subject: [PATCH] hwspinlock/core: add device tree support - -This patch adds a new OF-friendly API of_hwspin_lock_get_id() -for hwspinlock clients to use/request locks from a hwspinlock -device instantiated through a device-tree blob. This new API -can be used by hwspinlock clients to get the id for a specific -lock using the phandle + args specifier, so that it can be -requested using the available hwspin_lock_request_specific() -API. - -Signed-off-by: Suman Anna -Reviewed-by: Bjorn Andersson -[small comment clarification] -Signed-off-by: Ohad Ben-Cohen ---- - Documentation/hwspinlock.txt | 10 +++++ - drivers/hwspinlock/hwspinlock_core.c | 79 ++++++++++++++++++++++++++++++++++++ - include/linux/hwspinlock.h | 7 ++++ - 3 files changed, 96 insertions(+) - ---- a/Documentation/hwspinlock.txt -+++ b/Documentation/hwspinlock.txt -@@ -48,6 +48,16 @@ independent, drivers. - ids for predefined purposes. - Should be called from a process context (might sleep). - -+ int of_hwspin_lock_get_id(struct device_node *np, int index); -+ - retrieve the global lock id for an OF phandle-based specific lock. -+ This function provides a means for DT users of a hwspinlock module -+ to get the global lock id of a specific hwspinlock, so that it can -+ be requested using the normal hwspin_lock_request_specific() API. -+ The function returns a lock id number on success, -EPROBE_DEFER if -+ the hwspinlock device is not yet registered with the core, or other -+ error values. -+ Should be called from a process context (might sleep). -+ - int hwspin_lock_free(struct hwspinlock *hwlock); - - free a previously-assigned hwspinlock; returns 0 on success, or an - appropriate error code on failure (e.g. -EINVAL if the hwspinlock ---- a/drivers/hwspinlock/hwspinlock_core.c -+++ b/drivers/hwspinlock/hwspinlock_core.c -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - - #include "hwspinlock_internal.h" - -@@ -257,6 +258,84 @@ void __hwspin_unlock(struct hwspinlock * - } - EXPORT_SYMBOL_GPL(__hwspin_unlock); - -+/** -+ * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id -+ * @bank: the hwspinlock device bank -+ * @hwlock_spec: hwlock specifier as found in the device tree -+ * -+ * This is a simple translation function, suitable for hwspinlock platform -+ * drivers that only has a lock specifier length of 1. -+ * -+ * Returns a relative index of the lock within a specified bank on success, -+ * or -EINVAL on invalid specifier cell count. -+ */ -+static inline int -+of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec) -+{ -+ if (WARN_ON(hwlock_spec->args_count != 1)) -+ return -EINVAL; -+ -+ return hwlock_spec->args[0]; -+} -+ -+/** -+ * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock -+ * @np: device node from which to request the specific hwlock -+ * @index: index of the hwlock in the list of values -+ * -+ * This function provides a means for DT users of the hwspinlock module to -+ * get the global lock id of a specific hwspinlock using the phandle of the -+ * hwspinlock device, so that it can be requested using the normal -+ * hwspin_lock_request_specific() API. -+ * -+ * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock -+ * device is not yet registered, -EINVAL on invalid args specifier value or an -+ * appropriate error as returned from the OF parsing of the DT client node. -+ */ -+int of_hwspin_lock_get_id(struct device_node *np, int index) -+{ -+ struct of_phandle_args args; -+ struct hwspinlock *hwlock; -+ struct radix_tree_iter iter; -+ void **slot; -+ int id; -+ int ret; -+ -+ ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index, -+ &args); -+ if (ret) -+ return ret; -+ -+ /* Find the hwspinlock device: we need its base_id */ -+ ret = -EPROBE_DEFER; -+ rcu_read_lock(); -+ radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) { -+ hwlock = radix_tree_deref_slot(slot); -+ if (unlikely(!hwlock)) -+ continue; -+ -+ if (hwlock->bank->dev->of_node == args.np) { -+ ret = 0; -+ break; -+ } -+ } -+ rcu_read_unlock(); -+ if (ret < 0) -+ goto out; -+ -+ id = of_hwspin_lock_simple_xlate(&args); -+ if (id < 0 || id >= hwlock->bank->num_locks) { -+ ret = -EINVAL; -+ goto out; -+ } -+ id += hwlock->bank->base_id; -+ -+out: -+ of_node_put(args.np); -+ return ret ? ret : id; -+} -+EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id); -+ - static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) - { - struct hwspinlock *tmp; ---- a/include/linux/hwspinlock.h -+++ b/include/linux/hwspinlock.h -@@ -26,6 +26,7 @@ - #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ - - struct device; -+struct device_node; - struct hwspinlock; - struct hwspinlock_device; - struct hwspinlock_ops; -@@ -66,6 +67,7 @@ int hwspin_lock_unregister(struct hwspin - struct hwspinlock *hwspin_lock_request(void); - struct hwspinlock *hwspin_lock_request_specific(unsigned int id); - int hwspin_lock_free(struct hwspinlock *hwlock); -+int of_hwspin_lock_get_id(struct device_node *np, int index); - int hwspin_lock_get_id(struct hwspinlock *hwlock); - int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int, - unsigned long *); -@@ -120,6 +122,11 @@ void __hwspin_unlock(struct hwspinlock * - { - } - -+static inline int of_hwspin_lock_get_id(struct device_node *np, int index) -+{ -+ return 0; -+} -+ - static inline int hwspin_lock_get_id(struct hwspinlock *hwlock) - { - return 0; diff --git a/target/linux/ipq806x/patches-4.1/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch b/target/linux/ipq806x/patches-4.1/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch deleted file mode 100644 index 581b199d7f..0000000000 --- a/target/linux/ipq806x/patches-4.1/031-hwspinlock-qcom-Add-support-for-Qualcomm-HW-Mutex-bl.patch +++ /dev/null @@ -1,234 +0,0 @@ -From 19a0f61224d2d91860fa8291ab63cb104ee86bdd Mon Sep 17 00:00:00 2001 -From: Bjorn Andersson -Date: Tue, 24 Mar 2015 10:11:05 -0700 -Subject: [PATCH] hwspinlock: qcom: Add support for Qualcomm HW Mutex block - -Add driver for Qualcomm Hardware Mutex block found in many Qualcomm -SoCs. - -Based on initial effort by Kumar Gala - -Signed-off-by: Bjorn Andersson -Reviewed-by: Andy Gross -Reviewed-by: Jeffrey Hugo -Signed-off-by: Ohad Ben-Cohen ---- - drivers/hwspinlock/Kconfig | 12 +++ - drivers/hwspinlock/Makefile | 1 + - drivers/hwspinlock/qcom_hwspinlock.c | 181 +++++++++++++++++++++++++++++++++++ - 3 files changed, 194 insertions(+) - create mode 100644 drivers/hwspinlock/qcom_hwspinlock.c - ---- a/drivers/hwspinlock/Kconfig -+++ b/drivers/hwspinlock/Kconfig -@@ -18,6 +18,18 @@ config HWSPINLOCK_OMAP - - If unsure, say N. - -+config HWSPINLOCK_QCOM -+ tristate "Qualcomm Hardware Spinlock device" -+ depends on ARCH_QCOM -+ select HWSPINLOCK -+ select MFD_SYSCON -+ help -+ Say y here to support the Qualcomm Hardware Mutex functionality, which -+ provides a synchronisation mechanism for the various processors on -+ the SoC. -+ -+ If unsure, say N. -+ - config HSEM_U8500 - tristate "STE Hardware Semaphore functionality" - depends on ARCH_U8500 ---- a/drivers/hwspinlock/Makefile -+++ b/drivers/hwspinlock/Makefile -@@ -4,4 +4,5 @@ - - obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o - obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o -+obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o - obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o ---- /dev/null -+++ b/drivers/hwspinlock/qcom_hwspinlock.c -@@ -0,0 +1,181 @@ -+/* -+ * Copyright (c) 2013, The Linux Foundation. All rights reserved. -+ * Copyright (c) 2015, Sony Mobile Communications AB -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "hwspinlock_internal.h" -+ -+#define QCOM_MUTEX_APPS_PROC_ID 1 -+#define QCOM_MUTEX_NUM_LOCKS 32 -+ -+static int qcom_hwspinlock_trylock(struct hwspinlock *lock) -+{ -+ struct regmap_field *field = lock->priv; -+ u32 lock_owner; -+ int ret; -+ -+ ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID); -+ if (ret) -+ return ret; -+ -+ ret = regmap_field_read(field, &lock_owner); -+ if (ret) -+ return ret; -+ -+ return lock_owner == QCOM_MUTEX_APPS_PROC_ID; -+} -+ -+static void qcom_hwspinlock_unlock(struct hwspinlock *lock) -+{ -+ struct regmap_field *field = lock->priv; -+ u32 lock_owner; -+ int ret; -+ -+ ret = regmap_field_read(field, &lock_owner); -+ if (ret) { -+ pr_err("%s: unable to query spinlock owner\n", __func__); -+ return; -+ } -+ -+ if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) { -+ pr_err("%s: spinlock not owned by us (actual owner is %d)\n", -+ __func__, lock_owner); -+ } -+ -+ ret = regmap_field_write(field, 0); -+ if (ret) -+ pr_err("%s: failed to unlock spinlock\n", __func__); -+} -+ -+static const struct hwspinlock_ops qcom_hwspinlock_ops = { -+ .trylock = qcom_hwspinlock_trylock, -+ .unlock = qcom_hwspinlock_unlock, -+}; -+ -+static const struct of_device_id qcom_hwspinlock_of_match[] = { -+ { .compatible = "qcom,sfpb-mutex" }, -+ { .compatible = "qcom,tcsr-mutex" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); -+ -+static int qcom_hwspinlock_probe(struct platform_device *pdev) -+{ -+ struct hwspinlock_device *bank; -+ struct device_node *syscon; -+ struct reg_field field; -+ struct regmap *regmap; -+ size_t array_size; -+ u32 stride; -+ u32 base; -+ int ret; -+ int i; -+ -+ syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0); -+ if (!syscon) { -+ dev_err(&pdev->dev, "no syscon property\n"); -+ return -ENODEV; -+ } -+ -+ regmap = syscon_node_to_regmap(syscon); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); -+ -+ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &base); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "no offset in syscon\n"); -+ return -EINVAL; -+ } -+ -+ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, &stride); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "no stride syscon\n"); -+ return -EINVAL; -+ } -+ -+ array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock); -+ bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL); -+ if (!bank) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, bank); -+ -+ for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { -+ field.reg = base + i * stride; -+ field.lsb = 0; -+ field.msb = 32; -+ -+ bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, -+ regmap, field); -+ } -+ -+ pm_runtime_enable(&pdev->dev); -+ -+ ret = hwspin_lock_register(bank, &pdev->dev, &qcom_hwspinlock_ops, -+ 0, QCOM_MUTEX_NUM_LOCKS); -+ if (ret) -+ pm_runtime_disable(&pdev->dev); -+ -+ return ret; -+} -+ -+static int qcom_hwspinlock_remove(struct platform_device *pdev) -+{ -+ struct hwspinlock_device *bank = platform_get_drvdata(pdev); -+ int ret; -+ -+ ret = hwspin_lock_unregister(bank); -+ if (ret) { -+ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); -+ return ret; -+ } -+ -+ pm_runtime_disable(&pdev->dev); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_hwspinlock_driver = { -+ .probe = qcom_hwspinlock_probe, -+ .remove = qcom_hwspinlock_remove, -+ .driver = { -+ .name = "qcom_hwspinlock", -+ .of_match_table = qcom_hwspinlock_of_match, -+ }, -+}; -+ -+static int __init qcom_hwspinlock_init(void) -+{ -+ return platform_driver_register(&qcom_hwspinlock_driver); -+} -+/* board init code might need to reserve hwspinlocks for predefined purposes */ -+postcore_initcall(qcom_hwspinlock_init); -+ -+static void __exit qcom_hwspinlock_exit(void) -+{ -+ platform_driver_unregister(&qcom_hwspinlock_driver); -+} -+module_exit(qcom_hwspinlock_exit); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs"); diff --git a/target/linux/ipq806x/patches-4.1/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch b/target/linux/ipq806x/patches-4.1/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch deleted file mode 100644 index 70d5a58248..0000000000 --- a/target/linux/ipq806x/patches-4.1/032-hwspinlock-qcom-Correct-msb-in-regmap_field.patch +++ /dev/null @@ -1,26 +0,0 @@ -From bd5717a4632cdecafe82d03de7dcb3b1876e2828 Mon Sep 17 00:00:00 2001 -From: Bjorn Andersson -Date: Fri, 26 Jun 2015 14:47:21 -0700 -Subject: [PATCH] hwspinlock: qcom: Correct msb in regmap_field - -msb of the regmap_field was mistakenly given the value 32, to set all bits -in the regmap update mask; although incorrect this worked until 921cc294, -where the mask calculation was corrected. - -Signed-off-by: Bjorn Andersson -Signed-off-by: Ohad Ben-Cohen ---- - drivers/hwspinlock/qcom_hwspinlock.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/hwspinlock/qcom_hwspinlock.c -+++ b/drivers/hwspinlock/qcom_hwspinlock.c -@@ -123,7 +123,7 @@ static int qcom_hwspinlock_probe(struct - for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { - field.reg = base + i * stride; - field.lsb = 0; -- field.msb = 32; -+ field.msb = 31; - - bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, - regmap, field); diff --git a/target/linux/ipq806x/patches-4.1/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.1/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch deleted file mode 100644 index a126d69441..0000000000 --- a/target/linux/ipq806x/patches-4.1/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch +++ /dev/null @@ -1,33 +0,0 @@ -From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 13 Aug 2015 09:45:26 -0700 -Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts - -Add one new node to the ipq806x.dtsi file to declare & register the -hardware spinlock devices. This mechanism is required to be used by -other drivers such as SMEM. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++ - 1 file changed, 11 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -329,5 +329,16 @@ - #reset-cells = <1>; - }; - -+ sfpb_mutex_block: syscon@1200600 { -+ compatible = "syscon"; -+ reg = <0x01200600 0x100>; -+ }; - }; -+ -+ sfpb_mutex: sfpb-mutex { -+ compatible = "qcom,sfpb-mutex"; -+ syscon = <&sfpb_mutex_block 4 4>; -+ -+ #hwlock-cells = <1>; -+ }; - }; diff --git a/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch b/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch deleted file mode 100644 index d22db2226e..0000000000 --- a/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch +++ /dev/null @@ -1,82 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,1/2] soc: qcom: Add device tree binding for SMEM -From: Bjorn Andersson -X-Patchwork-Id: 6202201 -Message-Id: <1428795178-24312-1-git-send-email-bjorn.andersson@sonymobile.com> -To: Rob Herring , Pawel Moll , - Mark Rutland , - Ian Campbell , - Kumar Gala , Jeffrey Hugo , - Andry Gross -Cc: , - linux-arm-msm , - -Date: Sat, 11 Apr 2015 16:32:57 -0700 - -Add device tree binding documentation for the Qualcom Shared Memory -manager. - -Signed-off-by: Bjorn Andersson - ---- -Changes since v1: -- None - - .../devicetree/bindings/soc/qcom/qcom,smem.txt | 49 ++++++++++++++++++++++ - 1 file changed, 49 insertions(+) - create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt -@@ -0,0 +1,49 @@ -+Qualcomm Shared Memory binding -+ -+This binding describes the Qualcomm Shared Memory, used to share data between -+various subsystems and OSes in Qualcomm platforms. -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: must be: -+ "qcom,smem" -+ -+- memory-region: -+ Usage: required -+ Value type: -+ Definition: handle to memory reservation for main smem memory region. -+ -+- reg: -+ Usage: optional -+ Value type: -+ Definition: base address and size pair for any additional memory areas -+ of the shared memory. -+ -+- hwspinlocks: -+ Usage: required -+ Value type: -+ Definition: reference to a hwspinlock used to protect allocations from -+ the shared memory -+ -+= EXAMPLE -+ -+ reserved-memory { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges; -+ -+ smem_region: smem@fa00000 { -+ reg = <0xfa00000 0x200000>; -+ no-map; -+ }; -+ }; -+ -+ smem@fa00000 { -+ compatible = "qcom,smem"; -+ -+ memory-region = <&smem_region>; -+ reg = <0xfc428000 0x4000>; -+ -+ hwlocks = <&tcsr_mutex 3>; -+ }; diff --git a/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch b/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch deleted file mode 100644 index 5598da26d8..0000000000 --- a/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch +++ /dev/null @@ -1,841 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,2/2] soc: qcom: Add Shared Memory Manager driver -From: Bjorn Andersson -X-Patchwork-Id: 6202211 -Message-Id: <1428795178-24312-2-git-send-email-bjorn.andersson@sonymobile.com> -To: Kumar Gala , Andy Gross , - David Brown , Jeffrey Hugo -Cc: , , - -Date: Sat, 11 Apr 2015 16:32:58 -0700 - -The Shared Memory Manager driver implements an interface for allocating -and accessing items in the memory area shared among all of the -processors in a Qualcomm platform. - -Signed-off-by: Bjorn Andersson -Reviewed-by: Andy Gross -Tested-by: Andy Gross - ---- -Changes since v1: -- ioremapping the regions nocache -- improved documentation of the two regions of partitions -- corrected free space check in private allocator - - drivers/soc/qcom/Kconfig | 7 + - drivers/soc/qcom/Makefile | 1 + - drivers/soc/qcom/smem.c | 768 ++++++++++++++++++++++++++++++++++++++++++ - include/linux/soc/qcom/smem.h | 14 + - 4 files changed, 790 insertions(+) - create mode 100644 drivers/soc/qcom/smem.c - create mode 100644 include/linux/soc/qcom/smem.h - ---- a/drivers/soc/qcom/Kconfig -+++ b/drivers/soc/qcom/Kconfig -@@ -10,3 +10,10 @@ config QCOM_GSBI - functions for connecting the underlying serial UART, SPI, and I2C - devices to the output pins. - -+config QCOM_SMEM -+ tristate "Qualcomm Shared Memory Manager (SMEM)" -+ depends on ARCH_QCOM -+ help -+ Say y here to enable support for the Qualcomm Shared Memory Manager. -+ The driver provides an interface to items in a heap shared among all -+ processors in a Qualcomm platform. ---- a/drivers/soc/qcom/Makefile -+++ b/drivers/soc/qcom/Makefile -@@ -1 +1,2 @@ - obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o -+obj-$(CONFIG_QCOM_SMEM) += smem.o ---- /dev/null -+++ b/drivers/soc/qcom/smem.c -@@ -0,0 +1,768 @@ -+/* -+ * Copyright (c) 2015, Sony Mobile Communications AB. -+ * Copyright (c) 2012-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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * The Qualcomm shared memory system is a allocate only heap structure that -+ * consists of one of more memory areas that can be accessed by the processors -+ * in the SoC. -+ * -+ * All systems contains a global heap, accessible by all processors in the SoC, -+ * with a table of contents data structure (@smem_header) at the beginning of -+ * the main shared memory block. -+ * -+ * The global header contains metadata for allocations as well as a fixed list -+ * of 512 entries (@smem_global_entry) that can be initialized to reference -+ * parts of the shared memory space. -+ * -+ * -+ * In addition to this global heap a set of "private" heaps can be set up at -+ * boot time with access restrictions so that only certain processor pairs can -+ * access the data. -+ * -+ * These partitions are referenced from an optional partition table -+ * (@smem_ptable), that is found 4kB from the end of the main smem region. The -+ * partition table entries (@smem_ptable_entry) lists the involved processors -+ * (or hosts) and their location in the main shared memory region. -+ * -+ * Each partition starts with a header (@smem_partition_header) that identifies -+ * the partition and holds properties for the two internal memory regions. The -+ * two regions are cached and non-cached memory respectively. Each region -+ * contain a link list of allocation headers (@smem_private_entry) followed by -+ * their data. -+ * -+ * Items in the non-cached region are allocated from the start of the partition -+ * while items in the cached region are allocated from the end. The free area -+ * is hence the region between the cached and non-cached offsets. -+ * -+ * -+ * To synchronize allocations in the shared memory heaps a remote spinlock must -+ * be held - currently lock number 3 of the sfpb or tcsr is used for this on all -+ * platforms. -+ * -+ */ -+ -+/** -+ * struct smem_proc_comm - proc_comm communication struct (legacy) -+ * @command: current command to be executed -+ * @status: status of the currently requested command -+ * @params: parameters to the command -+ */ -+struct smem_proc_comm { -+ u32 command; -+ u32 status; -+ u32 params[2]; -+}; -+ -+/** -+ * struct smem_global_entry - entry to reference smem items on the heap -+ * @allocated: boolean to indicate if this entry is used -+ * @offset: offset to the allocated space -+ * @size: size of the allocated space, 8 byte aligned -+ * @aux_base: base address for the memory region used by this unit, or 0 for -+ * the default region. bits 0,1 are reserved -+ */ -+struct smem_global_entry { -+ u32 allocated; -+ u32 offset; -+ u32 size; -+ u32 aux_base; /* bits 1:0 reserved */ -+}; -+#define AUX_BASE_MASK 0xfffffffc -+ -+/** -+ * struct smem_header - header found in beginning of primary smem region -+ * @proc_comm: proc_comm communication interface (legacy) -+ * @version: array of versions for the various subsystems -+ * @initialized: boolean to indicate that smem is initialized -+ * @free_offset: index of the first unallocated byte in smem -+ * @available: number of bytes available for allocation -+ * @reserved: reserved field, must be 0 -+ * toc: array of references to items -+ */ -+struct smem_header { -+ struct smem_proc_comm proc_comm[4]; -+ u32 version[32]; -+ u32 initialized; -+ u32 free_offset; -+ u32 available; -+ u32 reserved; -+ struct smem_global_entry toc[]; -+}; -+ -+/** -+ * struct smem_ptable_entry - one entry in the @smem_ptable list -+ * @offset: offset, within the main shared memory region, of the partition -+ * @size: size of the partition -+ * @flags: flags for the partition (currently unused) -+ * @host0: first processor/host with access to this partition -+ * @host1: second processor/host with access to this partition -+ * @reserved: reserved entries for later use -+ */ -+struct smem_ptable_entry { -+ u32 offset; -+ u32 size; -+ u32 flags; -+ u16 host0; -+ u16 host1; -+ u32 reserved[8]; -+}; -+ -+/** -+ * struct smem_ptable - partition table for the private partitions -+ * @magic: magic number, must be SMEM_PTABLE_MAGIC -+ * @version: version of the partition table -+ * @num_entries: number of partitions in the table -+ * @reserved: for now reserved entries -+ * @entry: list of @smem_ptable_entry for the @num_entries partitions -+ */ -+struct smem_ptable { -+ u32 magic; -+ u32 version; -+ u32 num_entries; -+ u32 reserved[5]; -+ struct smem_ptable_entry entry[]; -+}; -+#define SMEM_PTABLE_MAGIC 0x434f5424 /* "$TOC" */ -+ -+/** -+ * struct smem_partition_header - header of the partitions -+ * @magic: magic number, must be SMEM_PART_MAGIC -+ * @host0: first processor/host with access to this partition -+ * @host1: second processor/host with access to this partition -+ * @size: size of the partition -+ * @offset_free_uncached: offset to the first free byte of uncached memory in -+ * this partition -+ * @offset_free_cached: offset to the first free byte of cached memory in this -+ * partition -+ * @reserved: for now reserved entries -+ */ -+struct smem_partition_header { -+ u32 magic; -+ u16 host0; -+ u16 host1; -+ u32 size; -+ u32 offset_free_uncached; -+ u32 offset_free_cached; -+ u32 reserved[3]; -+}; -+#define SMEM_PART_MAGIC 0x54525024 /* "$PRT" */ -+ -+/** -+ * struct smem_private_entry - header of each item in the private partition -+ * @canary: magic number, must be SMEM_PRIVATE_CANARY -+ * @item: identifying number of the smem item -+ * @size: size of the data, including padding bytes -+ * @padding_data: number of bytes of padding of data -+ * @padding_hdr: number of bytes of padding between the header and the data -+ * @reserved: for now reserved entry -+ */ -+struct smem_private_entry { -+ u16 canary; -+ u16 item; -+ u32 size; /* includes padding bytes */ -+ u16 padding_data; -+ u16 padding_hdr; -+ u32 reserved; -+}; -+#define SMEM_PRIVATE_CANARY 0xa5a5 -+ -+/* -+ * Item 3 of the global heap contains an array of versions for the various -+ * software components in the SoC. We verify that the boot loader version is -+ * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check. -+ */ -+#define SMEM_ITEM_VERSION 3 -+#define SMEM_MASTER_SBL_VERSION_INDEX 7 -+#define SMEM_EXPECTED_VERSION 11 -+ -+/* -+ * The first 8 items are only to be allocated by the boot loader while -+ * initializing the heap. -+ */ -+#define SMEM_ITEM_LAST_FIXED 8 -+ -+/* Highest accepted item number, for both global and private heaps */ -+#define SMEM_ITEM_LAST 512 -+ -+/* Processor/host identifier for the application processor */ -+#define SMEM_HOST_APPS 0 -+ -+/* Max number of processors/hosts in a system */ -+#define SMEM_HOST_COUNT 7 -+ -+/** -+ * struct smem_region - representation of a chunk of memory used for smem -+ * @aux_base: identifier of aux_mem base -+ * @virt_base: virtual base address of memory with this aux_mem identifier -+ * @size: size of the memory region -+ */ -+struct smem_region { -+ u32 aux_base; -+ void __iomem *virt_base; -+ size_t size; -+}; -+ -+/** -+ * struct qcom_smem - device data for the smem device -+ * @dev: device pointer -+ * @hwlock: reference to a hwspinlock -+ * @partitions: list of pointers to partitions affecting the current -+ * processor/host -+ * @num_regions: number of @regions -+ * @regions: list of the memory regions defining the shared memory -+ */ -+struct qcom_smem { -+ struct device *dev; -+ -+ struct hwspinlock *hwlock; -+ -+ struct smem_partition_header *partitions[SMEM_HOST_COUNT]; -+ -+ unsigned num_regions; -+ struct smem_region regions[0]; -+}; -+ -+/* Pointer to the one and only smem handle */ -+static struct qcom_smem *__smem; -+ -+/* Timeout (ms) for the trylock of remote spinlocks */ -+#define HWSPINLOCK_TIMEOUT 1000 -+ -+static int qcom_smem_alloc_private(struct qcom_smem *smem, -+ unsigned host, -+ unsigned item, -+ size_t size) -+{ -+ struct smem_partition_header *phdr; -+ struct smem_private_entry *hdr; -+ size_t alloc_size; -+ void *p; -+ -+ /* We're not going to find it if there's no matching partition */ -+ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) -+ return -ENOENT; -+ -+ phdr = smem->partitions[host]; -+ -+ p = (void *)phdr + sizeof(*phdr); -+ while (p < (void *)phdr + phdr->offset_free_uncached) { -+ hdr = p; -+ -+ if (hdr->canary != SMEM_PRIVATE_CANARY) { -+ dev_err(smem->dev, -+ "Found invalid canary in host %d partition\n", -+ host); -+ return -EINVAL; -+ } -+ -+ if (hdr->item == item) -+ return -EEXIST; -+ -+ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; -+ } -+ -+ /* Check that we don't grow into the cached region */ -+ alloc_size = sizeof(*hdr) + ALIGN(size, 8); -+ if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) { -+ dev_err(smem->dev, "Out of memory\n"); -+ return -ENOSPC; -+ } -+ -+ hdr = p; -+ hdr->canary = SMEM_PRIVATE_CANARY; -+ hdr->item = item; -+ hdr->size = ALIGN(size, 8); -+ hdr->padding_data = hdr->size - size; -+ hdr->padding_hdr = 0; -+ -+ /* -+ * Ensure the header is written before we advance the free offset, so -+ * that remote processors that does not take the remote spinlock still -+ * gets a consistent view of the linked list. -+ */ -+ wmb(); -+ phdr->offset_free_uncached += alloc_size; -+ -+ return 0; -+} -+ -+static int qcom_smem_alloc_global(struct qcom_smem *smem, -+ unsigned item, -+ size_t size) -+{ -+ struct smem_header *header; -+ struct smem_global_entry *entry; -+ -+ if (WARN_ON(item >= SMEM_ITEM_LAST)) -+ return -EINVAL; -+ -+ header = smem->regions[0].virt_base; -+ entry = &header->toc[item]; -+ if (entry->allocated) -+ return -EEXIST; -+ -+ size = ALIGN(size, 8); -+ if (WARN_ON(size > header->available)) -+ return -ENOMEM; -+ -+ entry->offset = header->free_offset; -+ entry->size = size; -+ -+ /* -+ * Ensure the header is consistent before we mark the item allocated, -+ * so that remote processors will get a consistent view of the item -+ * even though they do not take the spinlock on read. -+ */ -+ wmb(); -+ entry->allocated = 1; -+ -+ header->free_offset += size; -+ header->available -= size; -+ -+ return 0; -+} -+ -+/** -+ * qcom_smem_alloc - allocate space for a smem item -+ * @host: remote processor id, or -1 -+ * @item: smem item handle -+ * @size: number of bytes to be allocated -+ * -+ * Allocate space for a given smem item of size @size, given that the item is -+ * not yet allocated. -+ */ -+int qcom_smem_alloc(unsigned host, unsigned item, size_t size) -+{ -+ unsigned long flags; -+ int ret; -+ -+ if (!__smem) -+ return -EPROBE_DEFER; -+ -+ if (item < SMEM_ITEM_LAST_FIXED) { -+ dev_err(__smem->dev, -+ "Rejecting allocation of static entry %d\n", item); -+ return -EINVAL; -+ } -+ -+ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, -+ HWSPINLOCK_TIMEOUT, -+ &flags); -+ if (ret) -+ return ret; -+ -+ ret = qcom_smem_alloc_private(__smem, host, item, size); -+ if (ret == -ENOENT) -+ ret = qcom_smem_alloc_global(__smem, item, size); -+ -+ hwspin_unlock_irqrestore(__smem->hwlock, &flags); -+ -+ return ret; -+} -+EXPORT_SYMBOL(qcom_smem_alloc); -+ -+static int qcom_smem_get_global(struct qcom_smem *smem, -+ unsigned item, -+ void **ptr, -+ size_t *size) -+{ -+ struct smem_header *header; -+ struct smem_region *area; -+ struct smem_global_entry *entry; -+ u32 aux_base; -+ unsigned i; -+ -+ if (WARN_ON(item >= SMEM_ITEM_LAST)) -+ return -EINVAL; -+ -+ header = smem->regions[0].virt_base; -+ entry = &header->toc[item]; -+ if (!entry->allocated) -+ return -ENXIO; -+ -+ if (ptr != NULL) { -+ aux_base = entry->aux_base & AUX_BASE_MASK; -+ -+ for (i = 0; i < smem->num_regions; i++) { -+ area = &smem->regions[i]; -+ -+ if (area->aux_base == aux_base || !aux_base) { -+ *ptr = area->virt_base + entry->offset; -+ break; -+ } -+ } -+ } -+ if (size != NULL) -+ *size = entry->size; -+ -+ return 0; -+} -+ -+static int qcom_smem_get_private(struct qcom_smem *smem, -+ unsigned host, -+ unsigned item, -+ void **ptr, -+ size_t *size) -+{ -+ struct smem_partition_header *phdr; -+ struct smem_private_entry *hdr; -+ void *p; -+ -+ /* We're not going to find it if there's no matching partition */ -+ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) -+ return -ENOENT; -+ -+ phdr = smem->partitions[host]; -+ -+ p = (void *)phdr + sizeof(*phdr); -+ while (p < (void *)phdr + phdr->offset_free_uncached) { -+ hdr = p; -+ -+ if (hdr->canary != SMEM_PRIVATE_CANARY) { -+ dev_err(smem->dev, -+ "Found invalid canary in host %d partition\n", -+ host); -+ return -EINVAL; -+ } -+ -+ if (hdr->item == item) { -+ if (ptr != NULL) -+ *ptr = p + sizeof(*hdr) + hdr->padding_hdr; -+ -+ if (size != NULL) -+ *size = hdr->size - hdr->padding_data; -+ -+ return 0; -+ } -+ -+ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; -+ } -+ -+ return -ENOENT; -+} -+ -+/** -+ * qcom_smem_get - resolve ptr of size of a smem item -+ * @host: the remote processor, or -1 -+ * @item: smem item handle -+ * @ptr: pointer to be filled out with address of the item -+ * @size: pointer to be filled out with size of the item -+ * -+ * Looks up pointer and size of a smem item. -+ */ -+int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size) -+{ -+ unsigned long flags; -+ int ret; -+ -+ if (!__smem) -+ return -EPROBE_DEFER; -+ -+ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, -+ HWSPINLOCK_TIMEOUT, -+ &flags); -+ if (ret) -+ return ret; -+ -+ ret = qcom_smem_get_private(__smem, host, item, ptr, size); -+ if (ret == -ENOENT) -+ ret = qcom_smem_get_global(__smem, item, ptr, size); -+ -+ hwspin_unlock_irqrestore(__smem->hwlock, &flags); -+ return ret; -+ -+} -+EXPORT_SYMBOL(qcom_smem_get); -+ -+/** -+ * qcom_smem_get_free_space - retrieve amont of free space in a partition -+ * @host: the remote processor identifing a partition, or -1 -+ * -+ * To be used by smem clients as a quick way to determine if any new -+ * allocations has been made. -+ */ -+int qcom_smem_get_free_space(unsigned host) -+{ -+ struct smem_partition_header *phdr; -+ struct smem_header *header; -+ unsigned ret; -+ -+ if (!__smem) -+ return -EPROBE_DEFER; -+ -+ if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { -+ phdr = __smem->partitions[host]; -+ ret = phdr->offset_free_uncached; -+ } else { -+ header = __smem->regions[0].virt_base; -+ ret = header->available; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(qcom_smem_get_free_space); -+ -+static int qcom_smem_get_sbl_version(struct qcom_smem *smem) -+{ -+ unsigned *versions; -+ size_t size; -+ int ret; -+ -+ ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, -+ (void **)&versions, &size); -+ if (ret < 0) { -+ dev_err(smem->dev, "Unable to read the version item\n"); -+ return -ENOENT; -+ } -+ -+ if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) { -+ dev_err(smem->dev, "Version item is too small\n"); -+ return -EINVAL; -+ } -+ -+ return versions[SMEM_MASTER_SBL_VERSION_INDEX]; -+} -+ -+static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, -+ unsigned local_host) -+{ -+ struct smem_partition_header *header; -+ struct smem_ptable_entry *entry; -+ struct smem_ptable *ptable; -+ unsigned remote_host; -+ int i; -+ -+ ptable = smem->regions[0].virt_base + smem->regions[0].size - 4 * 1024; -+ if (ptable->magic != SMEM_PTABLE_MAGIC) -+ return 0; -+ -+ if (ptable->version != 1) { -+ dev_err(smem->dev, -+ "Unsupported partition header version %d\n", -+ ptable->version); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < ptable->num_entries; i++) { -+ entry = &ptable->entry[i]; -+ -+ if (entry->host0 != local_host && entry->host1 != local_host) -+ continue; -+ -+ if (!entry->offset) -+ continue; -+ -+ if (!entry->size) -+ continue; -+ -+ if (entry->host0 == local_host) -+ remote_host = entry->host1; -+ else -+ remote_host = entry->host0; -+ -+ if (smem->partitions[remote_host]) { -+ dev_err(smem->dev, -+ "Already found a partition for host %d\n", -+ remote_host); -+ return -EINVAL; -+ } -+ -+ header = smem->regions[0].virt_base + entry->offset; -+ -+ if (header->magic != SMEM_PART_MAGIC) { -+ dev_err(smem->dev, -+ "Partition %d has invalid magic\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->host0 != local_host && header->host1 != local_host) { -+ dev_err(smem->dev, -+ "Partition %d hosts are invalid\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->host0 != remote_host && header->host1 != remote_host) { -+ dev_err(smem->dev, -+ "Partition %d hosts are invalid\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->size != entry->size) { -+ dev_err(smem->dev, -+ "Partition %d has invalid size\n", i); -+ return -EINVAL; -+ } -+ -+ if (header->offset_free_uncached > header->size) { -+ dev_err(smem->dev, -+ "Partition %d has invalid free pointer\n", i); -+ return -EINVAL; -+ } -+ -+ smem->partitions[remote_host] = header; -+ } -+ -+ return 0; -+} -+ -+static int qcom_smem_count_mem_regions(struct platform_device *pdev) -+{ -+ struct resource *res; -+ int num_regions = 0; -+ int i; -+ -+ for (i = 0; i < pdev->num_resources; i++) { -+ res = &pdev->resource[i]; -+ -+ if (resource_type(res) == IORESOURCE_MEM) -+ num_regions++; -+ } -+ -+ return num_regions; -+} -+ -+static int qcom_smem_probe(struct platform_device *pdev) -+{ -+ struct smem_header *header; -+ struct device_node *np; -+ struct qcom_smem *smem; -+ struct resource *res; -+ struct resource r; -+ size_t array_size; -+ int num_regions = 0; -+ int hwlock_id; -+ u32 version; -+ int ret; -+ int i; -+ -+ num_regions = qcom_smem_count_mem_regions(pdev) + 1; -+ -+ array_size = num_regions * sizeof(struct smem_region); -+ smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL); -+ if (!smem) -+ return -ENOMEM; -+ -+ smem->dev = &pdev->dev; -+ smem->num_regions = num_regions; -+ -+ np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); -+ if (!np) { -+ dev_err(&pdev->dev, "No memory-region specified\n"); -+ return -EINVAL; -+ } -+ -+ ret = of_address_to_resource(np, 0, &r); -+ of_node_put(np); -+ if (ret) -+ return ret; -+ -+ smem->regions[0].aux_base = (u32)r.start; -+ smem->regions[0].size = resource_size(&r); -+ smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev, -+ r.start, -+ resource_size(&r)); -+ if (!smem->regions[0].virt_base) -+ return -ENOMEM; -+ -+ for (i = 1; i < num_regions; i++) { -+ res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1); -+ -+ smem->regions[i].aux_base = (u32)res->start; -+ smem->regions[i].size = resource_size(res); -+ smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev, -+ res->start, -+ resource_size(res)); -+ if (!smem->regions[i].virt_base) -+ return -ENOMEM; -+ } -+ -+ header = smem->regions[0].virt_base; -+ if (header->initialized != 1 || header->reserved) { -+ dev_err(&pdev->dev, "SMEM is not initilized by SBL\n"); -+ return -EINVAL; -+ } -+ -+ version = qcom_smem_get_sbl_version(smem); -+ if (version >> 16 != SMEM_EXPECTED_VERSION) { -+ dev_err(&pdev->dev, "Unsupported smem version 0x%x\n", version); -+ return -EINVAL; -+ } -+ -+ ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); -+ if (ret < 0) -+ return ret; -+ -+ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); -+ if (hwlock_id < 0) { -+ dev_err(&pdev->dev, "failed to retrieve hwlock\n"); -+ return hwlock_id; -+ } -+ -+ smem->hwlock = hwspin_lock_request_specific(hwlock_id); -+ if (!smem->hwlock) -+ return -ENXIO; -+ -+ __smem = smem; -+ -+ return 0; -+} -+ -+static int qcom_smem_remove(struct platform_device *pdev) -+{ -+ hwspin_lock_free(__smem->hwlock); -+ __smem = NULL; -+ -+ return 0; -+} -+ -+static const struct of_device_id qcom_smem_of_match[] = { -+ { .compatible = "qcom,smem" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, qcom_smem_of_match); -+ -+static struct platform_driver qcom_smem_driver = { -+ .probe = qcom_smem_probe, -+ .remove = qcom_smem_remove, -+ .driver = { -+ .name = "qcom_smem", -+ .of_match_table = qcom_smem_of_match, -+ .suppress_bind_attrs = true, -+ }, -+}; -+ -+static int __init qcom_smem_init(void) -+{ -+ return platform_driver_register(&qcom_smem_driver); -+} -+arch_initcall(qcom_smem_init); -+ -+static void __exit qcom_smem_exit(void) -+{ -+ platform_driver_unregister(&qcom_smem_driver); -+} -+module_exit(qcom_smem_exit) -+ -+MODULE_AUTHOR("Bjorn Andersson "); -+MODULE_DESCRIPTION("Qualcomm Shared Memory Manager"); -+MODULE_LICENSE("GPLv2"); ---- /dev/null -+++ b/include/linux/soc/qcom/smem.h -@@ -0,0 +1,14 @@ -+#ifndef __QCOM_SMEM_H__ -+#define __QCOM_SMEM_H__ -+ -+struct device_node; -+struct qcom_smem; -+ -+#define QCOM_SMEM_HOST_ANY -1 -+ -+int qcom_smem_alloc(unsigned host, unsigned item, size_t size); -+int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size); -+ -+int qcom_smem_get_free_space(unsigned host); -+ -+#endif diff --git a/target/linux/ipq806x/patches-4.1/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.1/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch deleted file mode 100644 index b55f17475e..0000000000 --- a/target/linux/ipq806x/patches-4.1/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch +++ /dev/null @@ -1,36 +0,0 @@ -From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Fri, 14 Aug 2015 11:17:20 -0700 -Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts - -SMEM is used on IPQ806x to store various board related information such -as boot device and flash partition layout. We'll declare it as a device -so we can make use of it thanks to the new SMEM soc driver. - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -55,7 +55,7 @@ - no-map; - }; - -- smem@41000000 { -+ smem: smem@41000000 { - reg = <0x41000000 0x200000>; - no-map; - }; -@@ -341,4 +341,10 @@ - - #hwlock-cells = <1>; - }; -+ -+ smem { -+ compatible = "qcom,smem"; -+ memory-region = <&smem>; -+ hwlocks = <&sfpb_mutex 3>; -+ }; - }; diff --git a/target/linux/ipq806x/patches-4.1/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.1/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch deleted file mode 100644 index 5fc54134de..0000000000 --- a/target/linux/ipq806x/patches-4.1/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch +++ /dev/null @@ -1,277 +0,0 @@ -From 0501f76b138cf1dc11a313bb7a094da524b79337 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 13 Aug 2015 09:53:14 -0700 -Subject: [PATCH 3/3] 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 ---- - drivers/mtd/Kconfig | 7 ++ - drivers/mtd/Makefile | 1 + - drivers/mtd/qcom_smem_part.c | 231 +++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 239 insertions(+) - create mode 100644 drivers/mtd/qcom_smem_part.c - ---- a/drivers/mtd/Kconfig -+++ b/drivers/mtd/Kconfig -@@ -195,6 +195,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,231 @@ -+/* -+ * 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 -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+/* 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) -+{ -+ int ret; -+ size_t size; -+ -+ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, -+ (void **) smem_blksz, &size); -+ -+ if (ret < 0) { -+ 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) -+{ -+ int ret; -+ size_t size; -+ -+ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, -+ (void **) smem_flash_type, &size); -+ -+ if (ret < 0) { -+ 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) -+{ -+ int ret; -+ size_t size; -+ -+ ret = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, -+ (void **) pparts, &size); -+ -+ if (ret < 0) { -+ 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, -+ 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 = data->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 struct mtd_part_parser qcom_smem_parser = { -+ .owner = THIS_MODULE, -+ .parse_fn = parse_qcom_smem_partitions, -+ .name = "qcom-smem", -+}; -+ -+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 "); -+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); ---- a/drivers/mtd/Makefile -+++ b/drivers/mtd/Makefile -@@ -16,6 +16,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 - - # 'Users' - code which presents functionality to userspace. - obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o diff --git a/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch deleted file mode 100644 index 41f91fae7c..0000000000 --- a/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch +++ /dev/null @@ -1,263 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings -From: Stanimir Varbanov -X-Patchwork-Id: 6326181 -Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com> -To: Rob Herring , Kumar Gala , - Mark Rutland , - Grant Likely , - Bjorn Helgaas , - Kishon Vijay Abraham I , - Russell King , Arnd Bergmann -Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, - linux-pci@vger.kernel.org, Mathieu Olivari , - Srinivas Kandagatla , - Stanimir Varbanov -Date: Mon, 4 May 2015 15:42:16 +0300 - -Document Qualcomm PCIe driver devicetree bindings. - -Signed-off-by: Stanimir Varbanov - ---- -.../devicetree/bindings/pci/qcom,pcie.txt | 231 ++++++++++++++++++++ - 1 files changed, 231 insertions(+), 0 deletions(-) - create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt -@@ -0,0 +1,231 @@ -+* Qualcomm PCI express root complex -+ -+- compatible: -+ Usage: required -+ Value type: -+ Definition: Value shall include -+ - "qcom,pcie-v0" for apq/ipq8064 -+ - "qcom,pcie-v1" for apq8084 -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: Register ranges as listed in the reg-names property -+ -+- reg-names: -+ Usage: required -+ Value type: -+ Definition: Must include the following entries -+ - "parf" Qualcomm specific registers -+ - "dbi" Designware PCIe registers -+ - "elbi" External local bus interface registers -+ - "config" PCIe configuration space -+ -+- device_type: -+ Usage: required -+ Value type: -+ Definition: Should be "pci". As specified in designware-pcie.txt -+ -+- #address-cells: -+ Usage: required -+ Value type: -+ Definition: Should be set to 3. As specified in designware-pcie.txt -+ -+- #size-cells: -+ Usage: required -+ Value type: -+ Definition: Should be set 2. As specified in designware-pcie.txt -+ -+- ranges: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- interrupts: -+ Usage: required -+ Value type: -+ Definition: MSI interrupt -+ -+- interrupt-names: -+ Usage: required -+ Value type: -+ Definition: Should contain "msi" -+ -+- #interrupt-cells: -+ Usage: required -+ Value type: -+ Definition: Should be 1. As specified in designware-pcie.txt -+ -+- interrupt-map-mask: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- interrupt-map: -+ Usage: required -+ Value type: -+ Definition: As specified in designware-pcie.txt -+ -+- clocks: -+ Usage: required -+ Value type: -+ Definition: List of phandle and clock specifier pairs as listed -+ in clock-names property -+ -+- clock-names: -+ Usage: required -+ Value type: -+ Definition: Should contain the following entries -+ * should be populated for v0 and v1 -+ - "iface" Configuration AHB clock -+ -+ * should be populated for v0 -+ - "core" Clocks the pcie hw block -+ - "phy" Clocks the pcie PHY block -+ -+ * should be populated for v1 -+ - "aux" Auxiliary (AUX) clock -+ - "bus_master" Master AXI clock -+ - "bus_slave" Slave AXI clock -+ -+- resets: -+ Usage: required -+ Value type: -+ Definition: List of phandle and reset specifier pairs as listed -+ in reset-names property -+ -+- reset-names: -+ Usage: required -+ Value type: -+ Definition: Should contain the following entries -+ * should be populated for v0 -+ - "axi" AXI reset -+ - "ahb" AHB reset -+ - "por" POR reset -+ - "pci" PCI reset -+ - "phy" PHY reset -+ -+ * should be populated for v1 -+ - "core" Core reset -+ -+- power-domains: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: A phandle and power domain specifier pair to the -+ power domain which is responsible for collapsing -+ and restoring power to the peripheral -+ -+- -supply: -+ Usage: required -+ Value type: -+ Definition: List of phandles to the power supply regulator(s) -+ * should be populated for v0 and v1 -+ - "vdda" core analog power supply -+ -+ * should be populated for v0 -+ - "vdda_phy" analog power supply for PHY -+ - "vdda_refclk" analog power supply for IC which generate -+ reference clock -+ -+- phys: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: List of phandle(s) as listed in phy-names property -+ -+- phy-names: -+ Usage: required (for v1 only) -+ Value type: -+ Definition: Should contain "pciephy" -+ -+- -gpio: -+ Usage: optional -+ Value type: -+ Definition: List of phandle and gpio specifier pairs. Should contain -+ - "perst" PCIe endpoint reset signal line -+ - "pewake" PCIe endpoint wake signal line -+ -+- pinctrl-0: -+ Usage: required -+ Value type: -+ Definition: List of phandles pointing at a pin(s) configuration -+ -+- pinctrl-names -+ Usage: required -+ Value type: -+ Definition: List of names of pinctrl-0 state -+ -+* Example for v0 -+ pcie0: pci@1b500000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b500000 0x1000 -+ 0x1b502000 0x80 -+ 0x1b600000 0x100 -+ 0x0ff00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */ -+ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ clocks = <&gcc PCIE_A_CLK>, -+ <&gcc PCIE_H_CLK>, -+ <&gcc PCIE_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ resets = <&gcc PCIE_ACLK_RESET>, -+ <&gcc PCIE_HCLK_RESET>, -+ <&gcc PCIE_POR_RESET>, -+ <&gcc PCIE_PCI_RESET>, -+ <&gcc PCIE_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ }; -+ -+* Example for v1 -+ pcie0@fc520000 { -+ compatible = "qcom,pcie-v1"; -+ reg = <0xfc520000 0x2000>, -+ <0xff000000 0x1000>, -+ <0xff001000 0x1000>, -+ <0xff002000 0x2000>; -+ reg-names = "parf", "dbi", "elbi", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */ -+ 0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>, -+ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, -+ <&gcc GCC_PCIE_0_SLV_AXI_CLK>, -+ <&gcc GCC_PCIE_0_AUX_CLK>; -+ clock-names = "iface", "master_bus", "slave_bus", "aux"; -+ resets = <&gcc GCC_PCIE_0_BCR>; -+ reset-names = "core"; -+ power-domains = <&gcc PCIE0_GDSC>; -+ vdda-supply = <&pma8084_l3>; -+ phys = <&pciephy0>; -+ phy-names = "pciephy"; -+ perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>; -+ pinctrl-0 = <&pcie0_pins_default>; -+ pinctrl-names = "default"; -+ }; diff --git a/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch deleted file mode 100644 index dbf16a303a..0000000000 --- a/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch +++ /dev/null @@ -1,753 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver -From: Stanimir Varbanov -X-Patchwork-Id: 6326161 -Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com> -To: Rob Herring , Kumar Gala , - Mark Rutland , - Grant Likely , - Bjorn Helgaas , - Kishon Vijay Abraham I , - Russell King , Arnd Bergmann -Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, - linux-pci@vger.kernel.org, Mathieu Olivari , - Srinivas Kandagatla , - Stanimir Varbanov -Date: Mon, 4 May 2015 15:42:17 +0300 - -The PCIe driver reuse the Designware common code for host -and MSI initialization, and also program the Qualcomm -application specific registers. - -Signed-off-by: Stanimir Varbanov - ---- -MAINTAINERS | 7 + - drivers/pci/host/Kconfig | 9 + - drivers/pci/host/Makefile | 1 + - drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 694 insertions(+), 0 deletions(-) - create mode 100644 drivers/pci/host/pcie-qcom.c - ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -7599,6 +7599,13 @@ L: linux-pci@vger.kernel.org - S: Maintained - F: drivers/pci/host/*spear* - -+PCIE DRIVER FOR QUALCOMM MSM -+M: Stanimir Varbanov -+L: linux-pci@vger.kernel.org -+L: linux-arm-msm@vger.kernel.org -+S: Maintained -+F: drivers/pci/host/*qcom* -+ - PCMCIA SUBSYSTEM - P: Linux PCMCIA Team - L: linux-pcmcia@lists.infradead.org ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig -@@ -125,4 +125,13 @@ config PCIE_IPROC_PLATFORM - Say Y here if you want to use the Broadcom iProc PCIe controller - through the generic platform bus interface - -+config PCIE_QCOM -+ bool "Qualcomm PCIe controller" -+ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST) -+ select PCIE_DW -+ select PCIEPORTBUS -+ help -+ Say Y here to enable PCIe controller support on Qualcomm SoCs. The -+ PCIe controller use Designware core plus Qualcomm specific hardware -+ wrappers. - endmenu ---- /dev/null -+++ b/drivers/pci/host/pcie-qcom.c -@@ -0,0 +1,677 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "pcie-designware.h" -+ -+#define PCIE20_PARF_PHY_CTRL 0x40 -+#define PCIE20_PARF_PHY_REFCLK 0x4C -+#define PCIE20_PARF_DBI_BASE_ADDR 0x168 -+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c -+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -+ -+#define PCIE20_ELBI_SYS_CTRL 0x04 -+#define PCIE20_ELBI_SYS_STTS 0x08 -+#define XMLH_LINK_UP BIT(10) -+ -+#define PCIE20_CAP 0x70 -+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -+ -+#define PERST_DELAY_MIN_US 1000 -+#define PERST_DELAY_MAX_US 1005 -+ -+#define LINKUP_DELAY_MIN_US 5000 -+#define LINKUP_DELAY_MAX_US 5100 -+#define LINKUP_RETRIES_COUNT 20 -+ -+#define PCIE_V0 0 /* apq8064 */ -+#define PCIE_V1 1 /* apq8084 */ -+ -+struct qcom_pcie_resources_v0 { -+ struct clk *iface_clk; -+ struct clk *core_clk; -+ struct clk *phy_clk; -+ struct reset_control *pci_reset; -+ struct reset_control *axi_reset; -+ struct reset_control *ahb_reset; -+ struct reset_control *por_reset; -+ struct reset_control *phy_reset; -+ struct regulator *vdda; -+ struct regulator *vdda_phy; -+ struct regulator *vdda_refclk; -+}; -+ -+struct qcom_pcie_resources_v1 { -+ struct clk *iface; -+ struct clk *aux; -+ struct clk *master_bus; -+ struct clk *slave_bus; -+ struct reset_control *core; -+ struct regulator *vdda; -+}; -+ -+union pcie_resources { -+ struct qcom_pcie_resources_v0 v0; -+ struct qcom_pcie_resources_v1 v1; -+}; -+ -+struct qcom_pcie { -+ struct pcie_port pp; -+ struct device *dev; -+ union pcie_resources res; -+ void __iomem *parf; -+ void __iomem *dbi; -+ void __iomem *elbi; -+ struct phy *phy; -+ struct gpio_desc *reset; -+ unsigned int version; -+}; -+ -+#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) -+ -+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_deassert(struct qcom_pcie *pcie, int assert) -+{ -+ int val, active_low; -+ -+ if (IS_ERR_OR_NULL(pcie->reset)) -+ return; -+ -+ active_low = gpiod_is_active_low(pcie->reset); -+ -+ if (assert) -+ val = !!active_low; -+ else -+ val = !active_low; -+ -+ gpiod_set_value(pcie->reset, val); -+ -+ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); -+} -+ -+static void qcom_ep_reset_assert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 1); -+} -+ -+static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 0); -+} -+ -+static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) -+{ -+ struct pcie_port *pp = arg; -+ -+ return dw_handle_msi_irq(pp); -+} -+ -+static int qcom_pcie_link_up(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS); -+ -+ return val & BIT(29) ? 1 : 0; -+} -+ -+static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ -+ reset_control_assert(res->pci_reset); -+ reset_control_assert(res->axi_reset); -+ reset_control_assert(res->ahb_reset); -+ reset_control_assert(res->por_reset); -+ reset_control_assert(res->pci_reset); -+ clk_disable_unprepare(res->iface_clk); -+ clk_disable_unprepare(res->core_clk); -+ clk_disable_unprepare(res->phy_clk); -+ regulator_disable(res->vdda); -+ regulator_disable(res->vdda_phy); -+ regulator_disable(res->vdda_refclk); -+} -+ -+static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ -+ reset_control_assert(res->core); -+ clk_disable_unprepare(res->slave_bus); -+ clk_disable_unprepare(res->master_bus); -+ clk_disable_unprepare(res->iface); -+ clk_disable_unprepare(res->aux); -+ regulator_disable(res->vdda); -+} -+ -+static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ return ret; -+ } -+ -+ ret = regulator_enable(res->vdda_refclk); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_refclk regulator\n"); -+ goto err_refclk; -+ } -+ -+ ret = regulator_enable(res->vdda_phy); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_phy regulator\n"); -+ goto err_vdda_phy; -+ } -+ -+ ret = clk_prepare_enable(res->iface_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_iface; -+ } -+ -+ 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 = reset_control_deassert(res->ahb_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert ahb reset\n"); -+ goto err_reset_ahb; -+ } -+ -+ return 0; -+ -+err_reset_ahb: -+ 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_iface: -+ regulator_disable(res->vdda_phy); -+err_vdda_phy: -+ regulator_disable(res->vdda_refclk); -+err_refclk: -+ regulator_disable(res->vdda); -+ return ret; -+} -+ -+static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = reset_control_deassert(res->core); -+ if (ret) { -+ dev_err(dev, "cannot deassert core reset\n"); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(res->aux); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable aux clock\n"); -+ goto err_res; -+ } -+ -+ ret = clk_prepare_enable(res->iface); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_aux; -+ } -+ -+ ret = clk_prepare_enable(res->master_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable master_bus clock\n"); -+ goto err_iface; -+ } -+ -+ ret = clk_prepare_enable(res->slave_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable slave_bus clock\n"); -+ goto err_master; -+ } -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ goto err_slave; -+ } -+ -+ return 0; -+ -+err_slave: -+ clk_disable_unprepare(res->slave_bus); -+err_master: -+ clk_disable_unprepare(res->master_bus); -+err_iface: -+ clk_disable_unprepare(res->iface); -+err_aux: -+ clk_disable_unprepare(res->aux); -+err_res: -+ reset_control_assert(res->core); -+ -+ return ret; -+} -+ -+static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); -+ if (IS_ERR(res->vdda_phy)) -+ return PTR_ERR(res->vdda_phy); -+ -+ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); -+ if (IS_ERR(res->vdda_refclk)) -+ return PTR_ERR(res->vdda_refclk); -+ -+ res->iface_clk = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface_clk)) -+ return PTR_ERR(res->iface_clk); -+ -+ res->core_clk = devm_clk_get(dev, "core"); -+ if (IS_ERR(res->core_clk)) -+ return PTR_ERR(res->core_clk); -+ -+ res->phy_clk = devm_clk_get(dev, "phy"); -+ if (IS_ERR(res->phy_clk)) -+ return PTR_ERR(res->phy_clk); -+ -+ res->pci_reset = devm_reset_control_get(dev, "pci"); -+ if (IS_ERR(res->pci_reset)) -+ return PTR_ERR(res->pci_reset); -+ -+ res->axi_reset = devm_reset_control_get(dev, "axi"); -+ if (IS_ERR(res->axi_reset)) -+ return PTR_ERR(res->axi_reset); -+ -+ res->ahb_reset = devm_reset_control_get(dev, "ahb"); -+ if (IS_ERR(res->ahb_reset)) -+ return PTR_ERR(res->ahb_reset); -+ -+ res->por_reset = devm_reset_control_get(dev, "por"); -+ if (IS_ERR(res->por_reset)) -+ return PTR_ERR(res->por_reset); -+ -+ res->phy_reset = devm_reset_control_get(dev, "phy"); -+ if (IS_ERR(res->phy_reset)) -+ return PTR_ERR(res->phy_reset); -+ -+ return 0; -+} -+ -+static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->iface = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface)) -+ return PTR_ERR(res->iface); -+ -+ res->aux = devm_clk_get(dev, "aux"); -+ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ else if (IS_ERR(res->aux)) -+ res->aux = NULL; -+ -+ res->master_bus = devm_clk_get(dev, "master_bus"); -+ if (IS_ERR(res->master_bus)) -+ return PTR_ERR(res->master_bus); -+ -+ res->slave_bus = devm_clk_get(dev, "slave_bus"); -+ if (IS_ERR(res->slave_bus)) -+ return PTR_ERR(res->slave_bus); -+ -+ res->core = devm_reset_control_get(dev, "core"); -+ if (IS_ERR(res->core)) -+ return PTR_ERR(res->core); -+ -+ return 0; -+} -+ -+static int qcom_pcie_enable_link_training(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct device *dev = pp->dev; -+ int retries; -+ u32 val; -+ -+ /* enable link training */ -+ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); -+ -+ /* wait for up to 100ms for the link to come up */ -+ retries = LINKUP_RETRIES_COUNT; -+ do { -+ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS); -+ if (val & XMLH_LINK_UP) -+ break; -+ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US); -+ } while (retries--); -+ -+ if (retries < 0 || !dw_pcie_link_up(pp)) { -+ dev_err(dev, "link initialization failed\n"); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+static void qcom_pcie_host_init_v1(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v1(pcie); -+ if (ret) -+ return; -+ -+ /* change DBI base address */ -+ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, -+ 0, BIT(31)); -+ -+ ret = phy_init(pcie->phy); -+ if (ret) -+ goto err_res; -+ -+ ret = phy_power_on(pcie->phy); -+ if (ret) -+ goto err_phy; -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+ -+err: -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+err_phy: -+ phy_exit(pcie->phy); -+err_res: -+ qcom_pcie_disable_resources_v1(pcie); -+} -+ -+static void qcom_pcie_host_init_v0(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v0(pcie); -+ if (ret) -+ return; -+ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); -+ -+ /* enable external reference clock */ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); -+ -+ ret = reset_control_deassert(res->phy_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert phy reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->pci_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert pci reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->por_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert por reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->axi_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert axi reset\n"); -+ return; -+ } -+ -+ /* wait 150ms for clock acquisition */ -+ usleep_range(10000, 15000); -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+err: -+ qcom_ep_reset_assert(pcie); -+ qcom_pcie_disable_resources_v0(pcie); -+} -+ -+static void qcom_pcie_host_init(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ -+ if (pcie->version == PCIE_V0) -+ return qcom_pcie_host_init_v0(pp); -+ else -+ return qcom_pcie_host_init_v1(pp); -+} -+ -+static int -+qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) -+{ -+ /* the device class is not reported correctly from the register */ -+ if (where == PCI_CLASS_REVISION && size == 4) { -+ *val = readl(pp->dbi_base + PCI_CLASS_REVISION); -+ *val &= ~(0xffff << 16); -+ *val |= PCI_CLASS_BRIDGE_PCI << 16; -+ return PCIBIOS_SUCCESSFUL; -+ } -+ -+ return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, -+ size, val); -+} -+ -+static struct pcie_host_ops qcom_pcie_ops = { -+ .link_up = qcom_pcie_link_up, -+ .host_init = qcom_pcie_host_init, -+ .rd_own_conf = qcom_pcie_rd_own_conf, -+}; -+ -+static const struct of_device_id qcom_pcie_match[] = { -+ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 }, -+ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 }, -+ { } -+}; -+ -+static int qcom_pcie_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct of_device_id *match; -+ struct resource *res; -+ struct qcom_pcie *pcie; -+ struct pcie_port *pp; -+ int ret; -+ -+ match = of_match_node(qcom_pcie_match, dev->of_node); -+ if (!match) -+ return -ENXIO; -+ -+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); -+ if (!pcie) -+ return -ENOMEM; -+ -+ pcie->version = (unsigned int)match->data; -+ -+ pcie->reset = devm_gpiod_get_optional(dev, "perst"); -+ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER) -+ return PTR_ERR(pcie->reset); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); -+ pcie->parf = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->parf)) -+ return PTR_ERR(pcie->parf); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -+ pcie->dbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->dbi)) -+ return PTR_ERR(pcie->dbi); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); -+ pcie->elbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->elbi)) -+ return PTR_ERR(pcie->elbi); -+ -+ pcie->phy = devm_phy_optional_get(dev, "pciephy"); -+ if (IS_ERR(pcie->phy)) -+ return PTR_ERR(pcie->phy); -+ -+ pcie->dev = dev; -+ -+ if (pcie->version == PCIE_V0) -+ ret = qcom_pcie_get_resources_v0(pcie); -+ else -+ ret = qcom_pcie_get_resources_v1(pcie); -+ -+ if (ret) -+ return ret; -+ -+ pp = &pcie->pp; -+ pp->dev = dev; -+ pp->dbi_base = pcie->dbi; -+ pp->root_bus_nr = -1; -+ pp->ops = &qcom_pcie_ops; -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) { -+ pp->msi_irq = platform_get_irq_byname(pdev, "msi"); -+ if (pp->msi_irq < 0) { -+ dev_err(dev, "cannot get msi irq\n"); -+ return pp->msi_irq; -+ } -+ -+ ret = devm_request_irq(dev, pp->msi_irq, -+ qcom_pcie_msi_irq_handler, -+ IRQF_SHARED, "qcom-pcie-msi", pp); -+ if (ret) { -+ dev_err(dev, "cannot request msi irq\n"); -+ return ret; -+ } -+ } -+ -+ ret = dw_pcie_host_init(pp); -+ if (ret) { -+ dev_err(dev, "cannot initialize host\n"); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, pcie); -+ -+ return 0; -+} -+ -+static int qcom_pcie_remove(struct platform_device *pdev) -+{ -+ struct qcom_pcie *pcie = platform_get_drvdata(pdev); -+ -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+ phy_exit(pcie->phy); -+ if (pcie->version == PCIE_V0) -+ qcom_pcie_disable_resources_v0(pcie); -+ else -+ qcom_pcie_disable_resources_v1(pcie); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_pcie_driver = { -+ .probe = qcom_pcie_probe, -+ .remove = qcom_pcie_remove, -+ .driver = { -+ .name = "qcom-pcie", -+ .of_match_table = qcom_pcie_match, -+ }, -+}; -+ -+module_platform_driver(qcom_pcie_driver); -+ -+MODULE_AUTHOR("Stanimir Varbanov "); -+MODULE_DESCRIPTION("Qualcomm PCIe root complex driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-pcie"); ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -15,3 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-laye - obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o - obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o - obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o -+obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o diff --git a/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch deleted file mode 100644 index 6ccb7d8080..0000000000 --- a/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch +++ /dev/null @@ -1,244 +0,0 @@ -From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 21 Apr 2015 19:01:42 -0700 -Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms - -qcom-pcie driver now supports version 0 of the controller. This change -adds the corresponding entries to the IPQ806x dtsi file and -corresponding platform (AP148). - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 30 ++++++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 124 +++++++++++++++++++++++++++++++ - 2 files changed, 154 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -91,5 +91,15 @@ - sata@29000000 { - status = "ok"; - }; -+ -+ pcie0: pci@1b500000 { -+ status = "ok"; -+ phy-tx0-term-offset = <7>; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ phy-tx0-term-offset = <7>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -128,5 +128,17 @@ - usb30@1 { - status = "ok"; - }; -+ -+ pcie0: pci@1b500000 { -+ status = "ok"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ }; -+ -+ pcie2: pci@1b900000 { -+ status = "ok"; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -4,6 +4,9 @@ - #include - #include - #include -+#include -+#include -+#include - - / { - model = "Qualcomm IPQ8064"; -@@ -99,6 +102,33 @@ - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <0 16 0x4>; -+ -+ pcie0_pins: pcie0_pinmux { -+ mux { -+ pins = "gpio3"; -+ function = "pcie1_rst"; -+ drive-strength = <12>; -+ bias-disable; -+ }; -+ }; -+ -+ pcie1_pins: pcie1_pinmux { -+ mux { -+ pins = "gpio48"; -+ function = "pcie2_rst"; -+ drive-strength = <12>; -+ bias-disable; -+ }; -+ }; -+ -+ pcie2_pins: pcie2_pinmux { -+ mux { -+ pins = "gpio63"; -+ function = "pcie3_rst"; -+ drive-strength = <12>; -+ bias-disable; -+ }; -+ }; - }; - - intc: interrupt-controller@2000000 { -@@ -333,6 +363,144 @@ - compatible = "syscon"; - reg = <0x01200600 0x100>; - }; -+ -+ pcie0: pci@1b500000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b500000 0x1000 -+ 0x1b502000 0x80 -+ 0x1b600000 0x100 -+ 0x0ff00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x0fe00000 0x0fe00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x08000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_A_CLK>, -+ <&gcc PCIE_H_CLK>, -+ <&gcc PCIE_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_ACLK_RESET>, -+ <&gcc PCIE_HCLK_RESET>, -+ <&gcc PCIE_POR_RESET>, -+ <&gcc PCIE_PCI_RESET>, -+ <&gcc PCIE_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie0_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b700000 0x1000 -+ 0x1b702000 0x80 -+ 0x1b800000 0x100 -+ 0x31f00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <1>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x31e00000 0x31e00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x2e000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_1_A_CLK>, -+ <&gcc PCIE_1_H_CLK>, -+ <&gcc PCIE_1_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_1_ACLK_RESET>, -+ <&gcc PCIE_1_HCLK_RESET>, -+ <&gcc PCIE_1_POR_RESET>, -+ <&gcc PCIE_1_PCI_RESET>, -+ <&gcc PCIE_1_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie1_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; -+ -+ pcie2: pci@1b900000 { -+ compatible = "qcom,pcie-v0"; -+ reg = <0x1b900000 0x1000 -+ 0x1b902000 0x80 -+ 0x1ba00000 0x100 -+ 0x35f00000 0x100000>; -+ reg-names = "dbi", "elbi", "parf", "config"; -+ device_type = "pci"; -+ linux,pci-domain = <2>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ ranges = <0x81000000 0 0x35e00000 0x35e00000 0 0x00100000 /* downstream I/O */ -+ 0x82000000 0 0x32000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */ -+ -+ interrupts = ; -+ interrupt-names = "msi"; -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ -+ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ -+ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ -+ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ -+ -+ clocks = <&gcc PCIE_2_A_CLK>, -+ <&gcc PCIE_2_H_CLK>, -+ <&gcc PCIE_2_PHY_CLK>; -+ clock-names = "core", "iface", "phy"; -+ -+ resets = <&gcc PCIE_2_ACLK_RESET>, -+ <&gcc PCIE_2_HCLK_RESET>, -+ <&gcc PCIE_2_POR_RESET>, -+ <&gcc PCIE_2_PCI_RESET>, -+ <&gcc PCIE_2_PHY_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy"; -+ -+ pinctrl-0 = <&pcie2_pins>; -+ pinctrl-names = "default"; -+ -+ perst-gpio = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch deleted file mode 100644 index e2d31354ed..0000000000 --- a/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch +++ /dev/null @@ -1,29 +0,0 @@ -From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Tue, 21 Apr 2015 19:09:07 -0700 -Subject: [PATCH 9/9] 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 ---- - arch/arm/mach-qcom/Kconfig | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/arm/mach-qcom/Kconfig -+++ b/arch/arm/mach-qcom/Kconfig -@@ -5,6 +5,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. - diff --git a/target/linux/ipq806x/patches-4.1/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-4.1/114-pcie-add-ctlr-init.patch deleted file mode 100644 index c00abca753..0000000000 --- a/target/linux/ipq806x/patches-4.1/114-pcie-add-ctlr-init.patch +++ /dev/null @@ -1,311 +0,0 @@ ---- a/drivers/pci/host/pcie-qcom.c -+++ b/drivers/pci/host/pcie-qcom.c -@@ -29,8 +29,53 @@ - - #include "pcie-designware.h" - -+/* DBI registers */ -+#define PCIE20_CAP 0x70 -+#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 -+ -+/* 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_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_CONFIG_BITS 0x50 -+#define PHY_RX0_EQ(x) (x << 24) -+ - #define PCIE20_PARF_DBI_BASE_ADDR 0x168 - #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c - #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -@@ -39,9 +84,6 @@ - #define PCIE20_ELBI_SYS_STTS 0x08 - #define XMLH_LINK_UP BIT(10) - --#define PCIE20_CAP 0x70 --#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -- - #define PERST_DELAY_MIN_US 1000 - #define PERST_DELAY_MAX_US 1005 - -@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 { - 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; - 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; -+ uint8_t phy_tx0_term_offset; - }; - - struct qcom_pcie_resources_v1 { -@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl - - static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) - { -- int val, active_low; -- - if (IS_ERR_OR_NULL(pcie->reset)) - return; - -- active_low = gpiod_is_active_low(pcie->reset); -- -- if (assert) -- val = !!active_low; -- else -- val = !active_low; -- -- gpiod_set_value(pcie->reset, val); -- -+ gpiod_set_value(pcie->reset, assert); - usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); - } - -@@ -156,10 +192,13 @@ static void qcom_pcie_disable_resources_ - reset_control_assert(res->axi_reset); - reset_control_assert(res->ahb_reset); - reset_control_assert(res->por_reset); -- reset_control_assert(res->pci_reset); -+ reset_control_assert(res->phy_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); -+ 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); -@@ -201,6 +240,12 @@ static int qcom_pcie_enable_resources_v0 - goto err_vdda_phy; - } - -+ ret = reset_control_deassert(res->ext_reset); -+ if (ret) { -+ 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"); -@@ -219,21 +264,40 @@ static int qcom_pcie_enable_resources_v0 - goto err_clk_phy; - } - -+ ret = clk_prepare_enable(res->aux_clk); -+ if (ret) { -+ 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); - if (ret) { - dev_err(dev, "cannot deassert ahb reset\n"); - goto err_reset_ahb; - } -+ udelay(1); - - return 0; - - err_reset_ahb: -+ 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_iface: -+ reset_control_assert(res->ext_reset); -+err_reset_ext: - regulator_disable(res->vdda_phy); - err_vdda_phy: - regulator_disable(res->vdda_refclk); -@@ -329,6 +393,14 @@ static int qcom_pcie_get_resources_v0(st - 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(dev, "pci"); - if (IS_ERR(res->pci_reset)) - return PTR_ERR(res->pci_reset); -@@ -349,6 +421,14 @@ static int qcom_pcie_get_resources_v0(st - if (IS_ERR(res->phy_reset)) - return PTR_ERR(res->phy_reset); - -+ res->ext_reset = devm_reset_control_get(dev, "ext"); -+ 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; -+ - return 0; - } - -@@ -461,6 +541,57 @@ err_res: - qcom_pcie_disable_resources_v1(pcie); - } - -+static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev) -+{ -+ struct pcie_port *pp = &pcie->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->dbi + PCIE20_PLR_IATU_VIEWPORT); -+ -+ writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1); -+ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); -+ writel(pp->cfg0_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); -+ writel((pp->cfg0_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); -+ writel((pp->cfg0_mod_base + pp->cfg0_size - 1), -+ pcie->dbi + PCIE20_PLR_IATU_LAR); -+ writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR); -+ writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR); -+} -+ -+static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie) -+{ -+ struct pcie_port *pp = &pcie->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->dbi + PCIE20_PLR_IATU_VIEWPORT); -+ -+ writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1); -+ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); -+ writel(pp->mem_mod_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); -+ writel((pp->mem_mod_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); -+ writel(pp->mem_mod_base + pp->mem_size - 1, -+ pcie->dbi + PCIE20_PLR_IATU_LAR); -+ writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR); -+ writel(upper_32_bits(pp->mem_bus_addr), -+ pcie->dbi + PCIE20_PLR_IATU_UTAR); -+ -+ /* 256B PCIE buffer setting */ -+ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); -+ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); -+} -+ - static void qcom_pcie_host_init_v0(struct pcie_port *pp) - { - struct qcom_pcie *pcie = to_qcom_pcie(pp); -@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc - - qcom_ep_reset_assert(pcie); - -+ reset_control_assert(res->ahb_reset); -+ - ret = qcom_pcie_enable_resources_v0(pcie); - if (ret) - return; - - writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); - -- /* enable external reference clock */ -- writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); -+ /* 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); - -+ /* De-assert PHY, PCIe, POR and AXI resets */ - ret = reset_control_deassert(res->phy_reset); - if (ret) { - dev_err(dev, "cannot deassert phy reset\n"); -@@ -517,6 +667,9 @@ static void qcom_pcie_host_init_v0(struc - if (ret) - goto err; - -+ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); -+ qcom_pcie_prog_viewport_mem2_outbound(pcie); -+ - return; - err: - qcom_ep_reset_assert(pcie); diff --git a/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch deleted file mode 100644 index a99857e314..0000000000 --- a/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch +++ /dev/null @@ -1,80 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -392,15 +392,21 @@ - - clocks = <&gcc PCIE_A_CLK>, - <&gcc PCIE_H_CLK>, -- <&gcc PCIE_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_PHY_CLK>, -+ <&gcc PCIE_AUX_CLK>, -+ <&gcc PCIE_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_ACLK_RESET>, - <&gcc PCIE_HCLK_RESET>, - <&gcc PCIE_POR_RESET>, - <&gcc PCIE_PCI_RESET>, -- <&gcc PCIE_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_PHY_RESET>, -+ <&gcc PCIE_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie0_pins>; - pinctrl-names = "default"; -@@ -438,15 +444,21 @@ - - clocks = <&gcc PCIE_1_A_CLK>, - <&gcc PCIE_1_H_CLK>, -- <&gcc PCIE_1_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_1_PHY_CLK>, -+ <&gcc PCIE_1_AUX_CLK>, -+ <&gcc PCIE_1_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_1_ACLK_RESET>, - <&gcc PCIE_1_HCLK_RESET>, - <&gcc PCIE_1_POR_RESET>, - <&gcc PCIE_1_PCI_RESET>, -- <&gcc PCIE_1_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_1_PHY_RESET>, -+ <&gcc PCIE_1_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie1_pins>; - pinctrl-names = "default"; -@@ -484,15 +496,21 @@ - - clocks = <&gcc PCIE_2_A_CLK>, - <&gcc PCIE_2_H_CLK>, -- <&gcc PCIE_2_PHY_CLK>; -- clock-names = "core", "iface", "phy"; -+ <&gcc PCIE_2_PHY_CLK>, -+ <&gcc PCIE_2_AUX_CLK>, -+ <&gcc PCIE_2_ALT_REF_CLK>; -+ clock-names = "core", "iface", "phy", "aux", "ref"; -+ -+ assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>; -+ assigned-clock-rates = <100000000>; - - resets = <&gcc PCIE_2_ACLK_RESET>, - <&gcc PCIE_2_HCLK_RESET>, - <&gcc PCIE_2_POR_RESET>, - <&gcc PCIE_2_PCI_RESET>, -- <&gcc PCIE_2_PHY_RESET>; -- reset-names = "axi", "ahb", "por", "pci", "phy"; -+ <&gcc PCIE_2_PHY_RESET>, -+ <&gcc PCIE_2_EXT_RESET>; -+ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - - pinctrl-0 = <&pcie2_pins>; - pinctrl-names = "default"; diff --git a/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch deleted file mode 100644 index 7daa93163b..0000000000 --- a/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch +++ /dev/null @@ -1,87 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -2,6 +2,7 @@ - - #include "skeleton.dtsi" - #include -+#include - #include - #include - #include -@@ -93,6 +94,63 @@ - reg-names = "lpass-lpaif"; - }; - -+ rpm@108000 { -+ compatible = "qcom,rpm-ipq8064"; -+ reg = <0x108000 0x1000>; -+ qcom,ipc = <&l2cc 0x8 2>; -+ -+ interrupts = <0 19 0>, -+ <0 21 0>, -+ <0 22 0>; -+ interrupt-names = "ack", -+ "err", -+ "wakeup"; -+ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ smb208_s1a: smb208-s1a { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ -+ }; -+ -+ smb208_s1b: smb208-s1b { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = <1050000>; -+ regulator-max-microvolt = <1150000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ -+ smb208_s2a: smb208-s2a { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ -+ smb208_s2b: smb208-s2b { -+ compatible = "qcom,rpm-smb208"; -+ reg = ; -+ -+ regulator-min-microvolt = < 800000>; -+ regulator-max-microvolt = <1250000>; -+ -+ qcom,switch-mode-frequency = <1200000>; -+ }; -+ }; -+ - qcom_pinmux: pinmux@800000 { - compatible = "qcom,ipq8064-pinctrl"; - reg = <0x800000 0x4000>; -@@ -164,6 +222,12 @@ - reg = <0x02098000 0x1000>, <0x02008000 0x1000>; - }; - -+ l2cc: clock-controller@2011000 { -+ compatible = "qcom,kpss-gcc", "syscon"; -+ reg = <0x2011000 0x1000>; -+ clock-output-names = "acpu_l2_aux"; -+ }; -+ - saw0: regulator@2089000 { - compatible = "qcom,saw2"; - reg = <0x02089000 0x1000>, <0x02009000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch deleted file mode 100644 index 36a92c858a..0000000000 --- a/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch +++ /dev/null @@ -1,144 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions -From: Stephen Boyd -X-Patchwork-Id: 6063051 -Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , - Mark Rutland , Russell King , - Courtney Cavin -Date: Fri, 20 Mar 2015 23:45:20 -0700 - -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 -Cc: Russell King -Cc: Courtney Cavin -Signed-off-by: Stephen Boyd - ---- -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 -@@ -9,6 +9,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_ICST) += icst.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 -+#include -+ -+#include -+#include -+ -+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 diff --git a/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch deleted file mode 100644 index 3a475fba3d..0000000000 --- a/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch +++ /dev/null @@ -1,192 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,02/13] clk: mux: Split out register accessors for reuse -From: Stephen Boyd -X-Patchwork-Id: 6063111 -Message-Id: <1426920332-9340-3-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:21 -0700 - -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 - ---- -drivers/clk/clk-mux.c | 76 +++++++++++++++++++++++++++----------------- - include/linux/clk-provider.h | 9 ++++-- - 2 files changed, 54 insertions(+), 31 deletions(-) - ---- a/drivers/clk/clk-mux.c -+++ b/drivers/clk/clk-mux.c -@@ -29,35 +29,24 @@ - - #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) - --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_get_num_parents(hw->clk); -- 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) -@@ -65,24 +54,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); -+} - -- else { -- if (mux->flags & CLK_MUX_INDEX_BIT) -- index = 1 << index; -+unsigned int clk_mux_reindex(u8 index, unsigned int *table, -+ unsigned long flags) -+{ -+ unsigned int val = index; - -- if (mux->flags & CLK_MUX_INDEX_ONE) -- index++; -+ if (table) { -+ val = table[val]; -+ } else { -+ if (flags & CLK_MUX_INDEX_BIT) -+ val = 1 << 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); - -@@ -102,21 +120,21 @@ 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); - - struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **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 *clk; ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -409,7 +409,7 @@ void clk_unregister_divider(struct clk * - struct clk_mux { - struct clk_hw hw; - void __iomem *reg; -- u32 *table; -+ unsigned int *table; - u32 mask; - u8 shift; - u8 flags; -@@ -425,6 +425,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 **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, -@@ -433,7 +438,7 @@ struct clk *clk_register_mux(struct devi - struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **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); - diff --git a/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch b/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch deleted file mode 100644 index 143e0795f0..0000000000 --- a/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch +++ /dev/null @@ -1,130 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3, 03/13] clk: Avoid sending high rates to downstream clocks during - set_rate -From: Stephen Boyd -X-Patchwork-Id: 6063271 -Message-Id: <1426920332-9340-4-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:22 -0700 - -If a clock is on and we call clk_set_rate() on it we may get into -a situation where the clock temporarily increases in rate -dramatically while we walk the tree and call .set_rate() ops. For -example, consider a case where a PLL feeds into a divider. -Initially the divider is set to divide by 1 and the PLL is -running fairly slow (100MHz). The downstream consumer of the -divider output can only handle rates =< 400 MHz, but the divider -can only choose between divisors of 1 and 4. - - +-----+ +----------------+ - | PLL |-->| div 1 or div 4 |---> consumer device - +-----+ +----------------+ - -To achieve a rate of 400MHz on the output of the divider, we -would have to set the rate of the PLL to 1.6 GHz and then divide -it by 4. The current code would set the PLL to 1.6GHz first while -the divider is still set to 1, thus causing the downstream -consumer of the clock to receive a few clock cycles of 1.6GHz -clock (far beyond it's maximum acceptable rate). We should be -changing the divider first before increasing the PLL rate to -avoid this problem. - -Therefore, set the rate of any child clocks that are increasing -in rate from their current rate so that they can increase their -dividers if necessary. We assume that there isn't such a thing as -minimum rate requirements. - -Signed-off-by: Stephen Boyd - ---- -drivers/clk/clk.c | 34 ++++++++++++++++++++++------------ - 1 file changed, 22 insertions(+), 12 deletions(-) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -1744,21 +1744,24 @@ static struct clk_core *clk_propagate_ra - * walk down a subtree and set the new rates notifying the rate - * change on the way - */ --static void clk_change_rate(struct clk_core *clk) -+static void -+clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate) - { - struct clk_core *child; - struct hlist_node *tmp; - unsigned long old_rate; -- unsigned long best_parent_rate = 0; - bool skip_set_rate = false; - struct clk_core *old_parent; - -- old_rate = clk->rate; -+ hlist_for_each_entry(child, &clk->children, child_node) { -+ /* Skip children who will be reparented to another clock */ -+ if (child->new_parent && child->new_parent != clk) -+ continue; -+ if (child->new_rate > child->rate) -+ clk_change_rate(child, clk->new_rate); -+ } - -- if (clk->new_parent) -- best_parent_rate = clk->new_parent->rate; -- else if (clk->parent) -- best_parent_rate = clk->parent->rate; -+ old_rate = clk->rate; - - if (clk->new_parent && clk->new_parent != clk->parent) { - old_parent = __clk_set_parent_before(clk, clk->new_parent); -@@ -1784,7 +1787,7 @@ static void clk_change_rate(struct clk_c - - trace_clk_set_rate_complete(clk, clk->new_rate); - -- clk->rate = clk_recalc(clk, best_parent_rate); -+ clk->rate = clk->new_rate; - - if (clk->notifier_count && old_rate != clk->rate) - __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); -@@ -1797,12 +1800,13 @@ static void clk_change_rate(struct clk_c - /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) - continue; -- clk_change_rate(child); -+ if (child->new_rate != child->rate) -+ clk_change_rate(child, clk->new_rate); - } - - /* handle the new child who might not be in clk->children yet */ -- if (clk->new_child) -- clk_change_rate(clk->new_child); -+ if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate) -+ clk_change_rate(clk->new_child, clk->new_rate); - } - - static int clk_core_set_rate_nolock(struct clk_core *clk, -@@ -1811,6 +1815,7 @@ static int clk_core_set_rate_nolock(stru - struct clk_core *top, *fail_clk; - unsigned long rate = req_rate; - int ret = 0; -+ unsigned long parent_rate; - - if (!clk) - return 0; -@@ -1836,8 +1841,13 @@ static int clk_core_set_rate_nolock(stru - return -EBUSY; - } - -+ if (top->parent) -+ parent_rate = top->parent->rate; -+ else -+ parent_rate = 0; -+ - /* change the rates */ -- clk_change_rate(top); -+ clk_change_rate(top, parent_rate); - - clk->req_rate = req_rate; - diff --git a/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch deleted file mode 100644 index b0d89a9681..0000000000 --- a/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch +++ /dev/null @@ -1,164 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,04/13] clk: Add safe switch hook -From: Stephen Boyd -X-Patchwork-Id: 6063211 -Message-Id: <1426920332-9340-5-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:23 -0700 - -Sometimes clocks can't accept their parent source turning off -while the source is reprogrammed to a different rate. Most -notably CPU clocks require a way to switch away from the current -PLL they're running on, reprogram that PLL to a new rate, and -then switch back to the PLL with the new rate once they're done. -Add a hook that drivers can implement allowing them to return a -'safe parent' that they can switch their parent to while the -upstream source is reprogrammed to support this. - -Signed-off-by: Stephen Boyd - ---- -This patch is good enough for Krait, but soon I'll need to -support a "safe rate" where we ask a clock what rate it needs to be running -at to be sure it's within voltage constraints. Right now safe parent -handles that problem on Krait, but on other platforms it won't work. - - drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------ - include/linux/clk-provider.h | 1 + - 2 files changed, 54 insertions(+), 8 deletions(-) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -56,9 +56,12 @@ struct clk_core { - struct clk_core **parents; - u8 num_parents; - u8 new_parent_index; -+ u8 safe_parent_index; - unsigned long rate; - unsigned long req_rate; -+ unsigned long old_rate; - unsigned long new_rate; -+ struct clk_core *safe_parent; - struct clk_core *new_parent; - struct clk_core *new_child; - unsigned long flags; -@@ -1596,7 +1599,8 @@ out: - static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, - struct clk_core *new_parent, u8 p_index) - { -- struct clk_core *child; -+ struct clk_core *child, *parent; -+ struct clk_hw *parent_hw; - - clk->new_rate = new_rate; - clk->new_parent = new_parent; -@@ -1606,6 +1610,18 @@ static void clk_calc_subtree(struct clk_ - if (new_parent && new_parent != clk->parent) - new_parent->new_child = clk; - -+ if (clk->ops->get_safe_parent) { -+ parent_hw = clk->ops->get_safe_parent(clk->hw); -+ if (parent_hw) { -+ parent = parent_hw->core; -+ p_index = clk_fetch_parent_index(clk, parent); -+ clk->safe_parent_index = p_index; -+ clk->safe_parent = parent; -+ } -+ } else { -+ clk->safe_parent = NULL; -+ } -+ - hlist_for_each_entry(child, &clk->children, child_node) { - child->new_rate = clk_recalc(child, new_rate); - clk_calc_subtree(child, child->new_rate, NULL, 0); -@@ -1710,14 +1726,43 @@ static struct clk_core *clk_propagate_ra - unsigned long event) - { - struct clk_core *child, *tmp_clk, *fail_clk = NULL; -+ struct clk_core *old_parent; - int ret = NOTIFY_DONE; - -- if (clk->rate == clk->new_rate) -+ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE) - return NULL; - -+ switch (event) { -+ case PRE_RATE_CHANGE: -+ if (clk->safe_parent) -+ clk->ops->set_parent(clk->hw, clk->safe_parent_index); -+ clk->old_rate = clk->rate; -+ break; -+ case POST_RATE_CHANGE: -+ if (clk->safe_parent) { -+ old_parent = __clk_set_parent_before(clk, -+ clk->new_parent); -+ if (clk->ops->set_rate_and_parent) { -+ clk->ops->set_rate_and_parent(clk->hw, -+ clk->new_rate, -+ clk->new_parent ? -+ clk->new_parent->rate : 0, -+ clk->new_parent_index); -+ } else if (clk->ops->set_parent) { -+ clk->ops->set_parent(clk->hw, -+ clk->new_parent_index); -+ } -+ __clk_set_parent_after(clk, clk->new_parent, -+ old_parent); -+ } -+ break; -+ } -+ - if (clk->notifier_count) { -- ret = __clk_notify(clk, event, clk->rate, clk->new_rate); -- if (ret & NOTIFY_STOP_MASK) -+ if (event != POST_RATE_CHANGE || clk->old_rate != clk->rate) -+ ret = __clk_notify(clk, event, clk->old_rate, -+ clk->new_rate); -+ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) - fail_clk = clk; - } - -@@ -1763,7 +1808,8 @@ clk_change_rate(struct clk_core *clk, un - - old_rate = clk->rate; - -- if (clk->new_parent && clk->new_parent != clk->parent) { -+ if (clk->new_parent && clk->new_parent != clk->parent && -+ !clk->safe_parent) { - old_parent = __clk_set_parent_before(clk, clk->new_parent); - trace_clk_set_parent(clk, clk->new_parent); - -@@ -1789,9 +1835,6 @@ clk_change_rate(struct clk_core *clk, un - - clk->rate = clk->new_rate; - -- if (clk->notifier_count && old_rate != clk->rate) -- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); -- - /* - * Use safe iteration, as change_rate can actually swap parents - * for certain clock types. -@@ -1851,6 +1894,8 @@ static int clk_core_set_rate_nolock(stru - - clk->req_rate = req_rate; - -+ clk_propagate_rate_change(top, POST_RATE_CHANGE); -+ - return ret; - } - ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -183,6 +183,7 @@ struct clk_ops { - struct clk_hw **best_parent_hw); - int (*set_parent)(struct clk_hw *hw, u8 index); - u8 (*get_parent)(struct clk_hw *hw); -+ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate); - int (*set_rate_and_parent)(struct clk_hw *hw, diff --git a/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch deleted file mode 100644 index 6fad6e8a0c..0000000000 --- a/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch +++ /dev/null @@ -1,351 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) -From: Stephen Boyd -X-Patchwork-Id: 6063261 -Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:24 -0700 - -HFPLLs are the main frequency source for Krait CPU clocks. Add -support for changing the rate of these PLLs. - -Signed-off-by: Stephen Boyd - ---- -I'd really like to get rid of __clk_hfpll_init_once() if possible... - - 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 -@@ -8,6 +8,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 - - obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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_get_rate(hw->clk); -+ -+ /* 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. -+ */ -+ udelay(10); -+ -+ /* 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 { -+ udelay(60); -+ } -+ -+ /* 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 -+#include -+#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 diff --git a/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch deleted file mode 100644 index 4056784287..0000000000 --- a/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch +++ /dev/null @@ -1,206 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,06/13] clk: qcom: Add HFPLL driver -From: Stephen Boyd -X-Patchwork-Id: 6063231 -Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:25 -0700 - -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: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++ - drivers/clk/qcom/Kconfig | 8 ++ - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++ - 4 files changed, 158 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: -+ Definition: must be "qcom,hfpll" -+ -+- reg: -+ Usage: required -+ Value type: -+ 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: -+ 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 -@@ -97,3 +97,11 @@ config MSM_MMCC_8974 - Support for the multimedia clock controller on msm8974 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 -@@ -22,3 +22,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 - obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o -+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o ---- /dev/null -+++ b/drivers/clk/qcom/hfpll.c -@@ -0,0 +1,109 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 clk *clk; -+ 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); -+ -+ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr); -+ -+ return PTR_ERR_OR_ZERO(clk); -+} -+ -+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"); diff --git a/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch deleted file mode 100644 index ffcd462d12..0000000000 --- a/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch +++ /dev/null @@ -1,127 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs -From: Stephen Boyd -X-Patchwork-Id: 6063241 -Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:27 -0700 - -Describe the HFPLLs present on IPQ806X devices. - -Signed-off-by: Stephen Boyd - ---- -drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 83 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,85 @@ 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, -@@ -2307,6 +2387,9 @@ static struct clk_regmap *gcc_ipq806x_cl - [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, - [EBI2_CLK] = &ebi2_clk.clkr, - [EBI2_AON_CLK] = &ebi2_aon_clk.clkr, -+ [PLL9] = &hfpll0.clkr, -+ [PLL10] = &hfpll1.clkr, -+ [PLL12] = &hfpll_l2.clkr, - }; - - static const struct qcom_reset_map gcc_ipq806x_resets[] = { diff --git a/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch deleted file mode 100644 index 5fcc787c89..0000000000 --- a/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch +++ /dev/null @@ -1,271 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,09/13] clk: qcom: Add support for Krait clocks -From: Stephen Boyd -X-Patchwork-Id: 6063251 -Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar -Date: Fri, 20 Mar 2015 23:45:28 -0700 - -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 - ---- -drivers/clk/qcom/Kconfig | 4 ++ - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++ - drivers/clk/qcom/clk-krait.h | 49 +++++++++++++ - 4 files changed, 220 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 -@@ -105,3 +105,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 -@@ -8,6 +8,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 - ---- /dev/null -+++ b/drivers/clk/qcom/clk-krait.c -@@ -0,0 +1,166 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#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); -+} -+ -+static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw) -+{ -+ int i; -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ int num_parents = __clk_get_num_parents(hw->clk); -+ -+ i = mux->safe_sel; -+ for (i = 0; i < num_parents; i++) -+ if (mux->safe_sel == mux->parent_map[i]) -+ break; -+ -+ return __clk_get_hw(clk_get_parent_by_index(hw->clk, i)); -+} -+ -+static int krait_mux_enable(struct clk_hw *hw) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ -+ __krait_mux_set_sel(mux, mux->en_mask); -+ -+ return 0; -+} -+ -+static void krait_mux_disable(struct clk_hw *hw) -+{ -+ struct krait_mux_clk *mux = to_krait_mux_clk(hw); -+ -+ __krait_mux_set_sel(mux, mux->safe_sel); -+} -+ -+const struct clk_ops krait_mux_clk_ops = { -+ .enable = krait_mux_enable, -+ .disable = krait_mux_disable, -+ .set_parent = krait_mux_set_parent, -+ .get_parent = krait_mux_get_parent, -+ .determine_rate = __clk_mux_determine_rate_closest, -+ .get_safe_parent = krait_mux_get_safe_parent, -+}; -+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_round_rate(__clk_get_parent(hw->clk), 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,49 @@ -+/* -+ * 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 -+ -+struct krait_mux_clk { -+ unsigned int *parent_map; -+ bool has_safe_parent; -+ u8 safe_sel; -+ u32 offset; -+ u32 mask; -+ u32 shift; -+ u32 en_mask; -+ bool lpl; -+ -+ struct clk_hw hw; -+}; -+ -+#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 diff --git a/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch deleted file mode 100644 index db4388812b..0000000000 --- a/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch +++ /dev/null @@ -1,205 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver -From: Stephen Boyd -X-Patchwork-Id: 6063201 -Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:29 -0700 - -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: -Signed-off-by: Stephen Boyd - ---- -.../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 | 95 ++++++++++++++++++++++ - 5 files changed, 139 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: -+ 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: -+ Definition: should be one of: -+ "qcom,kpss-gcc" -+ -+- reg: -+ Usage: required -+ Value type: -+ Definition: base address and size of the register region -+ -+- clock-output-names: -+ Usage: required -+ Value type: -+ 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 -@@ -106,6 +106,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 -@@ -23,4 +23,5 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 - obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.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,95 @@ -+/* 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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"); diff --git a/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch deleted file mode 100644 index 59fc44f1cb..0000000000 --- a/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch +++ /dev/null @@ -1,435 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver -From: Stephen Boyd -X-Patchwork-Id: 6063121 -Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:30 -0700 - -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: -Signed-off-by: Stephen Boyd - ---- -.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ - drivers/clk/qcom/Kconfig | 8 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++ - 4 files changed, 383 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: -+ Definition: must be one of: -+ "qcom,krait-cc-v1" -+ "qcom,krait-cc-v2" -+ -+- #clock-cells: -+ Usage: required -+ Value type: -+ 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 -@@ -114,6 +114,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 -@@ -25,3 +25,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8 - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.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,352 @@ -+/* 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 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 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->has_safe_parent = true; -+ mux->safe_sel = 2; -+ 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 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->has_safe_parent = true; -+ mux->safe_sel = 0; -+ 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 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, CLK_IS_ROOT, 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"); diff --git a/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch deleted file mode 100644 index fd37c05425..0000000000 --- a/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch +++ /dev/null @@ -1,304 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs -From: Stephen Boyd -X-Patchwork-Id: 6063191 -Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org> -To: Mike Turquette , Stephen Boyd -Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - Viresh Kumar , -Date: Fri, 20 Mar 2015 23:45:31 -0700 - -Register a cpufreq-generic device whenever we detect that a -"qcom,krait" compatible CPU is present in DT. - -Cc: -Signed-off-by: Stephen Boyd - ---- -.../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: -+ 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: -+ 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 -@@ -146,6 +146,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 -@@ -66,6 +66,7 @@ obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += ki - 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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"); diff --git a/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch deleted file mode 100644 index aaf140126c..0000000000 --- a/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch +++ /dev/null @@ -1,100 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -26,6 +26,11 @@ - next-level-cache = <&L2>; - qcom,acc = <&acc0>; - qcom,saw = <&saw0>; -+ clocks = <&kraitcc 0>; -+ clock-names = "cpu"; -+ clock-latency = <100000>; -+ core-supply = <&smb208_s2a>; -+ voltage-tolerance = <5>; - }; - - cpu@1 { -@@ -36,11 +41,24 @@ - next-level-cache = <&L2>; - qcom,acc = <&acc1>; - qcom,saw = <&saw1>; -+ clocks = <&kraitcc 1>; -+ clock-names = "cpu"; -+ clock-latency = <100000>; -+ core-supply = <&smb208_s2b>; - }; - - L2: l2-cache { - compatible = "cache"; - cache-level = <2>; -+ clocks = <&kraitcc 4>; -+ clock-names = "cache"; -+ cache-points-kHz = < -+ /* kHz uV CPU kHz */ -+ 1200000 1150000 1200000 -+ 1000000 1100000 600000 -+ 384000 1100000 384000 -+ >; -+ vdd_dig-supply = <&smb208_s1a>; - }; - }; - -@@ -73,6 +91,46 @@ - }; - }; - -+ kraitcc: clock-controller { -+ compatible = "qcom,krait-cc-v1"; -+ #clock-cells = <1>; -+ }; -+ -+ qcom,pvs { -+ qcom,pvs-format-a; -+ qcom,speed0-pvs0-bin-v0 = -+ < 1400000000 1250000 >, -+ < 1200000000 1200000 >, -+ < 1000000000 1150000 >, -+ < 800000000 1100000 >, -+ < 600000000 1050000 >, -+ < 384000000 1000000 >; -+ -+ qcom,speed0-pvs1-bin-v0 = -+ < 1400000000 1175000 >, -+ < 1200000000 1125000 >, -+ < 1000000000 1075000 >, -+ < 800000000 1025000 >, -+ < 600000000 975000 >, -+ < 384000000 925000 >; -+ -+ qcom,speed0-pvs2-bin-v0 = -+ < 1400000000 1125000 >, -+ < 1200000000 1075000 >, -+ < 1000000000 1025000 >, -+ < 800000000 995000 >, -+ < 600000000 925000 >, -+ < 384000000 875000 >; -+ -+ qcom,speed0-pvs3-bin-v0 = -+ < 1400000000 1050000 >, -+ < 1200000000 1000000 >, -+ < 1000000000 950000 >, -+ < 800000000 900000 >, -+ < 600000000 850000 >, -+ < 384000000 800000 >; -+ }; -+ - soc: soc { - #address-cells = <1>; - #size-cells = <1>; -@@ -215,11 +273,13 @@ - acc0: clock-controller@2088000 { - compatible = "qcom,kpss-acc-v1"; - reg = <0x02088000 0x1000>, <0x02008000 0x1000>; -+ clock-output-names = "acpu0_aux"; - }; - - acc1: clock-controller@2098000 { - compatible = "qcom,kpss-acc-v1"; - reg = <0x02098000 0x1000>, <0x02008000 0x1000>; -+ clock-output-names = "acpu1_aux"; - }; - - l2cc: clock-controller@2011000 { diff --git a/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch deleted file mode 100644 index 84f856bbf6..0000000000 --- a/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch +++ /dev/null @@ -1,461 +0,0 @@ -From dd77db4143290689d3a5e1ec61627233d0711b66 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 30 May 2014 16:36:11 -0700 -Subject: [PATCH] FROMLIST: cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 - -Krait processors have individual clocks for each CPU that can -scale independently from one another. cpufreq-cpu0 is fairly -close to this, but assumes that there is only one clock for all -CPUs. Add a driver to support the Krait configuration. - -TODO: Merge into cpufreq-cpu0? Or make generic? - -Signed-off-by: Stephen Boyd - ---- - drivers/cpufreq/Kconfig | 13 +++ - drivers/cpufreq/Makefile | 1 + - drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 204 insertions(+) - create mode 100644 drivers/cpufreq/cpufreq-krait.c - ---- a/drivers/cpufreq/Kconfig -+++ b/drivers/cpufreq/Kconfig -@@ -198,6 +198,19 @@ config CPUFREQ_DT - - If in doubt, say N. - -+config GENERIC_CPUFREQ_KRAIT -+ tristate "Krait cpufreq driver" -+ depends on HAVE_CLK && OF -+ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: -+ depends on !CPU_THERMAL || THERMAL -+ select PM_OPP -+ help -+ This adds a generic cpufreq driver for CPU0 frequency management. -+ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) -+ systems which share clock and voltage across all CPUs. -+ -+ If in doubt, say N. -+ - if X86 - source "drivers/cpufreq/Kconfig.x86" - endif ---- a/drivers/cpufreq/Makefile -+++ b/drivers/cpufreq/Makefile -@@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) - obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o - - obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o -+obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o - - ################################################################################## - # x86 drivers. ---- /dev/null -+++ b/drivers/cpufreq/cpufreq-krait.c -@@ -0,0 +1,390 @@ -+/* -+ * Copyright (C) 2012 Freescale Semiconductor, Inc. -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * The OPP code in function krait_set_target() is reused from -+ * drivers/cpufreq/omap-cpufreq.c -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static unsigned int transition_latency; -+static unsigned int voltage_tolerance; /* in percentage */ -+ -+static struct device *cpu_dev; -+static DEFINE_PER_CPU(struct clk *, krait_cpu_clks); -+static DEFINE_PER_CPU(struct regulator *, krait_supply_core); -+static struct cpufreq_frequency_table *freq_table; -+static struct thermal_cooling_device *cdev; -+ -+struct cache_points { -+ unsigned long cache_freq; -+ unsigned int cache_volt; -+ unsigned long cpu_freq; -+}; -+ -+static struct regulator *krait_l2_reg; -+static struct clk *krait_l2_clk; -+static struct cache_points *krait_l2_points; -+static int nr_krait_l2_points; -+ -+static int krait_parse_cache_points(struct device *dev, -+ struct device_node *of_node) -+{ -+ const struct property *prop; -+ const __be32 *val; -+ int nr, i; -+ -+ prop = of_find_property(of_node, "cache-points-kHz", NULL); -+ if (!prop) -+ return -ENODEV; -+ if (!prop->value) -+ return -ENODATA; -+ -+ /* -+ * Each OPP is a set of tuples consisting of frequency and -+ * cpu-frequency like . -+ */ -+ nr = prop->length / sizeof(u32); -+ if (nr % 3) { -+ dev_err(dev, "%s: Invalid cache points\n", __func__); -+ return -EINVAL; -+ } -+ nr /= 3; -+ -+ krait_l2_points = devm_kcalloc(dev, nr, sizeof(*krait_l2_points), -+ GFP_KERNEL); -+ if (!krait_l2_points) -+ return -ENOMEM; -+ nr_krait_l2_points = nr; -+ -+ for (i = 0, val = prop->value; i < nr; i++) { -+ unsigned long cache_freq = be32_to_cpup(val++) * 1000; -+ unsigned int cache_volt = be32_to_cpup(val++); -+ unsigned long cpu_freq = be32_to_cpup(val++) * 1000; -+ -+ krait_l2_points[i].cache_freq = cache_freq; -+ krait_l2_points[i].cache_volt = cache_volt; -+ krait_l2_points[i].cpu_freq = cpu_freq; -+ } -+ -+ return 0; -+} -+ -+static int krait_set_target(struct cpufreq_policy *policy, unsigned int index) -+{ -+ struct dev_pm_opp *opp; -+ unsigned long volt = 0, volt_old = 0, tol = 0; -+ unsigned long freq, max_cpu_freq = 0; -+ unsigned int old_freq, new_freq; -+ long freq_Hz, freq_exact; -+ int ret, i; -+ struct clk *cpu_clk; -+ struct regulator *core; -+ unsigned int cpu; -+ -+ cpu_clk = per_cpu(krait_cpu_clks, policy->cpu); -+ -+ freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); -+ if (freq_Hz <= 0) -+ freq_Hz = freq_table[index].frequency * 1000; -+ -+ freq_exact = freq_Hz; -+ new_freq = freq_Hz / 1000; -+ old_freq = clk_get_rate(cpu_clk) / 1000; -+ -+ core = per_cpu(krait_supply_core, policy->cpu); -+ -+ rcu_read_lock(); -+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); -+ if (IS_ERR(opp)) { -+ rcu_read_unlock(); -+ pr_err("failed to find OPP for %ld\n", freq_Hz); -+ return PTR_ERR(opp); -+ } -+ volt = dev_pm_opp_get_voltage(opp); -+ rcu_read_unlock(); -+ tol = volt * voltage_tolerance / 100; -+ volt_old = regulator_get_voltage(core); -+ -+ pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", -+ old_freq / 1000, volt_old ? volt_old / 1000 : -1, -+ new_freq / 1000, volt ? volt / 1000 : -1); -+ -+ /* scaling up? scale voltage before frequency */ -+ if (new_freq > old_freq) { -+ ret = regulator_set_voltage_tol(core, volt, tol); -+ if (ret) { -+ pr_err("failed to scale voltage up: %d\n", ret); -+ return ret; -+ } -+ } -+ -+ ret = clk_set_rate(cpu_clk, freq_exact); -+ if (ret) { -+ pr_err("failed to set clock rate: %d\n", ret); -+ return ret; -+ } -+ -+ /* scaling down? scale voltage after frequency */ -+ if (new_freq < old_freq) { -+ ret = regulator_set_voltage_tol(core, volt, tol); -+ if (ret) { -+ pr_err("failed to scale voltage down: %d\n", ret); -+ clk_set_rate(cpu_clk, old_freq * 1000); -+ } -+ } -+ -+ for_each_possible_cpu(cpu) { -+ freq = clk_get_rate(per_cpu(krait_cpu_clks, cpu)); -+ max_cpu_freq = max(max_cpu_freq, freq); -+ } -+ -+ for (i = 0; i < nr_krait_l2_points; i++) { -+ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { -+ if (krait_l2_reg) { -+ ret = regulator_set_voltage_tol(krait_l2_reg, -+ krait_l2_points[i].cache_volt, -+ tol); -+ if (ret) { -+ pr_err("failed to scale l2 voltage: %d\n", -+ ret); -+ } -+ } -+ ret = clk_set_rate(krait_l2_clk, -+ krait_l2_points[i].cache_freq); -+ if (ret) -+ pr_err("failed to scale l2 clk: %d\n", ret); -+ break; -+ } -+ -+ } -+ -+ return ret; -+} -+ -+static int krait_cpufreq_init(struct cpufreq_policy *policy) -+{ -+ int ret; -+ -+ policy->clk = per_cpu(krait_cpu_clks, policy->cpu); -+ -+ ret = cpufreq_table_validate_and_show(policy, freq_table); -+ if (ret) { -+ pr_err("%s: invalid frequency table: %d\n", __func__, ret); -+ return ret; -+ } -+ -+ policy->cpuinfo.transition_latency = transition_latency; -+ -+ return 0; -+} -+ -+static struct cpufreq_driver krait_cpufreq_driver = { -+ .flags = CPUFREQ_STICKY, -+ .verify = cpufreq_generic_frequency_table_verify, -+ .target_index = krait_set_target, -+ .get = cpufreq_generic_get, -+ .init = krait_cpufreq_init, -+ .name = "generic_krait", -+ .attr = cpufreq_generic_attr, -+}; -+ -+static int krait_cpufreq_probe(struct platform_device *pdev) -+{ -+ struct device_node *np, *cache; -+ int ret, i; -+ unsigned int cpu; -+ struct device *dev; -+ struct clk *clk; -+ struct regulator *core; -+ unsigned long freq_Hz, freq, max_cpu_freq = 0; -+ struct dev_pm_opp *opp; -+ unsigned long volt, tol; -+ -+ cpu_dev = get_cpu_device(0); -+ if (!cpu_dev) { -+ pr_err("failed to get krait device\n"); -+ return -ENODEV; -+ } -+ -+ np = of_node_get(cpu_dev->of_node); -+ if (!np) { -+ pr_err("failed to find krait node\n"); -+ return -ENOENT; -+ } -+ -+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); -+ if (ret) { -+ pr_err("failed to init cpufreq table: %d\n", ret); -+ goto out_put_node; -+ } -+ -+ of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance); -+ -+ if (of_property_read_u32(np, "clock-latency", &transition_latency)) -+ transition_latency = CPUFREQ_ETERNAL; -+ -+ cache = of_find_next_cache_node(np); -+ if (cache) { -+ struct device_node *vdd; -+ -+ vdd = of_parse_phandle(cache, "vdd_dig-supply", 0); -+ if (vdd) { -+ krait_l2_reg = regulator_get(NULL, vdd->name); -+ if (IS_ERR(krait_l2_reg)) { -+ pr_warn("failed to get l2 vdd_dig supply\n"); -+ krait_l2_reg = NULL; -+ } -+ of_node_put(vdd); -+ } -+ -+ krait_l2_clk = of_clk_get(cache, 0); -+ if (!IS_ERR(krait_l2_clk)) { -+ ret = krait_parse_cache_points(&pdev->dev, cache); -+ if (ret) -+ clk_put(krait_l2_clk); -+ } -+ if (IS_ERR(krait_l2_clk) || ret) -+ krait_l2_clk = NULL; -+ } -+ -+ for_each_possible_cpu(cpu) { -+ dev = get_cpu_device(cpu); -+ if (!dev) { -+ pr_err("failed to get krait device\n"); -+ ret = -ENOENT; -+ goto out_free_table; -+ } -+ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(clk)) { -+ ret = PTR_ERR(clk); -+ goto out_free_table; -+ } -+ core = devm_regulator_get(dev, "core"); -+ if (IS_ERR(core)) { -+ pr_debug("failed to get core regulator\n"); -+ ret = PTR_ERR(core); -+ goto out_free_table; -+ } -+ per_cpu(krait_supply_core, cpu) = core; -+ -+ freq = freq_Hz = clk_get_rate(clk); -+ -+ rcu_read_lock(); -+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); -+ if (IS_ERR(opp)) { -+ rcu_read_unlock(); -+ pr_err("failed to find OPP for %ld\n", freq_Hz); -+ ret = PTR_ERR(opp); -+ goto out_free_table; -+ } -+ volt = dev_pm_opp_get_voltage(opp); -+ rcu_read_unlock(); -+ -+ tol = volt * voltage_tolerance / 100; -+ ret = regulator_set_voltage_tol(core, volt, tol); -+ if (ret) { -+ pr_err("failed to scale voltage up: %d\n", ret); -+ goto out_free_table; -+ } -+ ret = regulator_enable(core); -+ if (ret) { -+ pr_err("failed to enable regulator: %d\n", ret); -+ goto out_free_table; -+ } -+ max_cpu_freq = max(max_cpu_freq, freq); -+ } -+ -+ for (i = 0; i < nr_krait_l2_points; i++) { -+ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { -+ if (krait_l2_reg) { -+ ret = regulator_set_voltage_tol(krait_l2_reg, -+ krait_l2_points[i].cache_volt, -+ tol); -+ if (ret) -+ pr_err("failed to scale l2 voltage: %d\n", -+ ret); -+ ret = regulator_enable(krait_l2_reg); -+ if (ret) -+ pr_err("failed to enable l2 voltage: %d\n", -+ ret); -+ } -+ break; -+ } -+ -+ } -+ -+ ret = cpufreq_register_driver(&krait_cpufreq_driver); -+ if (ret) { -+ pr_err("failed register driver: %d\n", ret); -+ goto out_free_table; -+ } -+ of_node_put(np); -+ -+ /* -+ * For now, just loading the cooling device; -+ * thermal DT code takes care of matching them. -+ */ -+ for_each_possible_cpu(cpu) { -+ dev = get_cpu_device(cpu); -+ np = of_node_get(dev->of_node); -+ if (of_find_property(np, "#cooling-cells", NULL)) { -+ cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu)); -+ if (IS_ERR(cdev)) -+ pr_err("running cpufreq without cooling device: %ld\n", -+ PTR_ERR(cdev)); -+ } -+ of_node_put(np); -+ } -+ -+ return 0; -+ -+out_free_table: -+ regulator_put(krait_l2_reg); -+ clk_put(krait_l2_clk); -+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -+out_put_node: -+ of_node_put(np); -+ return ret; -+} -+ -+static int krait_cpufreq_remove(struct platform_device *pdev) -+{ -+ cpufreq_cooling_unregister(cdev); -+ cpufreq_unregister_driver(&krait_cpufreq_driver); -+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -+ clk_put(krait_l2_clk); -+ regulator_put(krait_l2_reg); -+ -+ return 0; -+} -+ -+static struct platform_driver krait_cpufreq_platdrv = { -+ .driver = { -+ .name = "cpufreq-krait", -+ .owner = THIS_MODULE, -+ }, -+ .probe = krait_cpufreq_probe, -+ .remove = krait_cpufreq_remove, -+}; -+module_platform_driver(krait_cpufreq_platdrv); -+ -+MODULE_DESCRIPTION("Krait CPUfreq driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/cpufreq/qcom-cpufreq.c -+++ b/drivers/cpufreq/qcom-cpufreq.c -@@ -168,11 +168,8 @@ static int __init qcom_cpufreq_populate_ - - 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), -+ .name = "cpufreq-krait", - }; - struct device *cpu_dev; - struct device_node *np; diff --git a/target/linux/ipq806x/patches-4.1/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.1/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch deleted file mode 100644 index 4f5c0efb7b..0000000000 --- a/target/linux/ipq806x/patches-4.1/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch +++ /dev/null @@ -1,76 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers -From: Andy Gross -X-Patchwork-Id: 6027361 -Message-Id: <1426571172-9711-2-git-send-email-agross@codeaurora.org> -To: Vinod Koul -Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, - linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, Kumar Gala , - Bjorn Andersson , - Andy Gross -Date: Tue, 17 Mar 2015 00:46:11 -0500 - -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 - ---- -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: - -@@ -56,7 +52,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"; - }; diff --git a/target/linux/ipq806x/patches-4.1/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.1/156-dmaengine-Add-ADM-driver.patch deleted file mode 100644 index 29e8eaaec4..0000000000 --- a/target/linux/ipq806x/patches-4.1/156-dmaengine-Add-ADM-driver.patch +++ /dev/null @@ -1,962 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v6,2/2] dmaengine: Add ADM driver -From: Andy Gross -X-Patchwork-Id: 6027351 -Message-Id: <1426571172-9711-3-git-send-email-agross@codeaurora.org> -To: Vinod Koul -Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, - linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, Kumar Gala , - Bjorn Andersson , - Andy Gross -Date: Tue, 17 Mar 2015 00:46:12 -0500 - -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 -Reviewed-by: sricharan - ---- -drivers/dma/Kconfig | 10 + - drivers/dma/Makefile | 1 + - drivers/dma/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 911 insertions(+) - create mode 100644 drivers/dma/qcom_adm.c - ---- a/drivers/dma/Kconfig -+++ b/drivers/dma/Kconfig -@@ -492,4 +492,14 @@ config QCOM_BAM_DMA - Enable support for the QCOM BAM DMA controller. This controller - provides DMA capabilities for a variety of on-chip devices. - -+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. -+ - endif ---- /dev/null -+++ b/drivers/dma/qcom_adm.c -@@ -0,0 +1,900 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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; -+ 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_NOWAIT); -+ 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 = dma_alloc_writecombine(adev->dev, async_desc->dma_len, -+ &async_desc->dma_addr, GFP_NOWAIT); -+ -+ if (!async_desc->cpl) { -+ kfree(async_desc); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ 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); -+ -+ /* init cmd list */ -+ *cple = ADM_CPLE_LP; -+ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3; -+ -+ 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); -+ } -+ -+ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); -+} -+ -+/** -+ * 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_free_writecombine(async_desc->adev->dev, async_desc->dma_len, -+ async_desc->cpl, async_desc->dma_addr); -+ 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 "); -+MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/dma/Makefile -+++ b/drivers/dma/Makefile -@@ -54,3 +54,4 @@ obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o - obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o - obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o - obj-$(CONFIG_XGENE_DMA) += xgene-dma.o -+obj-$(CONFIG_QCOM_ADM) += qcom_adm.o diff --git a/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch deleted file mode 100644 index 6f1013e32e..0000000000 --- a/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001 -From: Andy Gross -Date: Wed, 20 May 2015 15:41:07 +0530 -Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node - -This patch adds support for the ADM DMA on the IPQ8064 SOC - -Signed-off-by: Andy Gross ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 4 ++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++ - 2 files changed, 25 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -643,6 +643,26 @@ - - status = "disabled"; - }; -+ -+ adm_dma: dma@18300000 { -+ compatible = "qcom,adm"; -+ reg = <0x18300000 0x100000>; -+ interrupts = <0 170 0>; -+ #dma-cells = <1>; -+ -+ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; -+ clock-names = "core", "iface"; -+ -+ resets = <&gcc ADM0_RESET>, -+ <&gcc ADM0_PBUS_RESET>, -+ <&gcc ADM0_C0_RESET>, -+ <&gcc ADM0_C1_RESET>, -+ <&gcc ADM0_C2_RESET>; -+ reset-names = "clk", "pbus", "c0", "c1", "c2"; -+ qcom,ee = <0>; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch deleted file mode 100644 index 088b8cb5a1..0000000000 --- a/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch +++ /dev/null @@ -1,84 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3, - 1/5] mtd: nand: Create a BBT flag to access bad block markers in raw - mode -From: Archit Taneja -X-Patchwork-Id: 6927081 -Message-Id: <1438578498-32254-2-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja -Date: Mon, 3 Aug 2015 10:38:14 +0530 - -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 -Signed-off-by: Archit Taneja - ---- -drivers/mtd/nand/nand_base.c | 6 +++++- - drivers/mtd/nand/nand_bbt.c | 6 +++++- - include/linux/mtd/bbm.h | 7 +++++++ - 3 files changed, 17 insertions(+), 2 deletions(-) - ---- a/drivers/mtd/nand/nand_base.c -+++ b/drivers/mtd/nand/nand_base.c -@@ -395,7 +395,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 -@@ -423,7 +423,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,13 @@ 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. diff --git a/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch deleted file mode 100644 index 4be7ecb1b9..0000000000 --- a/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch +++ /dev/null @@ -1,2024 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver -From: Archit Taneja -X-Patchwork-Id: 6927101 -Message-Id: <1438578498-32254-3-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja -Date: Mon, 3 Aug 2015 10:38:15 +0530 - -The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx, -MDM9x15 series. - -It exists as a sub block inside the IPs EBI2 (External Bus Interface 2) -and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a -broader interface for external slow peripheral devices such as LCD and -NAND/NOR flash memory or SRAM like interfaces. - -We add support for the NAND controller found within EBI2. For the SoCs -of our interest, we only use the NAND controller within EBI2. Therefore, -it's safe for us to assume that the NAND controller is a standalone block -within the SoC. - -The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND -flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and -16 bit correction/step) and RS ECC(4 bit correction/step) that covers main -and spare data. The controller contains an internal 512 byte page buffer -to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA -for register read/write and data transfers. The controller performs page -reads and writes at a codeword/step level of 512 bytes. It can support up -to 2 external chips of different configurations. - -The driver prepares register read and write configuration descriptors for -each codeword, followed by data descriptors to read or write data from the -controller's internal buffer. It uses a single ADM DMA channel that we get -via dmaengine API. The controller requires 2 ADM CRCIs for command and -data flow control. These are passed via DT. - -The ecc layout used by the controller is syndrome like, but we can't use -the standard syndrome ecc ops because of several reasons. First, the amount -of data bytes covered by ecc isn't same in each step. Second, writing to -free oob space requires us writing to the entire step in which the oob -lies. This forces us to create our own ecc ops. - -One more difference is how the controller accesses the bad block marker. -The controller ignores reading the marker when ECC is enabled. ECC needs -to be explicity disabled to read or write to the bad block marker. For -this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to -read the factory provided bad block markers. - -v3: -- Refactor dma functions for maximum reuse -- Use dma_slave_confing on stack -- optimize and clean upempty_page_fixup using memchr_inv -- ensure portability with dma register reads using le32_* funcs -- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves -- fix handling of return values of dmaengine funcs -- constify wherever possible -- Remove dependency on ADM DMA in Kconfig -- Misc fixes and clean ups - -v2: -- Use new BBT flag that allows us to read BBM in raw mode -- reduce memcpy-s in the driver -- some refactor and clean ups because of above changes - -Reviewed-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -drivers/mtd/nand/Kconfig | 7 + - drivers/mtd/nand/Makefile | 1 + - drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 1921 insertions(+) - create mode 100644 drivers/mtd/nand/qcom_nandc.c - ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -530,4 +530,11 @@ config MTD_NAND_HISI504 - help - Enables support for NAND controller on Hisilicon SoC Hip04. - -+config MTD_NAND_QCOM -+ tristate "Support for NAND on QCOM SoCs" -+ depends on ARCH_QCOM -+ help -+ Enables support for NAND flash chips on SoCs containing the EBI2 NAND -+ controller. This controller is found on IPQ806x SoC. -+ - endif # MTD_NAND ---- /dev/null -+++ b/drivers/mtd/nand/qcom_nandc.c -@@ -0,0 +1,1918 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* NANDc reg offsets */ -+#define NAND_FLASH_CMD 0x00 -+#define NAND_ADDR0 0x04 -+#define NAND_ADDR1 0x08 -+#define NAND_FLASH_CHIP_SELECT 0x0c -+#define NAND_EXEC_CMD 0x10 -+#define NAND_FLASH_STATUS 0x14 -+#define NAND_BUFFER_STATUS 0x18 -+#define NAND_DEV0_CFG0 0x20 -+#define NAND_DEV0_CFG1 0x24 -+#define NAND_DEV0_ECC_CFG 0x28 -+#define NAND_DEV1_ECC_CFG 0x2c -+#define NAND_DEV1_CFG0 0x30 -+#define NAND_DEV1_CFG1 0x34 -+#define NAND_READ_ID 0x40 -+#define NAND_READ_STATUS 0x44 -+#define NAND_DEV_CMD0 0xa0 -+#define NAND_DEV_CMD1 0xa4 -+#define NAND_DEV_CMD2 0xa8 -+#define NAND_DEV_CMD_VLD 0xac -+#define SFLASHC_BURST_CFG 0xe0 -+#define NAND_ERASED_CW_DETECT_CFG 0xe8 -+#define NAND_ERASED_CW_DETECT_STATUS 0xec -+#define NAND_EBI2_ECC_BUF_CFG 0xf0 -+#define FLASH_BUF_ACC 0x100 -+ -+#define NAND_CTRL 0xf00 -+#define NAND_VERSION 0xf08 -+#define NAND_READ_LOCATION_0 0xf20 -+#define NAND_READ_LOCATION_1 0xf24 -+ -+/* dummy register offsets, used by write_reg_dma */ -+#define NAND_DEV_CMD1_RESTORE 0xdead -+#define NAND_DEV_CMD_VLD_RESTORE 0xbeef -+ -+/* NAND_FLASH_CMD bits */ -+#define PAGE_ACC BIT(4) -+#define LAST_PAGE BIT(5) -+ -+/* NAND_FLASH_CHIP_SELECT bits */ -+#define NAND_DEV_SEL 0 -+#define DM_EN BIT(2) -+ -+/* NAND_FLASH_STATUS bits */ -+#define FS_OP_ERR BIT(4) -+#define FS_READY_BSY_N BIT(5) -+#define FS_MPU_ERR BIT(8) -+#define FS_DEVICE_STS_ERR BIT(16) -+#define FS_DEVICE_WP BIT(23) -+ -+/* NAND_BUFFER_STATUS bits */ -+#define BS_UNCORRECTABLE_BIT BIT(8) -+#define BS_CORRECTABLE_ERR_MSK 0x1f -+ -+/* NAND_DEVn_CFG0 bits */ -+#define DISABLE_STATUS_AFTER_WRITE 4 -+#define CW_PER_PAGE 6 -+#define UD_SIZE_BYTES 9 -+#define ECC_PARITY_SIZE_BYTES_RS 19 -+#define SPARE_SIZE_BYTES 23 -+#define NUM_ADDR_CYCLES 27 -+#define STATUS_BFR_READ 30 -+#define SET_RD_MODE_AFTER_STATUS 31 -+ -+/* NAND_DEVn_CFG0 bits */ -+#define DEV0_CFG1_ECC_DISABLE 0 -+#define WIDE_FLASH 1 -+#define NAND_RECOVERY_CYCLES 2 -+#define CS_ACTIVE_BSY 5 -+#define BAD_BLOCK_BYTE_NUM 6 -+#define BAD_BLOCK_IN_SPARE_AREA 16 -+#define WR_RD_BSY_GAP 17 -+#define ENABLE_BCH_ECC 27 -+ -+/* NAND_DEV0_ECC_CFG bits */ -+#define ECC_CFG_ECC_DISABLE 0 -+#define ECC_SW_RESET 1 -+#define ECC_MODE 4 -+#define ECC_PARITY_SIZE_BYTES_BCH 8 -+#define ECC_NUM_DATA_BYTES 16 -+#define ECC_FORCE_CLK_OPEN 30 -+ -+/* NAND_DEV_CMD1 bits */ -+#define READ_ADDR 0 -+ -+/* NAND_DEV_CMD_VLD bits */ -+#define READ_START_VLD 0 -+ -+/* NAND_EBI2_ECC_BUF_CFG bits */ -+#define NUM_STEPS 0 -+ -+/* NAND_ERASED_CW_DETECT_CFG bits */ -+#define ERASED_CW_ECC_MASK 1 -+#define AUTO_DETECT_RES 0 -+#define MASK_ECC (1 << ERASED_CW_ECC_MASK) -+#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) -+#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) -+#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) -+#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) -+ -+/* NAND_ERASED_CW_DETECT_STATUS bits */ -+#define PAGE_ALL_ERASED BIT(7) -+#define CODEWORD_ALL_ERASED BIT(6) -+#define PAGE_ERASED BIT(5) -+#define CODEWORD_ERASED BIT(4) -+#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) -+#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) -+ -+/* Version Mask */ -+#define NAND_VERSION_MAJOR_MASK 0xf0000000 -+#define NAND_VERSION_MAJOR_SHIFT 28 -+#define NAND_VERSION_MINOR_MASK 0x0fff0000 -+#define NAND_VERSION_MINOR_SHIFT 16 -+ -+/* NAND OP_CMDs */ -+#define PAGE_READ 0x2 -+#define PAGE_READ_WITH_ECC 0x3 -+#define PAGE_READ_WITH_ECC_SPARE 0x4 -+#define PROGRAM_PAGE 0x6 -+#define PAGE_PROGRAM_WITH_ECC 0x7 -+#define PROGRAM_PAGE_SPARE 0x9 -+#define BLOCK_ERASE 0xa -+#define FETCH_ID 0xb -+#define RESET_DEVICE 0xd -+ -+/* -+ * the NAND controller performs reads/writes with ECC in 516 byte chunks. -+ * the driver calls the chunks 'step' or 'codeword' interchangeably -+ */ -+#define NANDC_STEP_SIZE 512 -+ -+/* -+ * the largest page size we support is 8K, this will have 16 steps/codewords -+ * of 512 bytes each -+ */ -+#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) -+ -+/* we read at most 3 registers per codeword scan */ -+#define MAX_REG_RD (3 * MAX_NUM_STEPS) -+ -+/* ECC modes */ -+#define ECC_NONE BIT(0) -+#define ECC_RS_4BIT BIT(1) -+#define ECC_BCH_4BIT BIT(2) -+#define ECC_BCH_8BIT BIT(3) -+ -+struct desc_info { -+ struct list_head list; -+ -+ enum dma_transfer_direction dir; -+ struct scatterlist sgl; -+ struct dma_async_tx_descriptor *dma_desc; -+}; -+ -+/* -+ * holds the current register values that we want to write. acts as a contiguous -+ * chunk of memory which we use to write the controller registers through DMA. -+ */ -+struct nandc_regs { -+ u32 cmd; -+ u32 addr0; -+ u32 addr1; -+ u32 chip_sel; -+ u32 exec; -+ -+ u32 cfg0; -+ u32 cfg1; -+ u32 ecc_bch_cfg; -+ -+ u32 clrflashstatus; -+ u32 clrreadstatus; -+ -+ u32 cmd1; -+ u32 vld; -+ -+ u32 orig_cmd1; -+ u32 orig_vld; -+ -+ u32 ecc_buf_cfg; -+}; -+ -+/* -+ * @cmd_crci: ADM DMA CRCI for command flow control -+ * @data_crci: ADM DMA CRCI for data flow control -+ * @list: DMA descriptor list (list of desc_infos) -+ * @dma_done: completion param to denote end of last -+ * descriptor in the list -+ * @data_buffer: our local DMA buffer for page read/writes, -+ * used when we can't use the buffer provided -+ * by upper layers directly -+ * @buf_size/count/start: markers for chip->read_buf/write_buf functions -+ * @reg_read_buf: buffer for reading register data via DMA -+ * @reg_read_pos: marker for data read in reg_read_buf -+ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for -+ * ecc/non-ecc mode for the current nand flash -+ * device -+ * @regs: a contiguous chunk of memory for DMA register -+ * writes -+ * @ecc_strength: 4 bit or 8 bit ecc, received via DT -+ * @bus_width: 8 bit or 16 bit NAND bus width, received via DT -+ * @ecc_modes: supported ECC modes by the current controller, -+ * initialized via DT match data -+ * @cw_size: the number of bytes in a single step/codeword -+ * of a page, consisting of all data, ecc, spare -+ * and reserved bytes -+ * @cw_data: the number of bytes within a codeword protected -+ * by ECC -+ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used -+ * @status: value to be returned if NAND_CMD_STATUS command -+ * is executed -+ */ -+struct qcom_nandc_data { -+ struct platform_device *pdev; -+ struct device *dev; -+ -+ void __iomem *base; -+ struct resource *res; -+ -+ struct clk *core_clk; -+ struct clk *aon_clk; -+ -+ /* DMA stuff */ -+ struct dma_chan *chan; -+ struct dma_slave_config slave_conf; -+ unsigned int cmd_crci; -+ unsigned int data_crci; -+ struct list_head list; -+ struct completion dma_done; -+ -+ /* MTD stuff */ -+ struct nand_chip chip; -+ struct mtd_info mtd; -+ -+ /* local data buffer and markers */ -+ u8 *data_buffer; -+ int buf_size; -+ int buf_count; -+ int buf_start; -+ -+ /* local buffer to read back registers */ -+ u32 *reg_read_buf; -+ int reg_read_pos; -+ -+ /* required configs */ -+ u32 cfg0, cfg1; -+ u32 cfg0_raw, cfg1_raw; -+ u32 ecc_buf_cfg; -+ u32 ecc_bch_cfg; -+ u32 clrflashstatus; -+ u32 clrreadstatus; -+ u32 sflashc_burst_cfg; -+ u32 cmd1, vld; -+ -+ /* register state */ -+ struct nandc_regs *regs; -+ -+ /* things we get from DT */ -+ int ecc_strength; -+ int bus_width; -+ -+ u32 ecc_modes; -+ -+ /* misc params */ -+ int cw_size; -+ int cw_data; -+ bool use_ecc; -+ bool bch_enabled; -+ u8 status; -+ int last_command; -+}; -+ -+static inline u32 nandc_read(struct qcom_nandc_data *this, int offset) -+{ -+ return ioread32(this->base + offset); -+} -+ -+static inline void nandc_write(struct qcom_nandc_data *this, int offset, -+ u32 val) -+{ -+ iowrite32(val, this->base + offset); -+} -+ -+/* helper to configure address register values */ -+static void set_address(struct qcom_nandc_data *this, u16 column, int page) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nandc_regs *regs = this->regs; -+ -+ if (chip->options & NAND_BUSWIDTH_16) -+ column >>= 1; -+ -+ regs->addr0 = page << 16 | column; -+ regs->addr1 = page >> 16 & 0xff; -+} -+ -+/* -+ * update_rw_regs: set up read/write register values, these will be -+ * written to the NAND controller registers via DMA -+ * -+ * @num_cw: number of steps for the read/write operation -+ * @read: read or write operation -+ */ -+static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ if (read) { -+ if (this->use_ecc) -+ regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; -+ else -+ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; -+ } else { -+ regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; -+ } -+ -+ if (this->use_ecc) { -+ regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) | -+ (num_cw - 1) << CW_PER_PAGE; -+ -+ regs->cfg1 = this->cfg1; -+ regs->ecc_bch_cfg = this->ecc_bch_cfg; -+ } else { -+ regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) | -+ (num_cw - 1) << CW_PER_PAGE; -+ -+ regs->cfg1 = this->cfg1_raw; -+ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; -+ } -+ -+ regs->ecc_buf_cfg = this->ecc_buf_cfg; -+ regs->clrflashstatus = this->clrflashstatus; -+ regs->clrreadstatus = this->clrreadstatus; -+ regs->exec = 1; -+} -+ -+static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off, -+ const void *vaddr, int size, bool flow_control) -+{ -+ struct desc_info *desc; -+ struct dma_async_tx_descriptor *dma_desc; -+ struct scatterlist *sgl; -+ struct dma_slave_config slave_conf; -+ int r; -+ -+ desc = kzalloc(sizeof(*desc), GFP_KERNEL); -+ if (!desc) -+ return -ENOMEM; -+ -+ list_add_tail(&desc->list, &this->list); -+ -+ sgl = &desc->sgl; -+ -+ sg_init_one(sgl, vaddr, size); -+ -+ desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; -+ -+ r = dma_map_sg(this->dev, sgl, 1, desc->dir); -+ if (r == 0) { -+ r = -ENOMEM; -+ goto err; -+ } -+ -+ memset(&slave_conf, 0x00, sizeof(slave_conf)); -+ -+ slave_conf.device_fc = flow_control; -+ if (read) { -+ slave_conf.src_maxburst = 16; -+ slave_conf.src_addr = this->res->start + reg_off; -+ slave_conf.slave_id = this->data_crci; -+ } else { -+ slave_conf.dst_maxburst = 16; -+ slave_conf.dst_addr = this->res->start + reg_off; -+ slave_conf.slave_id = this->cmd_crci; -+ } -+ -+ r = dmaengine_slave_config(this->chan, &slave_conf); -+ if (r) { -+ dev_err(this->dev, "failed to configure dma channel\n"); -+ goto err; -+ } -+ -+ dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0); -+ if (!dma_desc) { -+ dev_err(this->dev, "failed to prepare desc\n"); -+ r = -EINVAL; -+ goto err; -+ } -+ -+ desc->dma_desc = dma_desc; -+ -+ return 0; -+err: -+ kfree(desc); -+ -+ return r; -+} -+ -+/* -+ * read_reg_dma: prepares a descriptor to read a given number of -+ * contiguous registers to the reg_read_buf pointer -+ * -+ * @first: offset of the first register in the contiguous block -+ * @num_regs: number of registers to read -+ */ -+static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) -+{ -+ bool flow_control = false; -+ void *vaddr; -+ int size; -+ -+ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) -+ flow_control = true; -+ -+ size = num_regs * sizeof(u32); -+ vaddr = this->reg_read_buf + this->reg_read_pos; -+ this->reg_read_pos += num_regs; -+ -+ return prep_dma_desc(this, true, first, vaddr, size, flow_control); -+} -+ -+/* -+ * write_reg_dma: prepares a descriptor to write a given number of -+ * contiguous registers -+ * -+ * @first: offset of the first register in the contiguous block -+ * @num_regs: number of registers to write -+ */ -+static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) -+{ -+ bool flow_control = false; -+ struct nandc_regs *regs = this->regs; -+ void *vaddr; -+ int size; -+ -+ switch (first) { -+ case NAND_FLASH_CMD: -+ vaddr = ®s->cmd; -+ flow_control = true; -+ break; -+ case NAND_EXEC_CMD: -+ vaddr = ®s->exec; -+ break; -+ case NAND_FLASH_STATUS: -+ vaddr = ®s->clrflashstatus; -+ break; -+ case NAND_DEV0_CFG0: -+ vaddr = ®s->cfg0; -+ break; -+ case NAND_READ_STATUS: -+ vaddr = ®s->clrreadstatus; -+ break; -+ case NAND_DEV_CMD1: -+ vaddr = ®s->cmd1; -+ break; -+ case NAND_DEV_CMD1_RESTORE: -+ first = NAND_DEV_CMD1; -+ vaddr = ®s->orig_cmd1; -+ break; -+ case NAND_DEV_CMD_VLD: -+ vaddr = ®s->vld; -+ break; -+ case NAND_DEV_CMD_VLD_RESTORE: -+ first = NAND_DEV_CMD_VLD; -+ vaddr = ®s->orig_vld; -+ break; -+ case NAND_EBI2_ECC_BUF_CFG: -+ vaddr = ®s->ecc_buf_cfg; -+ break; -+ default: -+ dev_err(this->dev, "invalid starting register\n"); -+ return -EINVAL; -+ } -+ -+ size = num_regs * sizeof(u32); -+ -+ return prep_dma_desc(this, false, first, vaddr, size, flow_control); -+} -+ -+/* -+ * read_data_dma: prepares a DMA descriptor to transfer data from the -+ * controller's internal buffer to the buffer 'vaddr' -+ * -+ * @reg_off: offset within the controller's data buffer -+ * @vaddr: virtual address of the buffer we want to write to -+ * @size: DMA transaction size in bytes -+ */ -+static int read_data_dma(struct qcom_nandc_data *this, int reg_off, -+ const u8 *vaddr, int size) -+{ -+ return prep_dma_desc(this, true, reg_off, vaddr, size, false); -+} -+ -+/* -+ * write_data_dma: prepares a DMA descriptor to transfer data from -+ * 'vaddr' to the controller's internal buffer -+ * -+ * @reg_off: offset within the controller's data buffer -+ * @vaddr: virtual address of the buffer we want to read from -+ * @size: DMA transaction size in bytes -+ */ -+static int write_data_dma(struct qcom_nandc_data *this, int reg_off, -+ const u8 *vaddr, int size) -+{ -+ return prep_dma_desc(this, false, reg_off, vaddr, size, false); -+} -+ -+/* -+ * helper to prepare dma descriptors to configure registers needed for reading a -+ * codeword/step in a page -+ */ -+static void config_cw_read(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 3); -+ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); -+ -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 2); -+ read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1); -+} -+ -+/* -+ * helpers to prepare dma descriptors used to configure registers needed for -+ * writing a codeword/step in a page -+ */ -+static void config_cw_write_pre(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 3); -+ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); -+} -+ -+static void config_cw_write_post(struct qcom_nandc_data *this) -+{ -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ write_reg_dma(this, NAND_FLASH_STATUS, 1); -+ write_reg_dma(this, NAND_READ_STATUS, 1); -+} -+ -+/* -+ * the following functions are used within chip->cmdfunc() to perform different -+ * NAND_CMD_* commands -+ */ -+ -+/* sets up descriptors for NAND_CMD_PARAM */ -+static int nandc_param(struct qcom_nandc_data *this) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ /* -+ * NAND_CMD_PARAM is called before we know much about the FLASH chip -+ * in use. we configure the controller to perform a raw read of 512 -+ * bytes to read onfi params -+ */ -+ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; -+ regs->addr0 = 0; -+ regs->addr1 = 0; -+ regs->cfg0 = 0 << CW_PER_PAGE -+ | 512 << UD_SIZE_BYTES -+ | 5 << NUM_ADDR_CYCLES -+ | 0 << SPARE_SIZE_BYTES; -+ -+ regs->cfg1 = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | 17 << BAD_BLOCK_BYTE_NUM -+ | 1 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | 0 << WIDE_FLASH -+ | 1 << DEV0_CFG1_ECC_DISABLE; -+ -+ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; -+ -+ /* configure CMD1 and VLD for ONFI param probing */ -+ regs->vld = (this->vld & ~(1 << READ_START_VLD)) -+ | 0 << READ_START_VLD; -+ -+ regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR)) -+ | NAND_CMD_PARAM << READ_ADDR; -+ -+ regs->exec = 1; -+ -+ regs->orig_cmd1 = this->cmd1; -+ regs->orig_vld = this->vld; -+ -+ write_reg_dma(this, NAND_DEV_CMD_VLD, 1); -+ write_reg_dma(this, NAND_DEV_CMD1, 1); -+ -+ this->buf_count = 512; -+ memset(this->data_buffer, 0xff, this->buf_count); -+ -+ config_cw_read(this); -+ -+ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count); -+ -+ /* restore CMD1 and VLD regs */ -+ write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1); -+ write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_ERASE1 */ -+static int erase_block(struct qcom_nandc_data *this, int page_addr) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE; -+ regs->addr0 = page_addr; -+ regs->addr1 = 0; -+ regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE); -+ regs->cfg1 = this->cfg1_raw; -+ regs->exec = 1; -+ regs->clrflashstatus = this->clrflashstatus; -+ regs->clrreadstatus = this->clrreadstatus; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 3); -+ write_reg_dma(this, NAND_DEV0_CFG0, 2); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ write_reg_dma(this, NAND_FLASH_STATUS, 1); -+ write_reg_dma(this, NAND_READ_STATUS, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_READID */ -+static int read_id(struct qcom_nandc_data *this, int column) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ if (column == -1) -+ return 0; -+ -+ regs->cmd = FETCH_ID; -+ regs->addr0 = column; -+ regs->addr1 = 0; -+ regs->chip_sel = DM_EN; -+ regs->exec = 1; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 4); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_READ_ID, 1); -+ -+ return 0; -+} -+ -+/* sets up descriptors for NAND_CMD_RESET */ -+static int reset(struct qcom_nandc_data *this) -+{ -+ struct nandc_regs *regs = this->regs; -+ -+ regs->cmd = RESET_DEVICE; -+ regs->exec = 1; -+ -+ write_reg_dma(this, NAND_FLASH_CMD, 1); -+ write_reg_dma(this, NAND_EXEC_CMD, 1); -+ -+ read_reg_dma(this, NAND_FLASH_STATUS, 1); -+ -+ return 0; -+} -+ -+/* helpers to submit/free our list of dma descriptors */ -+static void dma_callback(void *param) -+{ -+ struct qcom_nandc_data *this = param; -+ struct completion *c = &this->dma_done; -+ -+ complete(c); -+} -+ -+static int submit_descs(struct qcom_nandc_data *this) -+{ -+ struct completion *c = &this->dma_done; -+ struct desc_info *desc; -+ int r; -+ -+ init_completion(c); -+ -+ list_for_each_entry(desc, &this->list, list) { -+ /* -+ * we add a callback to the last descriptor in our list to -+ * notify completion of command -+ */ -+ if (list_is_last(&desc->list, &this->list)) { -+ desc->dma_desc->callback = dma_callback; -+ desc->dma_desc->callback_param = this; -+ } -+ -+ dmaengine_submit(desc->dma_desc); -+ } -+ -+ dma_async_issue_pending(this->chan); -+ -+ r = wait_for_completion_timeout(c, msecs_to_jiffies(500)); -+ if (!r) -+ return -ETIMEDOUT; -+ -+ return 0; -+} -+ -+static void free_descs(struct qcom_nandc_data *this) -+{ -+ struct desc_info *desc, *n; -+ -+ list_for_each_entry_safe(desc, n, &this->list, list) { -+ list_del(&desc->list); -+ dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir); -+ kfree(desc); -+ } -+} -+ -+/* reset the register read buffer for next NAND operation */ -+static void clear_read_regs(struct qcom_nandc_data *this) -+{ -+ this->reg_read_pos = 0; -+ memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf)); -+} -+ -+static void pre_command(struct qcom_nandc_data *this, int command) -+{ -+ this->buf_count = 0; -+ this->buf_start = 0; -+ this->use_ecc = false; -+ this->last_command = command; -+ -+ clear_read_regs(this); -+} -+ -+/* -+ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our -+ * privately maintained status byte, this status byte can be read after -+ * NAND_CMD_STATUS is called -+ */ -+static void parse_erase_write_errors(struct qcom_nandc_data *this, int command) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int num_cw; -+ int i; -+ -+ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; -+ -+ for (i = 0; i < num_cw; i++) { -+ __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]); -+ -+ if (flash_status & FS_MPU_ERR) -+ this->status &= ~NAND_STATUS_WP; -+ -+ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && -+ (flash_status & FS_DEVICE_STS_ERR))) -+ this->status |= NAND_STATUS_FAIL; -+ } -+} -+ -+static void post_command(struct qcom_nandc_data *this, int command) -+{ -+ switch (command) { -+ case NAND_CMD_READID: -+ memcpy(this->data_buffer, this->reg_read_buf, this->buf_count); -+ break; -+ case NAND_CMD_PAGEPROG: -+ case NAND_CMD_ERASE1: -+ parse_erase_write_errors(this, command); -+ break; -+ default: -+ break; -+ } -+} -+ -+/* -+ * Implements chip->cmdfunc. It's only used for a limited set of commands. -+ * The rest of the commands wouldn't be called by upper layers. For example, -+ * NAND_CMD_READOOB would never be called because we have our own versions -+ * of read_oob ops for nand_ecc_ctrl. -+ */ -+static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, -+ int column, int page_addr) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ struct qcom_nandc_data *this = chip->priv; -+ bool wait = false; -+ int r = 0; -+ -+ pre_command(this, command); -+ -+ switch (command) { -+ case NAND_CMD_RESET: -+ r = reset(this); -+ wait = true; -+ break; -+ -+ case NAND_CMD_READID: -+ this->buf_count = 4; -+ r = read_id(this, column); -+ wait = true; -+ break; -+ -+ case NAND_CMD_PARAM: -+ r = nandc_param(this); -+ wait = true; -+ break; -+ -+ case NAND_CMD_ERASE1: -+ r = erase_block(this, page_addr); -+ wait = true; -+ break; -+ -+ case NAND_CMD_READ0: -+ /* we read the entire page for now */ -+ WARN_ON(column != 0); -+ -+ this->use_ecc = true; -+ set_address(this, 0, page_addr); -+ update_rw_regs(this, ecc->steps, true); -+ break; -+ -+ case NAND_CMD_SEQIN: -+ WARN_ON(column != 0); -+ set_address(this, 0, page_addr); -+ break; -+ -+ case NAND_CMD_PAGEPROG: -+ case NAND_CMD_STATUS: -+ case NAND_CMD_NONE: -+ default: -+ break; -+ } -+ -+ if (r) { -+ dev_err(this->dev, "failure executing command %d\n", -+ command); -+ free_descs(this); -+ return; -+ } -+ -+ if (wait) { -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, -+ "failure submitting descs for command %d\n", -+ command); -+ } -+ -+ free_descs(this); -+ -+ post_command(this, command); -+} -+ -+/* -+ * when using RS ECC, the NAND controller flags an error when reading an -+ * erased page. however, there are special characters at certain offsets when -+ * we read the erased page. we check here if the page is really empty. if so, -+ * we replace the magic characters with 0xffs -+ */ -+static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = ecc->steps; -+ u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS]; -+ int i, j; -+ -+ /* if BCH is enabled, HW will take care of detecting erased pages */ -+ if (this->bch_enabled || !this->use_ecc) -+ return false; -+ -+ for (i = 0; i < cwperpage; i++) { -+ u8 *empty1, *empty2; -+ __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]); -+ -+ /* -+ * an erased page flags an error in NAND_FLASH_STATUS, check if -+ * the page is erased by looking for 0x54s at offsets 3 and 175 -+ * from the beginning of each codeword -+ */ -+ if (!(flash_status & FS_OP_ERR)) -+ break; -+ -+ empty1 = &data_buf[3 + i * this->cw_data]; -+ empty2 = &data_buf[175 + i * this->cw_data]; -+ -+ /* -+ * if the error wasn't because of an erased page, bail out and -+ * and let someone else do the error checking -+ */ -+ if ((*empty1 == 0x54 && *empty2 == 0xff) || -+ (*empty1 == 0xff && *empty2 == 0x54)) { -+ orig1[i] = *empty1; -+ orig2[i] = *empty2; -+ -+ *empty1 = 0xff; -+ *empty2 = 0xff; -+ } else { -+ break; -+ } -+ } -+ -+ if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize)) -+ goto not_empty; -+ -+ /* -+ * tell the caller that the page was empty and is fixed up, so that -+ * parse_read_errors() doesn't think it's an error -+ */ -+ return true; -+ -+not_empty: -+ /* restore original values if not empty*/ -+ for (j = 0; j < i; j++) { -+ data_buf[3 + j * this->cw_data] = orig1[j]; -+ data_buf[175 + j * this->cw_data] = orig2[j]; -+ } -+ -+ return false; -+} -+ -+struct read_stats { -+ __le32 flash; -+ __le32 buffer; -+ __le32 erased_cw; -+}; -+ -+/* -+ * reads back status registers set by the controller to notify page read -+ * errors. this is equivalent to what 'ecc->correct()' would do. -+ */ -+static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = ecc->steps; -+ unsigned int max_bitflips = 0; -+ int i; -+ -+ for (i = 0; i < cwperpage; i++) { -+ int stat; -+ struct read_stats *buf; -+ -+ buf = (struct read_stats *) (this->reg_read_buf + 3 * i); -+ -+ buf->flash = le32_to_cpu(buf->flash); -+ buf->buffer = le32_to_cpu(buf->buffer); -+ buf->erased_cw = le32_to_cpu(buf->erased_cw); -+ -+ if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) { -+ -+ /* ignore erased codeword errors */ -+ if (this->bch_enabled) { -+ if ((buf->erased_cw & ERASED_CW) == ERASED_CW) -+ continue; -+ } else if (erased_page) { -+ continue; -+ } -+ -+ if (buf->buffer & BS_UNCORRECTABLE_BIT) { -+ mtd->ecc_stats.failed++; -+ continue; -+ } -+ } -+ -+ stat = buf->buffer & BS_CORRECTABLE_ERR_MSK; -+ mtd->ecc_stats.corrected += stat; -+ -+ max_bitflips = max_t(unsigned int, max_bitflips, stat); -+ } -+ -+ return max_bitflips; -+} -+ -+/* -+ * helper to perform the actual page read operation, used by ecc->read_page() -+ * and ecc->read_oob() -+ */ -+static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf, -+ u8 *oob_buf) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int i, r; -+ -+ /* queue cmd descs for each codeword */ -+ for (i = 0; i < ecc->steps; i++) { -+ int data_size, oob_size; -+ -+ if (i == (ecc->steps - 1)) { -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ } else { -+ data_size = this->cw_data; -+ oob_size = ecc->bytes; -+ } -+ -+ config_cw_read(this); -+ -+ if (data_buf) -+ read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); -+ -+ if (oob_buf) -+ read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf, -+ oob_size); -+ -+ if (data_buf) -+ data_buf += data_size; -+ if (oob_buf) -+ oob_buf += oob_size; -+ } -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failure to read page/oob\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* -+ * a helper that copies the last step/codeword of a page (containing free oob) -+ * into our local buffer -+ */ -+static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page) -+{ -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int size; -+ int r; -+ -+ clear_read_regs(this); -+ -+ size = use_ecc ? this->cw_data : this->cw_size; -+ -+ /* prepare a clean read buffer */ -+ memset(this->data_buffer, 0xff, size); -+ -+ this->use_ecc = use_ecc; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, true); -+ -+ config_cw_read(this); -+ -+ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size); -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failed to copy last codeword\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* implements ecc->read_page() */ -+static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, -+ uint8_t *buf, int oob_required, int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ u8 *data_buf, *oob_buf = NULL; -+ bool erased_page; -+ int r; -+ -+ data_buf = buf; -+ oob_buf = oob_required ? chip->oob_poi : NULL; -+ -+ r = read_page_low(this, data_buf, oob_buf); -+ if (r) { -+ dev_err(this->dev, "failure to read page\n"); -+ return r; -+ } -+ -+ erased_page = empty_page_fixup(this, data_buf); -+ -+ return parse_read_errors(this, erased_page); -+} -+ -+/* implements ecc->read_oob() */ -+static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int r; -+ -+ clear_read_regs(this); -+ -+ this->use_ecc = true; -+ set_address(this, 0, page); -+ update_rw_regs(this, ecc->steps, true); -+ -+ r = read_page_low(this, NULL, chip->oob_poi); -+ if (r) -+ dev_err(this->dev, "failure to read oob\n"); -+ -+ return r; -+} -+ -+/* implements ecc->read_oob_raw(), used to read the bad block marker flag */ -+static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int start, length; -+ int r; -+ -+ /* -+ * configure registers for a raw page read, the address is set to the -+ * beginning of the last codeword, we don't care about reading ecc -+ * portion of oob, just the free stuff -+ */ -+ r = copy_last_cw(this, false, page); -+ if (r) -+ return r; -+ -+ /* -+ * reading raw oob has 2 parts, first the bad block byte, then the -+ * actual free oob region. perform a memcpy in two steps -+ */ -+ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); -+ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; -+ -+ memcpy(oob, this->data_buffer + start, length); -+ -+ oob += length; -+ -+ start = this->cw_data - (ecc->steps << 2) + 1; -+ length = ecc->steps << 2; -+ -+ memcpy(oob, this->data_buffer + start, length); -+ -+ return 0; -+} -+ -+/* implements ecc->write_page() */ -+static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, -+ const uint8_t *buf, int oob_required) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ u8 *data_buf, *oob_buf; -+ int i, r = 0; -+ -+ clear_read_regs(this); -+ -+ data_buf = (u8 *) buf; -+ oob_buf = chip->oob_poi; -+ -+ this->use_ecc = true; -+ update_rw_regs(this, ecc->steps, false); -+ -+ for (i = 0; i < ecc->steps; i++) { -+ int data_size, oob_size; -+ -+ if (i == (ecc->steps - 1)) { -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ } else { -+ data_size = this->cw_data; -+ oob_size = ecc->bytes; -+ } -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); -+ -+ /* -+ * we don't really need to write anything to oob for the -+ * first n - 1 codewords since these oob regions just -+ * contain ecc that's written by the controller itself -+ */ -+ if (i == (ecc->steps - 1)) -+ write_data_dma(this, FLASH_BUF_ACC + data_size, -+ oob_buf, oob_size); -+ config_cw_write_post(this); -+ -+ data_buf += data_size; -+ oob_buf += oob_size; -+ } -+ -+ r = submit_descs(this); -+ if (r) -+ dev_err(this->dev, "failure to write page\n"); -+ -+ free_descs(this); -+ -+ return r; -+} -+ -+/* -+ * implements ecc->write_oob() -+ * -+ * the NAND controller cannot write only data or only oob within a codeword, -+ * since ecc is calculated for the combined codeword. we first copy the -+ * entire contents for the last codeword(data + oob), replace the old oob -+ * with the new one in chip->oob_poi, and then write the entire codeword. -+ * this read-copy-write operation results in a slight perormance loss. -+ */ -+static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, -+ int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int free_boff; -+ int data_size, oob_size; -+ int r, status = 0; -+ -+ r = copy_last_cw(this, true, page); -+ if (r) -+ return r; -+ -+ clear_read_regs(this); -+ -+ /* calculate the data and oob size for the last codeword/step */ -+ data_size = ecc->size - ((ecc->steps - 1) << 2); -+ oob_size = (ecc->steps << 2) + ecc->bytes; -+ -+ /* -+ * the location of spare data in the oob buffer, we could also use -+ * ecc->layout.oobfree here -+ */ -+ free_boff = ecc->bytes * (ecc->steps - 1); -+ -+ /* override new oob content to last codeword */ -+ memcpy(this->data_buffer + data_size, oob + free_boff, oob_size); -+ -+ this->use_ecc = true; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, false); -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, -+ data_size + oob_size); -+ config_cw_write_post(this); -+ -+ r = submit_descs(this); -+ -+ free_descs(this); -+ -+ if (r) { -+ dev_err(this->dev, "failure to write oob\n"); -+ return -EIO; -+ } -+ -+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); -+ -+ status = chip->waitfunc(mtd, chip); -+ -+ return status & NAND_STATUS_FAIL ? -EIO : 0; -+} -+ -+/* implements ecc->write_oob_raw(), used to write bad block marker flag */ -+static int qcom_nandc_write_oob_raw(struct mtd_info *mtd, -+ struct nand_chip *chip, int page) -+{ -+ struct qcom_nandc_data *this = chip->priv; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ uint8_t *oob = chip->oob_poi; -+ int start, length; -+ int r, status = 0; -+ -+ r = copy_last_cw(this, false, page); -+ if (r) -+ return r; -+ -+ clear_read_regs(this); -+ -+ /* -+ * writing raw oob has 2 parts, first the bad block region, then the -+ * actual free region -+ */ -+ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); -+ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; -+ -+ memcpy(this->data_buffer + start, oob, length); -+ -+ oob += length; -+ -+ start = this->cw_data - (ecc->steps << 2) + 1; -+ length = ecc->steps << 2; -+ -+ memcpy(this->data_buffer + start, oob, length); -+ -+ /* prepare write */ -+ this->use_ecc = false; -+ set_address(this, this->cw_size * (ecc->steps - 1), page); -+ update_rw_regs(this, 1, false); -+ -+ config_cw_write_pre(this); -+ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size); -+ config_cw_write_post(this); -+ -+ r = submit_descs(this); -+ -+ free_descs(this); -+ -+ if (r) { -+ dev_err(this->dev, "failure to write updated oob\n"); -+ return -EIO; -+ } -+ -+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); -+ -+ status = chip->waitfunc(mtd, chip); -+ -+ return status & NAND_STATUS_FAIL ? -EIO : 0; -+} -+ -+/* -+ * the three functions below implement chip->read_byte(), chip->read_buf() -+ * and chip->write_buf() respectively. these aren't used for -+ * reading/writing page data, they are used for smaller data like reading -+ * id, status etc -+ */ -+static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ uint8_t *buf = this->data_buffer; -+ uint8_t ret = 0x0; -+ -+ if (this->last_command == NAND_CMD_STATUS) { -+ ret = this->status; -+ -+ this->status = NAND_STATUS_READY | NAND_STATUS_WP; -+ -+ return ret; -+ } -+ -+ if (this->buf_start < this->buf_count) -+ ret = buf[this->buf_start++]; -+ -+ return ret; -+} -+ -+static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); -+ -+ memcpy(buf, this->data_buffer + this->buf_start, real_len); -+ this->buf_start += real_len; -+} -+ -+static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, -+ int len) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); -+ -+ memcpy(this->data_buffer + this->buf_start, buf, real_len); -+ -+ this->buf_start += real_len; -+} -+ -+/* we support only one external chip for now */ -+static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) -+{ -+ struct nand_chip *chip = mtd->priv; -+ struct qcom_nandc_data *this = chip->priv; -+ -+ if (chipnr <= 0) -+ return; -+ -+ dev_warn(this->dev, "invalid chip select\n"); -+} -+ -+/* -+ * NAND controller page layout info -+ * -+ * |-----------------------| |---------------------------------| -+ * | xx.......xx| | *********xx.......xx| -+ * | DATA xx..ECC..xx| | DATA **SPARE**xx..ECC..xx| -+ * | (516) xx.......xx| | (516-n*4) **(n*4)**xx.......xx| -+ * | xx.......xx| | *********xx.......xx| -+ * |-----------------------| |---------------------------------| -+ * codeword 1,2..n-1 codeword n -+ * <---(528/532 Bytes)----> <-------(528/532 Bytes)----------> -+ * -+ * n = number of codewords in the page -+ * . = ECC bytes -+ * * = spare bytes -+ * x = unused/reserved bytes -+ * -+ * 2K page: n = 4, spare = 16 bytes -+ * 4K page: n = 8, spare = 32 bytes -+ * 8K page: n = 16, spare = 64 bytes -+ * -+ * the qcom nand controller operates at a sub page/codeword level. each -+ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. -+ * the number of ECC bytes vary based on the ECC strength and the bus width. -+ * -+ * the first n - 1 codewords contains 516 bytes of user data, the remaining -+ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains -+ * both user data and spare(oobavail) bytes that sum up to 516 bytes. -+ * -+ * the layout described above is used by the controller when the ECC block is -+ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are -+ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout -+ * layouts defined below doesn't consider the positions occupied by the reserved -+ * bytes -+ * -+ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width) -+ * in the last codeword is the position of bad block marker. the bad block -+ * marker cannot be accessed when ECC is enabled. -+ * -+ */ -+ -+/* -+ * Layouts for different page sizes and ecc modes. We skip the eccpos field -+ * since it isn't needed for this driver -+ */ -+ -+/* 2K page, 4 bit ECC */ -+static struct nand_ecclayout layout_oob_64 = { -+ .eccbytes = 40, -+ .oobfree = { -+ { 30, 16 }, -+ }, -+}; -+ -+/* 4K page, 4 bit ECC, 8/16 bit bus width */ -+static struct nand_ecclayout layout_oob_128 = { -+ .eccbytes = 80, -+ .oobfree = { -+ { 70, 32 }, -+ }, -+}; -+ -+/* 4K page, 8 bit ECC, 8 bit bus width */ -+static struct nand_ecclayout layout_oob_224_x8 = { -+ .eccbytes = 104, -+ .oobfree = { -+ { 91, 32 }, -+ }, -+}; -+ -+/* 4K page, 8 bit ECC, 16 bit bus width */ -+static struct nand_ecclayout layout_oob_224_x16 = { -+ .eccbytes = 112, -+ .oobfree = { -+ { 98, 32 }, -+ }, -+}; -+ -+/* 8K page, 4 bit ECC, 8/16 bit bus width */ -+static struct nand_ecclayout layout_oob_256 = { -+ .eccbytes = 160, -+ .oobfree = { -+ { 151, 64 }, -+ }, -+}; -+ -+/* -+ * this is called before scan_ident, we do some minimal configurations so -+ * that reading ID and ONFI params work -+ */ -+static void qcom_nandc_pre_init(struct qcom_nandc_data *this) -+{ -+ /* kill onenand */ -+ nandc_write(this, SFLASHC_BURST_CFG, 0); -+ -+ /* enable ADM DMA */ -+ nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN); -+ -+ /* save the original values of these registers */ -+ this->cmd1 = nandc_read(this, NAND_DEV_CMD1); -+ this->vld = nandc_read(this, NAND_DEV_CMD_VLD); -+ -+ /* initial status value */ -+ this->status = NAND_STATUS_READY | NAND_STATUS_WP; -+} -+ -+static int qcom_nandc_ecc_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage; -+ bool wide_bus; -+ -+ /* the nand controller fetches codewords/chunks of 512 bytes */ -+ cwperpage = mtd->writesize >> 9; -+ -+ ecc->strength = this->ecc_strength; -+ -+ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; -+ -+ if (ecc->strength >= 8) { -+ /* 8 bit ECC defaults to BCH ECC on all platforms */ -+ ecc->bytes = wide_bus ? 14 : 13; -+ } else { -+ /* -+ * if the controller supports BCH for 4 bit ECC, the controller -+ * uses lesser bytes for ECC. If RS is used, the ECC bytes is -+ * always 10 bytes -+ */ -+ if (this->ecc_modes & ECC_BCH_4BIT) -+ ecc->bytes = wide_bus ? 8 : 7; -+ else -+ ecc->bytes = 10; -+ } -+ -+ /* each step consists of 512 bytes of data */ -+ ecc->size = NANDC_STEP_SIZE; -+ -+ ecc->read_page = qcom_nandc_read_page; -+ ecc->read_oob = qcom_nandc_read_oob; -+ ecc->write_page = qcom_nandc_write_page; -+ ecc->write_oob = qcom_nandc_write_oob; -+ -+ /* -+ * the bad block marker is readable only when we read the page with ECC -+ * disabled. all the ops above run with ECC enabled. We need raw read -+ * and write function for oob in order to access bad block marker. -+ */ -+ ecc->read_oob_raw = qcom_nandc_read_oob_raw; -+ ecc->write_oob_raw = qcom_nandc_write_oob_raw; -+ -+ switch (mtd->oobsize) { -+ case 64: -+ ecc->layout = &layout_oob_64; -+ break; -+ case 128: -+ ecc->layout = &layout_oob_128; -+ break; -+ case 224: -+ if (wide_bus) -+ ecc->layout = &layout_oob_224_x16; -+ else -+ ecc->layout = &layout_oob_224_x8; -+ break; -+ case 256: -+ ecc->layout = &layout_oob_256; -+ break; -+ default: -+ dev_err(this->dev, "unsupported NAND device, oobsize %d\n", -+ mtd->oobsize); -+ return -ENODEV; -+ } -+ -+ ecc->mode = NAND_ECC_HW; -+ -+ /* enable ecc by default */ -+ this->use_ecc = true; -+ -+ return 0; -+} -+ -+static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct nand_ecc_ctrl *ecc = &chip->ecc; -+ int cwperpage = mtd->writesize / ecc->size; -+ int spare_bytes, bad_block_byte; -+ bool wide_bus; -+ int ecc_mode = 0; -+ -+ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; -+ -+ if (ecc->strength >= 8) { -+ this->cw_size = 532; -+ -+ spare_bytes = wide_bus ? 0 : 2; -+ -+ this->bch_enabled = true; -+ ecc_mode = 1; -+ } else { -+ this->cw_size = 528; -+ -+ if (this->ecc_modes & ECC_BCH_4BIT) { -+ spare_bytes = wide_bus ? 2 : 4; -+ -+ this->bch_enabled = true; -+ ecc_mode = 0; -+ } else { -+ spare_bytes = wide_bus ? 0 : 1; -+ } -+ } -+ -+ /* -+ * DATA_UD_BYTES varies based on whether the read/write command protects -+ * spare data with ECC too. We protect spare data by default, so we set -+ * it to main + spare data, which are 512 and 4 bytes respectively. -+ */ -+ this->cw_data = 516; -+ -+ bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1; -+ -+ this->cfg0 = (cwperpage - 1) << CW_PER_PAGE -+ | this->cw_data << UD_SIZE_BYTES -+ | 0 << DISABLE_STATUS_AFTER_WRITE -+ | 5 << NUM_ADDR_CYCLES -+ | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS -+ | 0 << STATUS_BFR_READ -+ | 1 << SET_RD_MODE_AFTER_STATUS -+ | spare_bytes << SPARE_SIZE_BYTES; -+ -+ this->cfg1 = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | bad_block_byte << BAD_BLOCK_BYTE_NUM -+ | 0 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | wide_bus << WIDE_FLASH -+ | this->bch_enabled << ENABLE_BCH_ECC; -+ -+ this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE -+ | this->cw_size << UD_SIZE_BYTES -+ | 5 << NUM_ADDR_CYCLES -+ | 0 << SPARE_SIZE_BYTES; -+ -+ this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES -+ | 0 << CS_ACTIVE_BSY -+ | 17 << BAD_BLOCK_BYTE_NUM -+ | 1 << BAD_BLOCK_IN_SPARE_AREA -+ | 2 << WR_RD_BSY_GAP -+ | wide_bus << WIDE_FLASH -+ | 1 << DEV0_CFG1_ECC_DISABLE; -+ -+ this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE -+ | 0 << ECC_SW_RESET -+ | this->cw_data << ECC_NUM_DATA_BYTES -+ | 1 << ECC_FORCE_CLK_OPEN -+ | ecc_mode << ECC_MODE -+ | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH; -+ -+ this->ecc_buf_cfg = 0x203 << NUM_STEPS; -+ -+ this->clrflashstatus = FS_READY_BSY_N; -+ this->clrreadstatus = 0xc0; -+ -+ dev_dbg(this->dev, -+ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", -+ this->cfg0, this->cfg1, this->ecc_buf_cfg, -+ this->ecc_bch_cfg, this->cw_size, this->cw_data, -+ ecc->strength, ecc->bytes, cwperpage); -+} -+ -+static int qcom_nandc_alloc(struct qcom_nandc_data *this) -+{ -+ int r; -+ -+ r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32)); -+ if (r) { -+ dev_err(this->dev, "failed to set DMA mask\n"); -+ return r; -+ } -+ -+ /* -+ * we use the internal buffer for reading ONFI params, reading small -+ * data like ID and status, and preforming read-copy-write operations -+ * when writing to a codeword partially. 532 is the maximum possible -+ * size of a codeword for our nand controller -+ */ -+ this->buf_size = 532; -+ -+ this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL); -+ if (!this->data_buffer) -+ return -ENOMEM; -+ -+ this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL); -+ if (!this->regs) -+ return -ENOMEM; -+ -+ this->reg_read_buf = devm_kzalloc(this->dev, -+ MAX_REG_RD * sizeof(*this->reg_read_buf), -+ GFP_KERNEL); -+ if (!this->reg_read_buf) -+ return -ENOMEM; -+ -+ INIT_LIST_HEAD(&this->list); -+ -+ this->chan = dma_request_slave_channel(this->dev, "rxtx"); -+ if (!this->chan) { -+ dev_err(this->dev, "failed to request slave channel\n"); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static void qcom_nandc_unalloc(struct qcom_nandc_data *this) -+{ -+ dma_release_channel(this->chan); -+} -+ -+static int qcom_nandc_init(struct qcom_nandc_data *this) -+{ -+ struct mtd_info *mtd = &this->mtd; -+ struct nand_chip *chip = &this->chip; -+ struct device_node *np = this->dev->of_node; -+ struct mtd_part_parser_data ppdata = { .of_node = np }; -+ int r; -+ -+ mtd->priv = chip; -+ mtd->name = "qcom-nandc"; -+ mtd->owner = THIS_MODULE; -+ -+ chip->priv = this; -+ -+ chip->cmdfunc = qcom_nandc_command; -+ chip->select_chip = qcom_nandc_select_chip; -+ chip->read_byte = qcom_nandc_read_byte; -+ chip->read_buf = qcom_nandc_read_buf; -+ chip->write_buf = qcom_nandc_write_buf; -+ -+ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; -+ if (this->bus_width == 16) -+ chip->options |= NAND_BUSWIDTH_16; -+ -+ chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW; -+ if (of_get_nand_on_flash_bbt(np)) -+ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; -+ -+ qcom_nandc_pre_init(this); -+ -+ r = nand_scan_ident(mtd, 1, NULL); -+ if (r) -+ return r; -+ -+ r = qcom_nandc_ecc_init(this); -+ if (r) -+ return r; -+ -+ qcom_nandc_hw_post_init(this); -+ -+ r = nand_scan_tail(mtd); -+ if (r) -+ return r; -+ -+ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); -+} -+ -+static int qcom_nandc_parse_dt(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this = platform_get_drvdata(pdev); -+ struct device_node *np = this->dev->of_node; -+ int r; -+ -+ this->ecc_strength = of_get_nand_ecc_strength(np); -+ if (this->ecc_strength < 0) { -+ dev_warn(this->dev, -+ "incorrect ecc strength, setting to 4 bits/step\n"); -+ this->ecc_strength = 4; -+ } -+ -+ this->bus_width = of_get_nand_bus_width(np); -+ if (this->bus_width < 0) { -+ dev_warn(this->dev, "incorrect bus width, setting to 8\n"); -+ this->bus_width = 8; -+ } -+ -+ r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci); -+ if (r) { -+ dev_err(this->dev, "command CRCI unspecified\n"); -+ return r; -+ } -+ -+ r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci); -+ if (r) { -+ dev_err(this->dev, "data CRCI unspecified\n"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+static int qcom_nandc_probe(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this; -+ const struct of_device_id *match; -+ int r; -+ -+ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); -+ if (!this) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, this); -+ -+ this->pdev = pdev; -+ this->dev = &pdev->dev; -+ -+ match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); -+ if (!match) { -+ dev_err(&pdev->dev, "failed to match device\n"); -+ return -ENODEV; -+ } -+ -+ if (!match->data) { -+ dev_err(&pdev->dev, "failed to get device data\n"); -+ return -ENODEV; -+ } -+ -+ this->ecc_modes = (u32) match->data; -+ -+ this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ this->base = devm_ioremap_resource(&pdev->dev, this->res); -+ if (IS_ERR(this->base)) -+ return PTR_ERR(this->base); -+ -+ this->core_clk = devm_clk_get(&pdev->dev, "core"); -+ if (IS_ERR(this->core_clk)) -+ return PTR_ERR(this->core_clk); -+ -+ this->aon_clk = devm_clk_get(&pdev->dev, "aon"); -+ if (IS_ERR(this->aon_clk)) -+ return PTR_ERR(this->aon_clk); -+ -+ r = qcom_nandc_parse_dt(pdev); -+ if (r) -+ return r; -+ -+ r = qcom_nandc_alloc(this); -+ if (r) -+ return r; -+ -+ r = clk_prepare_enable(this->core_clk); -+ if (r) -+ goto err_core_clk; -+ -+ r = clk_prepare_enable(this->aon_clk); -+ if (r) -+ goto err_aon_clk; -+ -+ r = qcom_nandc_init(this); -+ if (r) -+ goto err_init; -+ -+ return 0; -+ -+err_init: -+ clk_disable_unprepare(this->aon_clk); -+err_aon_clk: -+ clk_disable_unprepare(this->core_clk); -+err_core_clk: -+ qcom_nandc_unalloc(this); -+ -+ return r; -+} -+ -+static int qcom_nandc_remove(struct platform_device *pdev) -+{ -+ struct qcom_nandc_data *this = platform_get_drvdata(pdev); -+ -+ qcom_nandc_unalloc(this); -+ -+ clk_disable_unprepare(this->aon_clk); -+ clk_disable_unprepare(this->core_clk); -+ -+ return 0; -+} -+ -+#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT) -+ -+/* -+ * data will hold a struct pointer containing more differences once we support -+ * more IPs -+ */ -+static const struct of_device_id qcom_nandc_of_match[] = { -+ { .compatible = "qcom,ebi2-nandc", -+ .data = (void *) EBI2_NANDC_ECC_MODES, -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); -+ -+static struct platform_driver qcom_nandc_driver = { -+ .driver = { -+ .name = "qcom-nandc", -+ .of_match_table = qcom_nandc_of_match, -+ }, -+ .probe = qcom_nandc_probe, -+ .remove = qcom_nandc_remove, -+}; -+module_platform_driver(qcom_nandc_driver); -+ -+MODULE_AUTHOR("Archit Taneja "); -+MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/mtd/nand/Makefile -+++ b/drivers/mtd/nand/Makefile -@@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nan - obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ - obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o - obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o -+obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o - - nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch deleted file mode 100644 index 6530eb1c86..0000000000 --- a/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch +++ /dev/null @@ -1,82 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings -From: Archit Taneja -X-Patchwork-Id: 6927141 -Message-Id: <1438578498-32254-4-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:16 +0530 - -Add DT bindings document for the Qualcomm NAND controller driver. - -Cc: devicetree@vger.kernel.org - -v3: -- Don't use '0x' when specifying nand controller address space -- Add optional property for on-flash bbt usage - -Acked-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -.../devicetree/bindings/mtd/qcom_nandc.txt | 49 ++++++++++++++++++++++ - 1 file changed, 49 insertions(+) - create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt -@@ -0,0 +1,49 @@ -+* Qualcomm NAND controller -+ -+Required properties: -+- compatible: should be "qcom,ebi2-nand" for IPQ806x -+- reg: MMIO address range -+- clocks: must contain core clock and always on clock -+- clock-names: must contain "core" for the core clock and "aon" for the -+ always on clock -+- dmas: DMA specifier, consisting of a phandle to the ADM DMA -+ controller node and the channel number to be used for -+ NAND. Refer to dma.txt and qcom_adm.txt for more details -+- dma-names: must be "rxtx" -+- qcom,cmd-crci: must contain the ADM command type CRCI block instance -+ number specified for the NAND controller on the given -+ platform -+- qcom,data-crci: must contain the ADM data type CRCI block instance -+ number specified for the NAND controller on the given -+ platform -+ -+Optional properties: -+- nand-bus-width: bus width. Must be 8 or 16. If not present, 8 is chosen -+ as default -+ -+- nand-ecc-strength: number of bits to correct per ECC step. Must be 4 or 8 -+ bits. If not present, 4 is chosen as default -+- nand-on-flash-bbt: Create/use on-flash bad block table -+ -+The device tree may optionally contain sub-nodes describing partitions of the -+address space. See partition.txt for more detail. -+ -+Example: -+ -+nand@1ac00000 { -+ compatible = "qcom,ebi2-nandc"; -+ reg = <0x1ac00000 0x800>; -+ -+ clocks = <&gcc EBI2_CLK>, -+ <&gcc EBI2_AON_CLK>; -+ clock-names = "core", "aon"; -+ -+ dmas = <&adm_dma 3>; -+ dma-names = "rxtx"; -+ qcom,cmd-crci = <15>; -+ qcom,data-crci = <3>; -+ -+ partition@0 { -+ ... -+ }; -+}; diff --git a/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch deleted file mode 100644 index 6bdd5e05cc..0000000000 --- a/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch +++ /dev/null @@ -1,51 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x -From: Archit Taneja -X-Patchwork-Id: 6927121 -Message-Id: <1438578498-32254-5-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:17 +0530 - -The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding -compatible string. - -Cc: devicetree@vger.kernel.org - -Reviewed-by: Andy Gross -Signed-off-by: Archit Taneja - ---- -arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -663,6 +663,22 @@ - - status = "disabled"; - }; -+ -+ nand@1ac00000 { -+ compatible = "qcom,ebi2-nandc"; -+ reg = <0x1ac00000 0x800>; -+ -+ clocks = <&gcc EBI2_CLK>, -+ <&gcc EBI2_AON_CLK>; -+ clock-names = "core", "aon"; -+ -+ dmas = <&adm_dma 3>; -+ dma-names = "rxtx"; -+ qcom,cmd-crci = <15>; -+ qcom,data-crci = <3>; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch deleted file mode 100644 index da1ec46c4e..0000000000 --- a/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch +++ /dev/null @@ -1,76 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform -From: Archit Taneja -X-Patchwork-Id: 6927091 -Message-Id: <1438578498-32254-6-git-send-email-architt@codeaurora.org> -To: linux-mtd@lists.infradead.org, dehrenberg@google.com, - cernekee@gmail.com, computersforpeace@gmail.com -Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, - sboyd@codeaurora.org, linux-kernel@vger.kernel.org, - Archit Taneja , devicetree@vger.kernel.org -Date: Mon, 3 Aug 2015 10:38:18 +0530 - -Enable the NAND controller node on the AP148 platform. Provide pinmux -information. - -Cc: devicetree@vger.kernel.org - -Signed-off-by: Archit Taneja - ---- -arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++ - 1 file changed, 36 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -43,6 +43,28 @@ - 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; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -101,5 +123,19 @@ - status = "ok"; - phy-tx0-term-offset = <7>; - }; -+ -+ nand@1ac00000 { -+ status = "ok"; -+ -+ pinctrl-0 = <&nand_pins>; -+ pinctrl-names = "default"; -+ -+ nand-ecc-strength = <4>; -+ nand-bus-width = <8>; -+ }; - }; - }; -+ -+&adm_dma { -+ status = "ok"; -+}; diff --git a/target/linux/ipq806x/patches-4.1/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-4.1/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch deleted file mode 100644 index 9005de4eb5..0000000000 --- a/target/linux/ipq806x/patches-4.1/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -132,6 +132,8 @@ - - nand-ecc-strength = <4>; - nand-bus-width = <8>; -+ -+ linux,part-probe = "qcom-smem"; - }; - }; - }; diff --git a/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch deleted file mode 100644 index 18b7a80b12..0000000000 --- a/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch +++ /dev/null @@ -1,62 +0,0 @@ -From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Wed, 29 Apr 2015 15:21:46 -0700 -Subject: [PATCH] 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 ---- - 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 -@@ -320,7 +320,7 @@ config ARCH_MULTIPLATFORM - select ARCH_WANT_OPTIONAL_GPIOLIB - select ARM_HAS_SG_CHAIN - select ARM_PATCH_PHYS_VIRT -- select AUTO_ZRELADDR -+ select AUTO_ZRELADDR if !ARCH_QCOM - select CLKSRC_OF - select COMMON_CLK - select GENERIC_CLOCKEVENTS ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -248,9 +248,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 diff --git a/target/linux/ipq806x/patches-4.1/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch b/target/linux/ipq806x/patches-4.1/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch deleted file mode 100644 index 1f5eba8377..0000000000 --- a/target/linux/ipq806x/patches-4.1/301-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch +++ /dev/null @@ -1,367 +0,0 @@ -From 7e77aa188a7a7c4391856a9e5ef5ef58f769e679 Mon Sep 17 00:00:00 2001 -From: Jonas Gorski -Date: Sun, 9 Aug 2015 13:02:38 +0200 -Subject: [PATCH] ARM: qcom: add Netgear Nighthawk X4 R7500 device tree - -Signed-off-by: Jonas Gorski ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 370 +++++++++++++++++++++++++++++++ - 2 files changed, 371 insertions(+) - create mode 100644 arch/arm/boot/dts/qcom-ipq8064-r7500.dts - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -452,6 +452,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ - qcom-apq8084-mtp.dtb \ - qcom-ipq8064-ap148.dtb \ - qcom-ipq8064-db149.dtb \ -+ qcom-ipq8064-r7500.dtb \ - qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb \ - qcom-msm8974-sony-xperia-honami.dtb ---- /dev/null -+++ b/arch/arm/boot/dts/qcom-ipq8064-r7500.dts -@@ -0,0 +1,342 @@ -+#include "qcom-ipq8064-v1.0.dtsi" -+ -+#include -+ -+/ { -+ 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 = &uart4; -+ mdio-gpio0 = &mdio0; -+ }; -+ -+ chosen { -+ bootargs = "rootfstype=squashfs noinitrd"; -+ linux,stdout-path = "serial0:115200n8"; -+ }; -+ -+ soc { -+ pinmux@800000 { -+ i2c4_pins: i2c4_pinmux { -+ pins = "gpio12", "gpio13"; -+ function = "gsbi4"; -+ 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; -+ }; -+ }; -+ -+ 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 = ; -+ 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"; -+ }; -+ -+ pcie1: pci@1b700000 { -+ status = "ok"; -+ }; -+ -+ nand@1ac00000 { -+ status = "ok"; -+ -+ pinctrl-0 = <&nand_pins>; -+ pinctrl-names = "default"; -+ -+ nand-ecc-strength = <4>; -+ nand-bus-width = <8>; -+ -+ #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 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 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; -+ }; -+ }; -+ }; -+ -+ gpio-keys { -+ compatible = "gpio-keys"; -+ -+ wifi { -+ label = "wifi"; -+ gpios = <&qcom_pinmux 6 1>; -+ linux,code = ; -+ }; -+ -+ reset { -+ label = "reset"; -+ gpios = <&qcom_pinmux 54 1>; -+ linux,code = ; -+ }; -+ -+ wps { -+ label = "wps"; -+ gpios = <&qcom_pinmux 65 1>; -+ linux,code = ; -+ }; -+ }; -+ -+ gpio-leds { -+ compatible = "gpio-leds"; -+ -+ usb1 { -+ label = "r7500:amber:usb1"; -+ gpios = <&qcom_pinmux 7 0>; -+ }; -+ -+ usb3 { -+ label = "r7500:amber:usb3"; -+ gpios = <&qcom_pinmux 8 0>; -+ }; -+ -+ status { -+ label = "r7500:amber:status"; -+ gpios = <&qcom_pinmux 9 0>; -+ }; -+ -+ internet { -+ label = "r7500:white:internet"; -+ gpios = <&qcom_pinmux 22 0>; -+ }; -+ -+ wan { -+ label = "r7500:white:wan"; -+ gpios = <&qcom_pinmux 23 0>; -+ }; -+ -+ wps { -+ label = "r7500:white:wps"; -+ gpios = <&qcom_pinmux 24 0>; -+ }; -+ -+ esata { -+ label = "r7500:white:esata"; -+ gpios = <&qcom_pinmux 26 0>; -+ }; -+ -+ power { -+ label = "r7500:white:power"; -+ gpios = <&qcom_pinmux 53 0>; -+ default-state = "on"; -+ }; -+ -+ rfkill { -+ label = "r7500:white:rfkill"; -+ gpios = <&qcom_pinmux 64 0>; -+ }; -+ -+ wifi5g { -+ label = "r7500:white:wifi5g"; -+ gpios = <&qcom_pinmux 67 0>; -+ }; -+ }; -+}; -+ -+&adm_dma { -+ status = "ok"; -+}; diff --git a/target/linux/ipq806x/patches-4.1/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-4.1/302-mtd-qcom-smem-rename-rootfs-ubi.patch deleted file mode 100644 index 471a87ba68..0000000000 --- a/target/linux/ipq806x/patches-4.1/302-mtd-qcom-smem-rename-rootfs-ubi.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- a/drivers/mtd/qcom_smem_part.c -+++ b/drivers/mtd/qcom_smem_part.c -@@ -192,6 +192,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 diff --git a/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch deleted file mode 100644 index dfe716953f..0000000000 --- a/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch +++ /dev/null @@ -1,734 +0,0 @@ -From 2fbb18f85826a9ba308fedb2cf90d3a661a39fd7 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 27 Mar 2015 00:16:14 -0700 -Subject: [PATCH] clk: qcom: Add support for NSS/GMAC clocks and resets - -Add the NSS/GMAC clocks and the TCM clock and NSS resets. - -Signed-off-by: Stephen Boyd ---- - drivers/clk/qcom/gcc-ipq806x.c | 594 ++++++++++++++++++++++++++- - drivers/clk/qcom/gcc-ipq806x.c.rej | 50 +++ - include/dt-bindings/clock/qcom,gcc-ipq806x.h | 2 + - include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++ - 4 files changed, 688 insertions(+), 1 deletion(-) - create mode 100644 drivers/clk/qcom/gcc-ipq806x.c.rej - ---- a/drivers/clk/qcom/gcc-ipq806x.c -+++ b/drivers/clk/qcom/gcc-ipq806x.c -@@ -220,12 +220,47 @@ static struct clk_regmap pll14_vote = { - }, - }; - -+#define NSS_PLL_RATE(f, _l, _m, _n, i) \ -+ { \ -+ .freq = f, \ -+ .l = _l, \ -+ .m = _m, \ -+ .n = _n, \ -+ .ibits = i, \ -+ } -+ -+static struct pll_freq_tbl pll18_freq_tbl[] = { -+ NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625), -+ NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625), -+}; -+ -+static struct clk_pll pll18 = { -+ .l_reg = 0x31a4, -+ .m_reg = 0x31a8, -+ .n_reg = 0x31ac, -+ .config_reg = 0x31b4, -+ .mode_reg = 0x31a0, -+ .status_reg = 0x31b8, -+ .status_bit = 16, -+ .post_div_shift = 16, -+ .post_div_width = 1, -+ .freq_tbl = pll18_freq_tbl, -+ .clkr.hw.init = &(struct clk_init_data){ -+ .name = "pll18", -+ .parent_names = (const char *[]){ "pxo" }, -+ .num_parents = 1, -+ .ops = &clk_pll_ops, -+ }, -+}; -+ - enum { - P_PXO, - P_PLL8, - P_PLL3, - P_PLL0, - P_CXO, -+ P_PLL14, -+ P_PLL18, - }; - - static const struct parent_map gcc_pxo_pll8_map[] = { -@@ -277,6 +312,22 @@ static const char *gcc_pxo_pll8_pll0_map - "pll0_vote", - }; - -+static const struct parent_map gcc_pxo_pll8_pll14_pll18_pll0_map[] = { -+ { P_PXO, 0 }, -+ { P_PLL8, 4 }, -+ { P_PLL0, 2 }, -+ { P_PLL14, 5 }, -+ { P_PLL18, 1 }, -+}; -+ -+static const char *gcc_pxo_pll8_pll14_pll18_pll0[] = { -+ "pxo", -+ "pll8_vote", -+ "pll0_vote", -+ "pll14", -+ "pll18", -+}; -+ - static struct freq_tbl clk_tbl_gsbi_uart[] = { - { 1843200, P_PLL8, 2, 6, 625 }, - { 3686400, P_PLL8, 2, 12, 625 }, -@@ -2282,6 +2333,472 @@ static struct clk_branch ebi2_aon_clk = - }, - }; - -+static const struct freq_tbl clk_tbl_gmac[] = { -+ { 133000000, P_PLL0, 1, 50, 301 }, -+ { 266000000, P_PLL0, 1, 127, 382 }, -+ { } -+}; -+ -+static struct clk_dyn_rcg gmac_core1_src = { -+ .ns_reg[0] = 0x3cac, -+ .ns_reg[1] = 0x3cb0, -+ .md_reg[0] = 0x3ca4, -+ .md_reg[1] = 0x3ca8, -+ .bank_reg = 0x3ca0, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3ca0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core1_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core1_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 4, -+ .hwcg_reg = 0x3cb4, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3cb4, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core1_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core1_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg gmac_core2_src = { -+ .ns_reg[0] = 0x3ccc, -+ .ns_reg[1] = 0x3cd0, -+ .md_reg[0] = 0x3cc4, -+ .md_reg[1] = 0x3cc8, -+ .bank_reg = 0x3ca0, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3cc0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core2_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core2_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 5, -+ .hwcg_reg = 0x3cd4, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3cd4, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core2_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core2_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg gmac_core3_src = { -+ .ns_reg[0] = 0x3cec, -+ .ns_reg[1] = 0x3cf0, -+ .md_reg[0] = 0x3ce4, -+ .md_reg[1] = 0x3ce8, -+ .bank_reg = 0x3ce0, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3ce0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core3_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core3_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 6, -+ .hwcg_reg = 0x3cf4, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3cf4, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core3_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core3_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg gmac_core4_src = { -+ .ns_reg[0] = 0x3d0c, -+ .ns_reg[1] = 0x3d10, -+ .md_reg[0] = 0x3d04, -+ .md_reg[1] = 0x3d08, -+ .bank_reg = 0x3d00, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_gmac, -+ .clkr = { -+ .enable_reg = 0x3d00, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core4_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch gmac_core4_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 7, -+ .hwcg_reg = 0x3d14, -+ .hwcg_bit = 6, -+ .clkr = { -+ .enable_reg = 0x3d14, -+ .enable_mask = BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "gmac_core4_clk", -+ .parent_names = (const char *[]){ -+ "gmac_core4_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static const struct freq_tbl clk_tbl_nss_tcm[] = { -+ { 266000000, P_PLL0, 3, 0, 0 }, -+ { 400000000, P_PLL0, 2, 0, 0 }, -+ { } -+}; -+ -+static struct clk_dyn_rcg nss_tcm_src = { -+ .ns_reg[0] = 0x3dc4, -+ .ns_reg[1] = 0x3dc8, -+ .bank_reg = 0x3dc0, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 4, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 4, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_nss_tcm, -+ .clkr = { -+ .enable_reg = 0x3dc0, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "nss_tcm_src", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ }, -+ }, -+}; -+ -+static struct clk_branch nss_tcm_clk = { -+ .halt_reg = 0x3c20, -+ .halt_bit = 14, -+ .clkr = { -+ .enable_reg = 0x3dd0, -+ .enable_mask = BIT(6) | BIT(4), -+ .hw.init = &(struct clk_init_data){ -+ .name = "nss_tcm_clk", -+ .parent_names = (const char *[]){ -+ "nss_tcm_src", -+ }, -+ .num_parents = 1, -+ .ops = &clk_branch_ops, -+ .flags = CLK_SET_RATE_PARENT, -+ }, -+ }, -+}; -+ -+static const struct freq_tbl clk_tbl_nss[] = { -+ { 110000000, P_PLL18, 1, 1, 5 }, -+ { 275000000, P_PLL18, 2, 0, 0 }, -+ { 550000000, P_PLL18, 1, 0, 0 }, -+ { 733000000, P_PLL18, 1, 0, 0 }, -+ { } -+}; -+ -+static struct clk_dyn_rcg ubi32_core1_src_clk = { -+ .ns_reg[0] = 0x3d2c, -+ .ns_reg[1] = 0x3d30, -+ .md_reg[0] = 0x3d24, -+ .md_reg[1] = 0x3d28, -+ .bank_reg = 0x3d20, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_nss, -+ .clkr = { -+ .enable_reg = 0x3d20, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "ubi32_core1_src_clk", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, -+ }, -+ }, -+}; -+ -+static struct clk_dyn_rcg ubi32_core2_src_clk = { -+ .ns_reg[0] = 0x3d4c, -+ .ns_reg[1] = 0x3d50, -+ .md_reg[0] = 0x3d44, -+ .md_reg[1] = 0x3d48, -+ .bank_reg = 0x3d40, -+ .mn[0] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .mn[1] = { -+ .mnctr_en_bit = 8, -+ .mnctr_reset_bit = 7, -+ .mnctr_mode_shift = 5, -+ .n_val_shift = 16, -+ .m_val_shift = 16, -+ .width = 8, -+ }, -+ .s[0] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .s[1] = { -+ .src_sel_shift = 0, -+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, -+ }, -+ .p[0] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .p[1] = { -+ .pre_div_shift = 3, -+ .pre_div_width = 2, -+ }, -+ .mux_sel_bit = 0, -+ .freq_tbl = clk_tbl_nss, -+ .clkr = { -+ .enable_reg = 0x3d40, -+ .enable_mask = BIT(1), -+ .hw.init = &(struct clk_init_data){ -+ .name = "ubi32_core2_src_clk", -+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, -+ .num_parents = 5, -+ .ops = &clk_dyn_rcg_ops, -+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, -+ }, -+ }, -+}; -+ - static struct clk_regmap *gcc_ipq806x_clks[] = { - [PLL0] = &pll0.clkr, - [PLL0_VOTE] = &pll0_vote, -@@ -2291,6 +2808,7 @@ static struct clk_regmap *gcc_ipq806x_cl - [PLL8_VOTE] = &pll8_vote, - [PLL14] = &pll14.clkr, - [PLL14_VOTE] = &pll14_vote, -+ [PLL18] = &pll18.clkr, - [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, - [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, - [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, -@@ -2390,6 +2908,18 @@ static struct clk_regmap *gcc_ipq806x_cl - [PLL9] = &hfpll0.clkr, - [PLL10] = &hfpll1.clkr, - [PLL12] = &hfpll_l2.clkr, -+ [GMAC_CORE1_CLK_SRC] = &gmac_core1_src.clkr, -+ [GMAC_CORE1_CLK] = &gmac_core1_clk.clkr, -+ [GMAC_CORE2_CLK_SRC] = &gmac_core2_src.clkr, -+ [GMAC_CORE2_CLK] = &gmac_core2_clk.clkr, -+ [GMAC_CORE3_CLK_SRC] = &gmac_core3_src.clkr, -+ [GMAC_CORE3_CLK] = &gmac_core3_clk.clkr, -+ [GMAC_CORE4_CLK_SRC] = &gmac_core4_src.clkr, -+ [GMAC_CORE4_CLK] = &gmac_core4_clk.clkr, -+ [UBI32_CORE1_CLK_SRC] = &ubi32_core1_src_clk.clkr, -+ [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, -+ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, -+ [NSSTCM_CLK] = &nss_tcm_clk.clkr, - }; - - static const struct qcom_reset_map gcc_ipq806x_resets[] = { -@@ -2508,6 +3038,48 @@ static const struct qcom_reset_map gcc_i - [USB30_1_PHY_RESET] = { 0x3b58, 0 }, - [NSSFB0_RESET] = { 0x3b60, 6 }, - [NSSFB1_RESET] = { 0x3b60, 7 }, -+ [UBI32_CORE1_CLKRST_CLAMP_RESET] = { 0x3d3c, 3}, -+ [UBI32_CORE1_CLAMP_RESET] = { 0x3d3c, 2 }, -+ [UBI32_CORE1_AHB_RESET] = { 0x3d3c, 1 }, -+ [UBI32_CORE1_AXI_RESET] = { 0x3d3c, 0 }, -+ [UBI32_CORE2_CLKRST_CLAMP_RESET] = { 0x3d5c, 3 }, -+ [UBI32_CORE2_CLAMP_RESET] = { 0x3d5c, 2 }, -+ [UBI32_CORE2_AHB_RESET] = { 0x3d5c, 1 }, -+ [UBI32_CORE2_AXI_RESET] = { 0x3d5c, 0 }, -+ [GMAC_CORE1_RESET] = { 0x3cbc, 0 }, -+ [GMAC_CORE2_RESET] = { 0x3cdc, 0 }, -+ [GMAC_CORE3_RESET] = { 0x3cfc, 0 }, -+ [GMAC_CORE4_RESET] = { 0x3d1c, 0 }, -+ [GMAC_AHB_RESET] = { 0x3e24, 0 }, -+ [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 }, -+ [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 }, -+ [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 }, -+ [NSS_CH0_HW_RST_RX_125M_N_RESET] = { 0x3b60, 3 }, -+ [NSS_CH0_RST_TX_125M_N_RESET] = { 0x3b60, 4 }, -+ [NSS_CH1_RST_RX_CLK_N_RESET] = { 0x3b60, 5 }, -+ [NSS_CH1_RST_TX_CLK_N_RESET] = { 0x3b60, 6 }, -+ [NSS_CH1_RST_RX_125M_N_RESET] = { 0x3b60, 7 }, -+ [NSS_CH1_HW_RST_RX_125M_N_RESET] = { 0x3b60, 8 }, -+ [NSS_CH1_RST_TX_125M_N_RESET] = { 0x3b60, 9 }, -+ [NSS_CH2_RST_RX_CLK_N_RESET] = { 0x3b60, 10 }, -+ [NSS_CH2_RST_TX_CLK_N_RESET] = { 0x3b60, 11 }, -+ [NSS_CH2_RST_RX_125M_N_RESET] = { 0x3b60, 12 }, -+ [NSS_CH2_HW_RST_RX_125M_N_RESET] = { 0x3b60, 13 }, -+ [NSS_CH2_RST_TX_125M_N_RESET] = { 0x3b60, 14 }, -+ [NSS_CH3_RST_RX_CLK_N_RESET] = { 0x3b60, 15 }, -+ [NSS_CH3_RST_TX_CLK_N_RESET] = { 0x3b60, 16 }, -+ [NSS_CH3_RST_RX_125M_N_RESET] = { 0x3b60, 17 }, -+ [NSS_CH3_HW_RST_RX_125M_N_RESET] = { 0x3b60, 18 }, -+ [NSS_CH3_RST_TX_125M_N_RESET] = { 0x3b60, 19 }, -+ [NSS_RST_RX_250M_125M_N_RESET] = { 0x3b60, 20 }, -+ [NSS_RST_TX_250M_125M_N_RESET] = { 0x3b60, 21 }, -+ [NSS_QSGMII_TXPI_RST_N_RESET] = { 0x3b60, 22 }, -+ [NSS_QSGMII_CDR_RST_N_RESET] = { 0x3b60, 23 }, -+ [NSS_SGMII2_CDR_RST_N_RESET] = { 0x3b60, 24 }, -+ [NSS_SGMII3_CDR_RST_N_RESET] = { 0x3b60, 25 }, -+ [NSS_CAL_PRBS_RST_N_RESET] = { 0x3b60, 26 }, -+ [NSS_LCKDT_RST_N_RESET] = { 0x3b60, 27 }, -+ [NSS_SRDS_N_RESET] = { 0x3b60, 28 }, - }; - - static const struct regmap_config gcc_ipq806x_regmap_config = { -@@ -2536,6 +3108,8 @@ static int gcc_ipq806x_probe(struct plat - { - struct clk *clk; - struct device *dev = &pdev->dev; -+ struct regmap *regmap; -+ int ret; - - /* Temporary until RPM clocks supported */ - clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); -@@ -2546,7 +3120,25 @@ static int gcc_ipq806x_probe(struct plat - if (IS_ERR(clk)) - return PTR_ERR(clk); - -- return qcom_cc_probe(pdev, &gcc_ipq806x_desc); -+ ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc); -+ if (ret) -+ return ret; -+ -+ regmap = dev_get_regmap(dev, NULL); -+ if (!regmap) -+ return -ENODEV; -+ -+ /* Setup PLL18 static bits */ -+ regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400); -+ regmap_write(regmap, 0x31b0, 0x3080); -+ -+ /* Set GMAC footswitch sleep/wakeup values */ -+ regmap_write(regmap, 0x3cb8, 8); -+ regmap_write(regmap, 0x3cd8, 8); -+ regmap_write(regmap, 0x3cf8, 8); -+ regmap_write(regmap, 0x3d18, 8); -+ -+ return 0; - } - - static int gcc_ipq806x_remove(struct platform_device *pdev) ---- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h -+++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h -@@ -129,4 +129,47 @@ - #define USB30_1_PHY_RESET 112 - #define NSSFB0_RESET 113 - #define NSSFB1_RESET 114 -+#define UBI32_CORE1_CLKRST_CLAMP_RESET 115 -+#define UBI32_CORE1_CLAMP_RESET 116 -+#define UBI32_CORE1_AHB_RESET 117 -+#define UBI32_CORE1_AXI_RESET 118 -+#define UBI32_CORE2_CLKRST_CLAMP_RESET 119 -+#define UBI32_CORE2_CLAMP_RESET 120 -+#define UBI32_CORE2_AHB_RESET 121 -+#define UBI32_CORE2_AXI_RESET 122 -+#define GMAC_CORE1_RESET 123 -+#define GMAC_CORE2_RESET 124 -+#define GMAC_CORE3_RESET 125 -+#define GMAC_CORE4_RESET 126 -+#define GMAC_AHB_RESET 127 -+#define NSS_CH0_RST_RX_CLK_N_RESET 128 -+#define NSS_CH0_RST_TX_CLK_N_RESET 129 -+#define NSS_CH0_RST_RX_125M_N_RESET 130 -+#define NSS_CH0_HW_RST_RX_125M_N_RESET 131 -+#define NSS_CH0_RST_TX_125M_N_RESET 132 -+#define NSS_CH1_RST_RX_CLK_N_RESET 133 -+#define NSS_CH1_RST_TX_CLK_N_RESET 134 -+#define NSS_CH1_RST_RX_125M_N_RESET 135 -+#define NSS_CH1_HW_RST_RX_125M_N_RESET 136 -+#define NSS_CH1_RST_TX_125M_N_RESET 137 -+#define NSS_CH2_RST_RX_CLK_N_RESET 138 -+#define NSS_CH2_RST_TX_CLK_N_RESET 139 -+#define NSS_CH2_RST_RX_125M_N_RESET 140 -+#define NSS_CH2_HW_RST_RX_125M_N_RESET 141 -+#define NSS_CH2_RST_TX_125M_N_RESET 142 -+#define NSS_CH3_RST_RX_CLK_N_RESET 143 -+#define NSS_CH3_RST_TX_CLK_N_RESET 144 -+#define NSS_CH3_RST_RX_125M_N_RESET 145 -+#define NSS_CH3_HW_RST_RX_125M_N_RESET 146 -+#define NSS_CH3_RST_TX_125M_N_RESET 147 -+#define NSS_RST_RX_250M_125M_N_RESET 148 -+#define NSS_RST_TX_250M_125M_N_RESET 149 -+#define NSS_QSGMII_TXPI_RST_N_RESET 150 -+#define NSS_QSGMII_CDR_RST_N_RESET 151 -+#define NSS_SGMII2_CDR_RST_N_RESET 152 -+#define NSS_SGMII3_CDR_RST_N_RESET 153 -+#define NSS_CAL_PRBS_RST_N_RESET 154 -+#define NSS_LCKDT_RST_N_RESET 155 -+#define NSS_SRDS_N_RESET 156 -+ - #endif ---- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h -+++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h -@@ -289,5 +289,7 @@ - #define UBI32_CORE1_CLK 279 - #define UBI32_CORE2_CLK 280 - #define EBI2_AON_CLK 281 -+#define NSSTCM_CLK_SRC 282 -+#define NSSTCM_CLK 283 - - #endif diff --git a/target/linux/ipq806x/patches-4.1/701-stmmac_update_to_4.3.patch b/target/linux/ipq806x/patches-4.1/701-stmmac_update_to_4.3.patch deleted file mode 100644 index 22450d4bbe..0000000000 --- a/target/linux/ipq806x/patches-4.1/701-stmmac_update_to_4.3.patch +++ /dev/null @@ -1,2355 +0,0 @@ ---- a/drivers/net/ethernet/stmicro/Kconfig -+++ b/drivers/net/ethernet/stmicro/Kconfig -@@ -7,9 +7,7 @@ config NET_VENDOR_STMICRO - default y - depends on HAS_IOMEM - ---help--- -- If you have a network (Ethernet) card belonging to this class, say Y -- and read the Ethernet-HOWTO, available from -- . -+ If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all ---- a/drivers/net/ethernet/stmicro/stmmac/Kconfig -+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig -@@ -16,6 +16,7 @@ if STMMAC_ETH - config STMMAC_PLATFORM - tristate "STMMAC Platform bus support" - depends on STMMAC_ETH -+ select MFD_SYSCON - default y - ---help--- - This selects the platform specific bus support for the stmmac driver. -@@ -26,6 +27,95 @@ config STMMAC_PLATFORM - - If unsure, say N. - -+if STMMAC_PLATFORM -+ -+config DWMAC_GENERIC -+ tristate "Generic driver for DWMAC" -+ default STMMAC_PLATFORM -+ ---help--- -+ Generic DWMAC driver for platforms that don't require any -+ platform specific code to function or is using platform -+ data for setup. -+ -+config DWMAC_IPQ806X -+ tristate "QCA IPQ806x DWMAC support" -+ default ARCH_QCOM -+ depends on OF -+ select MFD_SYSCON -+ help -+ Support for QCA IPQ806X DWMAC Ethernet. -+ -+ This selects the IPQ806x SoC glue layer support for the stmmac -+ device driver. This driver does not use any of the hardware -+ acceleration features available on this SoC. Network devices -+ will behave like standard non-accelerated ethernet interfaces. -+ -+config DWMAC_LPC18XX -+ tristate "NXP LPC18xx/43xx DWMAC support" -+ default ARCH_LPC18XX -+ depends on OF -+ select MFD_SYSCON -+ ---help--- -+ Support for NXP LPC18xx/43xx DWMAC Ethernet. -+ -+config DWMAC_MESON -+ tristate "Amlogic Meson dwmac support" -+ default ARCH_MESON -+ depends on OF -+ help -+ Support for Ethernet controller on Amlogic Meson SoCs. -+ -+ This selects the Amlogic Meson SoC glue layer support for -+ the stmmac device driver. This driver is used for Meson6 and -+ Meson8 SoCs. -+ -+config DWMAC_ROCKCHIP -+ tristate "Rockchip dwmac support" -+ default ARCH_ROCKCHIP -+ depends on OF -+ select MFD_SYSCON -+ help -+ Support for Ethernet controller on Rockchip RK3288 SoC. -+ -+ This selects the Rockchip RK3288 SoC glue layer support for -+ the stmmac device driver. -+ -+config DWMAC_SOCFPGA -+ tristate "SOCFPGA dwmac support" -+ default ARCH_SOCFPGA -+ depends on OF -+ select MFD_SYSCON -+ help -+ Support for ethernet controller on Altera SOCFPGA -+ -+ This selects the Altera SOCFPGA SoC glue layer support -+ for the stmmac device driver. This driver is used for -+ arria5 and cyclone5 FPGA SoCs. -+ -+config DWMAC_STI -+ tristate "STi GMAC support" -+ default ARCH_STI -+ depends on OF -+ select MFD_SYSCON -+ ---help--- -+ Support for ethernet controller on STi SOCs. -+ -+ This selects STi SoC glue layer support for the stmmac -+ device driver. This driver is used on for the STi series -+ SOCs GMAC ethernet controller. -+ -+config DWMAC_SUNXI -+ tristate "Allwinner GMAC support" -+ default ARCH_SUNXI -+ depends on OF -+ ---help--- -+ Support for Allwinner A20/A31 GMAC ethernet controllers. -+ -+ This selects Allwinner SoC glue layer support for the -+ stmmac device driver. This driver is used for A20/A31 -+ GMAC ethernet controller. -+endif -+ - config STMMAC_PCI - tristate "STMMAC PCI bus support" - depends on STMMAC_ETH && PCI ---- a/drivers/net/ethernet/stmicro/stmmac/Makefile -+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile -@@ -4,9 +4,17 @@ stmmac-objs:= stmmac_main.o stmmac_ethto - dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ - mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y) - --obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o --stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \ -- dwmac-sti.o dwmac-socfpga.o dwmac-rk.o -+# Ordering matters. Generic driver must be last. -+obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o -+obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o -+obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o -+obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o -+obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o -+obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o -+obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o -+obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o -+obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o -+stmmac-platform-objs:= stmmac_platform.o - - obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o - stmmac-pci-objs:= stmmac_pci.o ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c -@@ -0,0 +1,81 @@ -+/* -+ * Generic DWMAC platform driver -+ * -+ * Copyright (C) 2007-2011 STMicroelectronics Ltd -+ * Copyright (C) 2015 Joachim Eastwood -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+ -+#include "stmmac.h" -+#include "stmmac_platform.h" -+ -+static int dwmac_generic_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ if (pdev->dev.of_node) { -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) { -+ dev_err(&pdev->dev, "dt configuration failed\n"); -+ return PTR_ERR(plat_dat); -+ } -+ } else { -+ plat_dat = dev_get_platdata(&pdev->dev); -+ if (!plat_dat) { -+ dev_err(&pdev->dev, "no platform data provided\n"); -+ return -EINVAL; -+ } -+ -+ /* Set default value for multicast hash bins */ -+ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; -+ -+ /* Set default value for unicast filter entries */ -+ plat_dat->unicast_filter_entries = 1; -+ } -+ -+ /* Custom initialisation (if needed) */ -+ if (plat_dat->init) { -+ ret = plat_dat->init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ } -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id dwmac_generic_match[] = { -+ { .compatible = "st,spear600-gmac"}, -+ { .compatible = "snps,dwmac-3.610"}, -+ { .compatible = "snps,dwmac-3.70a"}, -+ { .compatible = "snps,dwmac-3.710"}, -+ { .compatible = "snps,dwmac"}, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, dwmac_generic_match); -+ -+static struct platform_driver dwmac_generic_driver = { -+ .probe = dwmac_generic_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = STMMAC_RESOURCE_NAME, -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = of_match_ptr(dwmac_generic_match), -+ }, -+}; -+module_platform_driver(dwmac_generic_driver); -+ -+MODULE_DESCRIPTION("Generic dwmac driver"); -+MODULE_LICENSE("GPL v2"); ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -@@ -0,0 +1,373 @@ -+/* -+ * Qualcomm Atheros IPQ806x GMAC glue layer -+ * -+ * Copyright (C) 2015 The Linux Foundation -+ * -+ * Permission to use, copy, modify, and/or distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "stmmac_platform.h" -+ -+#define NSS_COMMON_CLK_GATE 0x8 -+#define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x) -+#define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2)) -+#define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2)) -+#define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x) -+#define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x) -+ -+#define NSS_COMMON_CLK_DIV0 0xC -+#define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8) -+#define NSS_COMMON_CLK_DIV_MASK 0x7f -+ -+#define NSS_COMMON_CLK_SRC_CTRL 0x14 -+#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (x) -+/* Mode is coded on 1 bit but is different depending on the MAC ID: -+ * MAC0: QSGMII=0 RGMII=1 -+ * MAC1: QSGMII=0 SGMII=0 RGMII=1 -+ * MAC2 & MAC3: QSGMII=0 SGMII=1 -+ */ -+#define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1 -+#define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0) -+ -+#define NSS_COMMON_MACSEC_CTL 0x28 -+#define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x) -+ -+#define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4)) -+#define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19) -+#define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16) -+#define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8 -+#define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0 -+#define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f -+ -+#define NSS_COMMON_CLK_DIV_RGMII_1000 1 -+#define NSS_COMMON_CLK_DIV_RGMII_100 9 -+#define NSS_COMMON_CLK_DIV_RGMII_10 99 -+#define NSS_COMMON_CLK_DIV_SGMII_1000 0 -+#define NSS_COMMON_CLK_DIV_SGMII_100 4 -+#define NSS_COMMON_CLK_DIV_SGMII_10 49 -+ -+#define QSGMII_PCS_MODE_CTL 0x68 -+#define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7) -+ -+#define QSGMII_PCS_CAL_LCKDT_CTL 0x120 -+#define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19) -+ -+/* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */ -+#define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \ -+ (0x13c + (4 * (x - 2)))) -+#define QSGMII_PHY_CDR_EN BIT(0) -+#define QSGMII_PHY_RX_FRONT_EN BIT(1) -+#define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2) -+#define QSGMII_PHY_TX_DRIVER_EN BIT(3) -+#define QSGMII_PHY_QSGMII_EN BIT(7) -+#define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12 -+#define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7 -+#define QSGMII_PHY_RX_DC_BIAS_OFFSET 18 -+#define QSGMII_PHY_RX_DC_BIAS_MASK 0x3 -+#define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20 -+#define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3 -+#define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22 -+#define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3 -+#define QSGMII_PHY_TX_DRV_AMP_OFFSET 28 -+#define QSGMII_PHY_TX_DRV_AMP_MASK 0xf -+ -+struct ipq806x_gmac { -+ struct platform_device *pdev; -+ struct regmap *nss_common; -+ struct regmap *qsgmii_csr; -+ uint32_t id; -+ struct clk *core_clk; -+ phy_interface_t phy_mode; -+}; -+ -+static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed) -+{ -+ struct device *dev = &gmac->pdev->dev; -+ int div; -+ -+ switch (speed) { -+ case SPEED_1000: -+ div = NSS_COMMON_CLK_DIV_SGMII_1000; -+ break; -+ -+ case SPEED_100: -+ div = NSS_COMMON_CLK_DIV_SGMII_100; -+ break; -+ -+ case SPEED_10: -+ div = NSS_COMMON_CLK_DIV_SGMII_10; -+ break; -+ -+ default: -+ dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed); -+ return -EINVAL; -+ } -+ -+ return div; -+} -+ -+static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed) -+{ -+ struct device *dev = &gmac->pdev->dev; -+ int div; -+ -+ switch (speed) { -+ case SPEED_1000: -+ div = NSS_COMMON_CLK_DIV_RGMII_1000; -+ break; -+ -+ case SPEED_100: -+ div = NSS_COMMON_CLK_DIV_RGMII_100; -+ break; -+ -+ case SPEED_10: -+ div = NSS_COMMON_CLK_DIV_RGMII_10; -+ break; -+ -+ default: -+ dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed); -+ return -EINVAL; -+ } -+ -+ return div; -+} -+ -+static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed) -+{ -+ uint32_t clk_bits, val; -+ int div; -+ -+ switch (gmac->phy_mode) { -+ case PHY_INTERFACE_MODE_RGMII: -+ div = get_clk_div_rgmii(gmac, speed); -+ clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) | -+ NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id); -+ break; -+ -+ case PHY_INTERFACE_MODE_SGMII: -+ div = get_clk_div_sgmii(gmac, speed); -+ clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) | -+ NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id); -+ break; -+ -+ default: -+ dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n", -+ phy_modes(gmac->phy_mode)); -+ return -EINVAL; -+ } -+ -+ /* Disable the clocks */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); -+ val &= ~clk_bits; -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); -+ -+ /* Set the divider */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val); -+ val &= ~(NSS_COMMON_CLK_DIV_MASK -+ << NSS_COMMON_CLK_DIV_OFFSET(gmac->id)); -+ val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id); -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val); -+ -+ /* Enable the clock back */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); -+ val |= clk_bits; -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); -+ -+ return 0; -+} -+ -+static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) -+{ -+ struct device *dev = &gmac->pdev->dev; -+ -+ gmac->phy_mode = of_get_phy_mode(dev->of_node); -+ if (gmac->phy_mode < 0) { -+ dev_err(dev, "missing phy mode property\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) { -+ dev_err(dev, "missing qcom id property\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ /* The GMACs are called 1 to 4 in the documentation, but to simplify the -+ * code and keep it consistent with the Linux convention, we'll number -+ * them from 0 to 3 here. -+ */ -+ if (gmac->id < 0 || gmac->id > 3) { -+ dev_err(dev, "invalid gmac id\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ gmac->core_clk = devm_clk_get(dev, "stmmaceth"); -+ if (IS_ERR(gmac->core_clk)) { -+ dev_err(dev, "missing stmmaceth clk property\n"); -+ return gmac->core_clk; -+ } -+ clk_set_rate(gmac->core_clk, 266000000); -+ -+ /* Setup the register map for the nss common registers */ -+ gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "qcom,nss-common"); -+ if (IS_ERR(gmac->nss_common)) { -+ dev_err(dev, "missing nss-common node\n"); -+ return gmac->nss_common; -+ } -+ -+ /* Setup the register map for the qsgmii csr registers */ -+ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "qcom,qsgmii-csr"); -+ if (IS_ERR(gmac->qsgmii_csr)) { -+ dev_err(dev, "missing qsgmii-csr node\n"); -+ return gmac->qsgmii_csr; -+ } -+ -+ return NULL; -+} -+ -+static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed) -+{ -+ struct ipq806x_gmac *gmac = priv; -+ -+ ipq806x_gmac_set_speed(gmac, speed); -+} -+ -+static int ipq806x_gmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct device *dev = &pdev->dev; -+ struct ipq806x_gmac *gmac; -+ int val; -+ void *err; -+ -+ val = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (val) -+ return val; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); -+ if (!gmac) -+ return -ENOMEM; -+ -+ gmac->pdev = pdev; -+ -+ err = ipq806x_gmac_of_parse(gmac); -+ if (IS_ERR(err)) { -+ dev_err(dev, "device tree parsing error\n"); -+ return PTR_ERR(err); -+ } -+ -+ regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL, -+ QSGMII_PCS_CAL_LCKDT_CTL_RST); -+ -+ /* Inter frame gap is set to 12 */ -+ val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET | -+ 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET; -+ /* We also initiate an AXI low power exit request */ -+ val |= NSS_COMMON_GMAC_CTL_CSYS_REQ; -+ switch (gmac->phy_mode) { -+ case PHY_INTERFACE_MODE_RGMII: -+ val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; -+ break; -+ case PHY_INTERFACE_MODE_SGMII: -+ val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; -+ break; -+ default: -+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", -+ phy_modes(gmac->phy_mode)); -+ return -EINVAL; -+ } -+ regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val); -+ -+ /* Configure the clock src according to the mode */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val); -+ val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id)); -+ switch (gmac->phy_mode) { -+ case PHY_INTERFACE_MODE_RGMII: -+ val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) << -+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); -+ break; -+ case PHY_INTERFACE_MODE_SGMII: -+ val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) << -+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); -+ break; -+ default: -+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", -+ phy_modes(gmac->phy_mode)); -+ return -EINVAL; -+ } -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val); -+ -+ /* Enable PTP clock */ -+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); -+ val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id); -+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); -+ -+ if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) { -+ regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id), -+ QSGMII_PHY_CDR_EN | -+ QSGMII_PHY_RX_FRONT_EN | -+ QSGMII_PHY_RX_SIGNAL_DETECT_EN | -+ QSGMII_PHY_TX_DRIVER_EN | -+ QSGMII_PHY_QSGMII_EN | -+ 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET | -+ 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET | -+ 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET | -+ 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET | -+ 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET); -+ } -+ -+ plat_dat->has_gmac = true; -+ plat_dat->bsp_priv = gmac; -+ plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id ipq806x_gmac_dwmac_match[] = { -+ { .compatible = "qcom,ipq806x-gmac" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, ipq806x_gmac_dwmac_match); -+ -+static struct platform_driver ipq806x_gmac_dwmac_driver = { -+ .probe = ipq806x_gmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "ipq806x-gmac-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = ipq806x_gmac_dwmac_match, -+ }, -+}; -+module_platform_driver(ipq806x_gmac_dwmac_driver); -+ -+MODULE_AUTHOR("Mathieu Olivari "); -+MODULE_DESCRIPTION("Qualcomm Atheros IPQ806x DWMAC specific glue layer"); -+MODULE_LICENSE("Dual BSD/GPL"); ---- /dev/null -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c -@@ -0,0 +1,86 @@ -+/* -+ * DWMAC glue for NXP LPC18xx/LPC43xx Ethernet -+ * -+ * Copyright (C) 2015 Joachim Eastwood -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "stmmac_platform.h" -+ -+/* Register defines for CREG syscon */ -+#define LPC18XX_CREG_CREG6 0x12c -+# define LPC18XX_CREG_CREG6_ETHMODE_MASK 0x7 -+# define LPC18XX_CREG_CREG6_ETHMODE_MII 0x0 -+# define LPC18XX_CREG_CREG6_ETHMODE_RMII 0x4 -+ -+static int lpc18xx_dwmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct regmap *reg; -+ u8 ethmode; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ plat_dat->has_gmac = true; -+ -+ reg = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg"); -+ if (IS_ERR(reg)) { -+ dev_err(&pdev->dev, "syscon lookup failed\n"); -+ return PTR_ERR(reg); -+ } -+ -+ if (plat_dat->interface == PHY_INTERFACE_MODE_MII) { -+ ethmode = LPC18XX_CREG_CREG6_ETHMODE_MII; -+ } else if (plat_dat->interface == PHY_INTERFACE_MODE_RMII) { -+ ethmode = LPC18XX_CREG_CREG6_ETHMODE_RMII; -+ } else { -+ dev_err(&pdev->dev, "Only MII and RMII mode supported\n"); -+ return -EINVAL; -+ } -+ -+ regmap_update_bits(reg, LPC18XX_CREG_CREG6, -+ LPC18XX_CREG_CREG6_ETHMODE_MASK, ethmode); -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id lpc18xx_dwmac_match[] = { -+ { .compatible = "nxp,lpc1850-dwmac" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, lpc18xx_dwmac_match); -+ -+static struct platform_driver lpc18xx_dwmac_driver = { -+ .probe = lpc18xx_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "lpc18xx-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = lpc18xx_dwmac_match, -+ }, -+}; -+module_platform_driver(lpc18xx_dwmac_driver); -+ -+MODULE_AUTHOR("Joachim Eastwood "); -+MODULE_DESCRIPTION("DWMAC glue for LPC18xx/43xx Ethernet"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -46,24 +47,54 @@ static void meson6_dwmac_fix_mac_speed(v - writel(val, dwmac->reg); - } - --static void *meson6_dwmac_setup(struct platform_device *pdev) -+static int meson6_dwmac_probe(struct platform_device *pdev) - { -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; - struct meson_dwmac *dwmac; - struct resource *res; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); - - dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); - if (!dwmac) -- return ERR_PTR(-ENOMEM); -+ return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - dwmac->reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dwmac->reg)) -- return ERR_CAST(dwmac->reg); -+ return PTR_ERR(dwmac->reg); -+ -+ plat_dat->bsp_priv = dwmac; -+ plat_dat->fix_mac_speed = meson6_dwmac_fix_mac_speed; - -- return dwmac; -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - } - --const struct stmmac_of_data meson6_dwmac_data = { -- .setup = meson6_dwmac_setup, -- .fix_mac_speed = meson6_dwmac_fix_mac_speed, -+static const struct of_device_id meson6_dwmac_match[] = { -+ { .compatible = "amlogic,meson6-dwmac" }, -+ { } - }; -+MODULE_DEVICE_TABLE(of, meson6_dwmac_match); -+ -+static struct platform_driver meson6_dwmac_driver = { -+ .probe = meson6_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "meson6-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = meson6_dwmac_match, -+ }, -+}; -+module_platform_driver(meson6_dwmac_driver); -+ -+MODULE_AUTHOR("Beniamino Galvani "); -+MODULE_DESCRIPTION("Amlogic Meson DWMAC glue layer"); -+MODULE_LICENSE("GPL v2"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c -@@ -22,17 +22,31 @@ - #include - #include - #include -+#include - #include - #include -+#include - #include - #include - #include - #include - -+#include "stmmac_platform.h" -+ -+struct rk_priv_data; -+struct rk_gmac_ops { -+ void (*set_to_rgmii)(struct rk_priv_data *bsp_priv, -+ int tx_delay, int rx_delay); -+ void (*set_to_rmii)(struct rk_priv_data *bsp_priv); -+ void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed); -+ void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed); -+}; -+ - struct rk_priv_data { - struct platform_device *pdev; - int phy_iface; - struct regulator *regulator; -+ const struct rk_gmac_ops *ops; - - bool clk_enabled; - bool clock_input; -@@ -60,103 +74,228 @@ struct rk_priv_data { - - #define RK3288_GRF_SOC_CON1 0x0248 - #define RK3288_GRF_SOC_CON3 0x0250 --#define RK3288_GRF_GPIO3D_E 0x01ec --#define RK3288_GRF_GPIO4A_E 0x01f0 --#define RK3288_GRF_GPIO4B_E 0x01f4 - - /*RK3288_GRF_SOC_CON1*/ --#define GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | GRF_CLR_BIT(8)) --#define GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | GRF_BIT(8)) --#define GMAC_FLOW_CTRL GRF_BIT(9) --#define GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9) --#define GMAC_SPEED_10M GRF_CLR_BIT(10) --#define GMAC_SPEED_100M GRF_BIT(10) --#define GMAC_RMII_CLK_25M GRF_BIT(11) --#define GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11) --#define GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13)) --#define GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13)) --#define GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13)) --#define GMAC_RMII_MODE GRF_BIT(14) --#define GMAC_RMII_MODE_CLR GRF_CLR_BIT(14) -+#define RK3288_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | \ -+ GRF_CLR_BIT(8)) -+#define RK3288_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | \ -+ GRF_BIT(8)) -+#define RK3288_GMAC_FLOW_CTRL GRF_BIT(9) -+#define RK3288_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9) -+#define RK3288_GMAC_SPEED_10M GRF_CLR_BIT(10) -+#define RK3288_GMAC_SPEED_100M GRF_BIT(10) -+#define RK3288_GMAC_RMII_CLK_25M GRF_BIT(11) -+#define RK3288_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11) -+#define RK3288_GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13)) -+#define RK3288_GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13)) -+#define RK3288_GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13)) -+#define RK3288_GMAC_RMII_MODE GRF_BIT(14) -+#define RK3288_GMAC_RMII_MODE_CLR GRF_CLR_BIT(14) - - /*RK3288_GRF_SOC_CON3*/ --#define GMAC_TXCLK_DLY_ENABLE GRF_BIT(14) --#define GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14) --#define GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) --#define GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) --#define GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) --#define GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) -+#define RK3288_GMAC_TXCLK_DLY_ENABLE GRF_BIT(14) -+#define RK3288_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14) -+#define RK3288_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) -+#define RK3288_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) -+#define RK3288_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) -+#define RK3288_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) - --static void set_to_rgmii(struct rk_priv_data *bsp_priv, -- int tx_delay, int rx_delay) -+static void rk3288_set_to_rgmii(struct rk_priv_data *bsp_priv, -+ int tx_delay, int rx_delay) - { - struct device *dev = &bsp_priv->pdev->dev; - - if (IS_ERR(bsp_priv->grf)) { -- dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ dev_err(dev, "Missing rockchip,grf property\n"); - return; - } - - regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -- GMAC_PHY_INTF_SEL_RGMII | GMAC_RMII_MODE_CLR); -+ RK3288_GMAC_PHY_INTF_SEL_RGMII | -+ RK3288_GMAC_RMII_MODE_CLR); - regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3, -- GMAC_RXCLK_DLY_ENABLE | GMAC_TXCLK_DLY_ENABLE | -- GMAC_CLK_RX_DL_CFG(rx_delay) | -- GMAC_CLK_TX_DL_CFG(tx_delay)); -+ RK3288_GMAC_RXCLK_DLY_ENABLE | -+ RK3288_GMAC_TXCLK_DLY_ENABLE | -+ RK3288_GMAC_CLK_RX_DL_CFG(rx_delay) | -+ RK3288_GMAC_CLK_TX_DL_CFG(tx_delay)); - } - --static void set_to_rmii(struct rk_priv_data *bsp_priv) -+static void rk3288_set_to_rmii(struct rk_priv_data *bsp_priv) - { - struct device *dev = &bsp_priv->pdev->dev; - - if (IS_ERR(bsp_priv->grf)) { -- dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ dev_err(dev, "Missing rockchip,grf property\n"); - return; - } - - regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -- GMAC_PHY_INTF_SEL_RMII | GMAC_RMII_MODE); -+ RK3288_GMAC_PHY_INTF_SEL_RMII | RK3288_GMAC_RMII_MODE); - } - --static void set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) -+static void rk3288_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) - { - struct device *dev = &bsp_priv->pdev->dev; - - if (IS_ERR(bsp_priv->grf)) { -- dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ dev_err(dev, "Missing rockchip,grf property\n"); - return; - } - - if (speed == 10) -- regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_2_5M); -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_CLK_2_5M); - else if (speed == 100) -- regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_25M); -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_CLK_25M); - else if (speed == 1000) -- regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_125M); -+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -+ RK3288_GMAC_CLK_125M); - else - dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); - } - --static void set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) -+static void rk3288_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) - { - struct device *dev = &bsp_priv->pdev->dev; - - if (IS_ERR(bsp_priv->grf)) { -- dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ dev_err(dev, "Missing rockchip,grf property\n"); - return; - } - - if (speed == 10) { - regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -- GMAC_RMII_CLK_2_5M | GMAC_SPEED_10M); -+ RK3288_GMAC_RMII_CLK_2_5M | -+ RK3288_GMAC_SPEED_10M); - } else if (speed == 100) { - regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, -- GMAC_RMII_CLK_25M | GMAC_SPEED_100M); -+ RK3288_GMAC_RMII_CLK_25M | -+ RK3288_GMAC_SPEED_100M); -+ } else { -+ dev_err(dev, "unknown speed value for RMII! speed=%d", speed); -+ } -+} -+ -+static const struct rk_gmac_ops rk3288_ops = { -+ .set_to_rgmii = rk3288_set_to_rgmii, -+ .set_to_rmii = rk3288_set_to_rmii, -+ .set_rgmii_speed = rk3288_set_rgmii_speed, -+ .set_rmii_speed = rk3288_set_rmii_speed, -+}; -+ -+#define RK3368_GRF_SOC_CON15 0x043c -+#define RK3368_GRF_SOC_CON16 0x0440 -+ -+/* RK3368_GRF_SOC_CON15 */ -+#define RK3368_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(9) | GRF_CLR_BIT(10) | \ -+ GRF_CLR_BIT(11)) -+#define RK3368_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \ -+ GRF_BIT(11)) -+#define RK3368_GMAC_FLOW_CTRL GRF_BIT(8) -+#define RK3368_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(8) -+#define RK3368_GMAC_SPEED_10M GRF_CLR_BIT(7) -+#define RK3368_GMAC_SPEED_100M GRF_BIT(7) -+#define RK3368_GMAC_RMII_CLK_25M GRF_BIT(3) -+#define RK3368_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(3) -+#define RK3368_GMAC_CLK_125M (GRF_CLR_BIT(4) | GRF_CLR_BIT(5)) -+#define RK3368_GMAC_CLK_25M (GRF_BIT(4) | GRF_BIT(5)) -+#define RK3368_GMAC_CLK_2_5M (GRF_CLR_BIT(4) | GRF_BIT(5)) -+#define RK3368_GMAC_RMII_MODE GRF_BIT(6) -+#define RK3368_GMAC_RMII_MODE_CLR GRF_CLR_BIT(6) -+ -+/* RK3368_GRF_SOC_CON16 */ -+#define RK3368_GMAC_TXCLK_DLY_ENABLE GRF_BIT(7) -+#define RK3368_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7) -+#define RK3368_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) -+#define RK3368_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) -+#define RK3368_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) -+#define RK3368_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) -+ -+static void rk3368_set_to_rgmii(struct rk_priv_data *bsp_priv, -+ int tx_delay, int rx_delay) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_PHY_INTF_SEL_RGMII | -+ RK3368_GMAC_RMII_MODE_CLR); -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON16, -+ RK3368_GMAC_RXCLK_DLY_ENABLE | -+ RK3368_GMAC_TXCLK_DLY_ENABLE | -+ RK3368_GMAC_CLK_RX_DL_CFG(rx_delay) | -+ RK3368_GMAC_CLK_TX_DL_CFG(tx_delay)); -+} -+ -+static void rk3368_set_to_rmii(struct rk_priv_data *bsp_priv) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_PHY_INTF_SEL_RMII | RK3368_GMAC_RMII_MODE); -+} -+ -+static void rk3368_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ if (speed == 10) -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_CLK_2_5M); -+ else if (speed == 100) -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_CLK_25M); -+ else if (speed == 1000) -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_CLK_125M); -+ else -+ dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); -+} -+ -+static void rk3368_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) -+{ -+ struct device *dev = &bsp_priv->pdev->dev; -+ -+ if (IS_ERR(bsp_priv->grf)) { -+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); -+ return; -+ } -+ -+ if (speed == 10) { -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_RMII_CLK_2_5M | -+ RK3368_GMAC_SPEED_10M); -+ } else if (speed == 100) { -+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15, -+ RK3368_GMAC_RMII_CLK_25M | -+ RK3368_GMAC_SPEED_100M); - } else { - dev_err(dev, "unknown speed value for RMII! speed=%d", speed); - } - } - -+static const struct rk_gmac_ops rk3368_ops = { -+ .set_to_rgmii = rk3368_set_to_rgmii, -+ .set_to_rmii = rk3368_set_to_rmii, -+ .set_rgmii_speed = rk3368_set_rgmii_speed, -+ .set_rmii_speed = rk3368_set_rmii_speed, -+}; -+ - static int gmac_clk_init(struct rk_priv_data *bsp_priv) - { - struct device *dev = &bsp_priv->pdev->dev; -@@ -165,46 +304,46 @@ static int gmac_clk_init(struct rk_priv_ - - bsp_priv->mac_clk_rx = devm_clk_get(dev, "mac_clk_rx"); - if (IS_ERR(bsp_priv->mac_clk_rx)) -- dev_err(dev, "%s: cannot get clock %s\n", -- __func__, "mac_clk_rx"); -+ dev_err(dev, "cannot get clock %s\n", -+ "mac_clk_rx"); - - bsp_priv->mac_clk_tx = devm_clk_get(dev, "mac_clk_tx"); - if (IS_ERR(bsp_priv->mac_clk_tx)) -- dev_err(dev, "%s: cannot get clock %s\n", -- __func__, "mac_clk_tx"); -+ dev_err(dev, "cannot get clock %s\n", -+ "mac_clk_tx"); - - bsp_priv->aclk_mac = devm_clk_get(dev, "aclk_mac"); - if (IS_ERR(bsp_priv->aclk_mac)) -- dev_err(dev, "%s: cannot get clock %s\n", -- __func__, "aclk_mac"); -+ dev_err(dev, "cannot get clock %s\n", -+ "aclk_mac"); - - bsp_priv->pclk_mac = devm_clk_get(dev, "pclk_mac"); - if (IS_ERR(bsp_priv->pclk_mac)) -- dev_err(dev, "%s: cannot get clock %s\n", -- __func__, "pclk_mac"); -+ dev_err(dev, "cannot get clock %s\n", -+ "pclk_mac"); - - bsp_priv->clk_mac = devm_clk_get(dev, "stmmaceth"); - if (IS_ERR(bsp_priv->clk_mac)) -- dev_err(dev, "%s: cannot get clock %s\n", -- __func__, "stmmaceth"); -+ dev_err(dev, "cannot get clock %s\n", -+ "stmmaceth"); - - if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { - bsp_priv->clk_mac_ref = devm_clk_get(dev, "clk_mac_ref"); - if (IS_ERR(bsp_priv->clk_mac_ref)) -- dev_err(dev, "%s: cannot get clock %s\n", -- __func__, "clk_mac_ref"); -+ dev_err(dev, "cannot get clock %s\n", -+ "clk_mac_ref"); - - if (!bsp_priv->clock_input) { - bsp_priv->clk_mac_refout = - devm_clk_get(dev, "clk_mac_refout"); - if (IS_ERR(bsp_priv->clk_mac_refout)) -- dev_err(dev, "%s: cannot get clock %s\n", -- __func__, "clk_mac_refout"); -+ dev_err(dev, "cannot get clock %s\n", -+ "clk_mac_refout"); - } - } - - if (bsp_priv->clock_input) { -- dev_info(dev, "%s: clock input from PHY\n", __func__); -+ dev_info(dev, "clock input from PHY\n"); - } else { - if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) - clk_set_rate(bsp_priv->clk_mac, 50000000); -@@ -291,26 +430,25 @@ static int phy_power_on(struct rk_priv_d - struct device *dev = &bsp_priv->pdev->dev; - - if (!ldo) { -- dev_err(dev, "%s: no regulator found\n", __func__); -+ dev_err(dev, "no regulator found\n"); - return -1; - } - - if (enable) { - ret = regulator_enable(ldo); - if (ret) -- dev_err(dev, "%s: fail to enable phy-supply\n", -- __func__); -+ dev_err(dev, "fail to enable phy-supply\n"); - } else { - ret = regulator_disable(ldo); - if (ret) -- dev_err(dev, "%s: fail to disable phy-supply\n", -- __func__); -+ dev_err(dev, "fail to disable phy-supply\n"); - } - - return 0; - } - --static void *rk_gmac_setup(struct platform_device *pdev) -+static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, -+ const struct rk_gmac_ops *ops) - { - struct rk_priv_data *bsp_priv; - struct device *dev = &pdev->dev; -@@ -323,6 +461,7 @@ static void *rk_gmac_setup(struct platfo - return ERR_PTR(-ENOMEM); - - bsp_priv->phy_iface = of_get_phy_mode(dev->of_node); -+ bsp_priv->ops = ops; - - bsp_priv->regulator = devm_regulator_get_optional(dev, "phy"); - if (IS_ERR(bsp_priv->regulator)) { -@@ -336,12 +475,11 @@ static void *rk_gmac_setup(struct platfo - - ret = of_property_read_string(dev->of_node, "clock_in_out", &strings); - if (ret) { -- dev_err(dev, "%s: Can not read property: clock_in_out.\n", -- __func__); -+ dev_err(dev, "Can not read property: clock_in_out.\n"); - bsp_priv->clock_input = true; - } else { -- dev_info(dev, "%s: clock input or output? (%s).\n", -- __func__, strings); -+ dev_info(dev, "clock input or output? (%s).\n", -+ strings); - if (!strcmp(strings, "input")) - bsp_priv->clock_input = true; - else -@@ -351,22 +489,22 @@ static void *rk_gmac_setup(struct platfo - ret = of_property_read_u32(dev->of_node, "tx_delay", &value); - if (ret) { - bsp_priv->tx_delay = 0x30; -- dev_err(dev, "%s: Can not read property: tx_delay.", __func__); -- dev_err(dev, "%s: set tx_delay to 0x%x\n", -- __func__, bsp_priv->tx_delay); -+ dev_err(dev, "Can not read property: tx_delay."); -+ dev_err(dev, "set tx_delay to 0x%x\n", -+ bsp_priv->tx_delay); - } else { -- dev_info(dev, "%s: TX delay(0x%x).\n", __func__, value); -+ dev_info(dev, "TX delay(0x%x).\n", value); - bsp_priv->tx_delay = value; - } - - ret = of_property_read_u32(dev->of_node, "rx_delay", &value); - if (ret) { - bsp_priv->rx_delay = 0x10; -- dev_err(dev, "%s: Can not read property: rx_delay.", __func__); -- dev_err(dev, "%s: set rx_delay to 0x%x\n", -- __func__, bsp_priv->rx_delay); -+ dev_err(dev, "Can not read property: rx_delay."); -+ dev_err(dev, "set rx_delay to 0x%x\n", -+ bsp_priv->rx_delay); - } else { -- dev_info(dev, "%s: RX delay(0x%x).\n", __func__, value); -+ dev_info(dev, "RX delay(0x%x).\n", value); - bsp_priv->rx_delay = value; - } - -@@ -376,13 +514,14 @@ static void *rk_gmac_setup(struct platfo - - /*rmii or rgmii*/ - if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) { -- dev_info(dev, "%s: init for RGMII\n", __func__); -- set_to_rgmii(bsp_priv, bsp_priv->tx_delay, bsp_priv->rx_delay); -+ dev_info(dev, "init for RGMII\n"); -+ bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay, -+ bsp_priv->rx_delay); - } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { -- dev_info(dev, "%s: init for RMII\n", __func__); -- set_to_rmii(bsp_priv); -+ dev_info(dev, "init for RMII\n"); -+ bsp_priv->ops->set_to_rmii(bsp_priv); - } else { -- dev_err(dev, "%s: NO interface defined!\n", __func__); -+ dev_err(dev, "NO interface defined!\n"); - } - - gmac_clk_init(bsp_priv); -@@ -420,17 +559,68 @@ static void rk_fix_speed(void *priv, uns - struct device *dev = &bsp_priv->pdev->dev; - - if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) -- set_rgmii_speed(bsp_priv, speed); -+ bsp_priv->ops->set_rgmii_speed(bsp_priv, speed); - else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) -- set_rmii_speed(bsp_priv, speed); -+ bsp_priv->ops->set_rmii_speed(bsp_priv, speed); - else - dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface); - } - --const struct stmmac_of_data rk3288_gmac_data = { -- .has_gmac = 1, -- .fix_mac_speed = rk_fix_speed, -- .setup = rk_gmac_setup, -- .init = rk_gmac_init, -- .exit = rk_gmac_exit, -+static int rk_gmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ const struct rk_gmac_ops *data; -+ int ret; -+ -+ data = of_device_get_match_data(&pdev->dev); -+ if (!data) { -+ dev_err(&pdev->dev, "no of match data provided\n"); -+ return -EINVAL; -+ } -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ plat_dat->has_gmac = true; -+ plat_dat->init = rk_gmac_init; -+ plat_dat->exit = rk_gmac_exit; -+ plat_dat->fix_mac_speed = rk_fix_speed; -+ -+ plat_dat->bsp_priv = rk_gmac_setup(pdev, data); -+ if (IS_ERR(plat_dat->bsp_priv)) -+ return PTR_ERR(plat_dat->bsp_priv); -+ -+ ret = rk_gmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id rk_gmac_dwmac_match[] = { -+ { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops }, -+ { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, -+ { } - }; -+MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); -+ -+static struct platform_driver rk_gmac_dwmac_driver = { -+ .probe = rk_gmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "rk_gmac-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = rk_gmac_dwmac_match, -+ }, -+}; -+module_platform_driver(rk_gmac_dwmac_driver); -+ -+MODULE_AUTHOR("Chen-Zhi (Roger Chen) "); -+MODULE_DESCRIPTION("Rockchip RK3288 DWMAC specific glue layer"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c -@@ -175,31 +175,6 @@ static int socfpga_dwmac_setup(struct so - return 0; - } - --static void *socfpga_dwmac_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- int ret; -- struct socfpga_dwmac *dwmac; -- -- dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL); -- if (!dwmac) -- return ERR_PTR(-ENOMEM); -- -- ret = socfpga_dwmac_parse_data(dwmac, dev); -- if (ret) { -- dev_err(dev, "Unable to parse OF data\n"); -- return ERR_PTR(ret); -- } -- -- ret = socfpga_dwmac_setup(dwmac); -- if (ret) { -- dev_err(dev, "couldn't setup SoC glue (%d)\n", ret); -- return ERR_PTR(ret); -- } -- -- return dwmac; --} -- - static void socfpga_dwmac_exit(struct platform_device *pdev, void *priv) - { - struct socfpga_dwmac *dwmac = priv; -@@ -257,9 +232,65 @@ static int socfpga_dwmac_init(struct pla - return ret; - } - --const struct stmmac_of_data socfpga_gmac_data = { -- .setup = socfpga_dwmac_probe, -- .init = socfpga_dwmac_init, -- .exit = socfpga_dwmac_exit, -- .fix_mac_speed = socfpga_dwmac_fix_mac_speed, -+static int socfpga_dwmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct device *dev = &pdev->dev; -+ int ret; -+ struct socfpga_dwmac *dwmac; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL); -+ if (!dwmac) -+ return -ENOMEM; -+ -+ ret = socfpga_dwmac_parse_data(dwmac, dev); -+ if (ret) { -+ dev_err(dev, "Unable to parse OF data\n"); -+ return ret; -+ } -+ -+ ret = socfpga_dwmac_setup(dwmac); -+ if (ret) { -+ dev_err(dev, "couldn't setup SoC glue (%d)\n", ret); -+ return ret; -+ } -+ -+ plat_dat->bsp_priv = dwmac; -+ plat_dat->init = socfpga_dwmac_init; -+ plat_dat->exit = socfpga_dwmac_exit; -+ plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; -+ -+ ret = socfpga_dwmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id socfpga_dwmac_match[] = { -+ { .compatible = "altr,socfpga-stmmac" }, -+ { } - }; -+MODULE_DEVICE_TABLE(of, socfpga_dwmac_match); -+ -+static struct platform_driver socfpga_dwmac_driver = { -+ .probe = socfpga_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "socfpga-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = socfpga_dwmac_match, -+ }, -+}; -+module_platform_driver(socfpga_dwmac_driver); -+ -+MODULE_LICENSE("GPL v2"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c -@@ -17,9 +17,11 @@ - #include - #include - #include -+#include - #include - #include - #include -+#include - #include - - #include "stmmac_platform.h" -@@ -127,6 +129,11 @@ struct sti_dwmac { - struct device *dev; - struct regmap *regmap; - u32 speed; -+ void (*fix_retime_src)(void *priv, unsigned int speed); -+}; -+ -+struct sti_dwmac_of_data { -+ void (*fix_retime_src)(void *priv, unsigned int speed); - }; - - static u32 phy_intf_sels[] = { -@@ -221,8 +228,9 @@ static void stid127_fix_retime_src(void - regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val); - } - --static void sti_dwmac_ctrl_init(struct sti_dwmac *dwmac) -+static int sti_dwmac_init(struct platform_device *pdev, void *priv) - { -+ struct sti_dwmac *dwmac = priv; - struct regmap *regmap = dwmac->regmap; - int iface = dwmac->interface; - struct device *dev = dwmac->dev; -@@ -240,28 +248,8 @@ static void sti_dwmac_ctrl_init(struct s - - val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII; - regmap_update_bits(regmap, reg, ENMII_MASK, val); --} -- --static int stix4xx_init(struct platform_device *pdev, void *priv) --{ -- struct sti_dwmac *dwmac = priv; -- u32 spd = dwmac->speed; - -- sti_dwmac_ctrl_init(dwmac); -- -- stih4xx_fix_retime_src(priv, spd); -- -- return 0; --} -- --static int stid127_init(struct platform_device *pdev, void *priv) --{ -- struct sti_dwmac *dwmac = priv; -- u32 spd = dwmac->speed; -- -- sti_dwmac_ctrl_init(dwmac); -- -- stid127_fix_retime_src(priv, spd); -+ dwmac->fix_retime_src(priv, dwmac->speed); - - return 0; - } -@@ -333,34 +321,80 @@ static int sti_dwmac_parse_data(struct s - return 0; - } - --static void *sti_dwmac_setup(struct platform_device *pdev) -+static int sti_dwmac_probe(struct platform_device *pdev) - { -+ struct plat_stmmacenet_data *plat_dat; -+ const struct sti_dwmac_of_data *data; -+ struct stmmac_resources stmmac_res; - struct sti_dwmac *dwmac; - int ret; - -+ data = of_device_get_match_data(&pdev->dev); -+ if (!data) { -+ dev_err(&pdev->dev, "No OF match data provided\n"); -+ return -EINVAL; -+ } -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ - dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); - if (!dwmac) -- return ERR_PTR(-ENOMEM); -+ return -ENOMEM; - - ret = sti_dwmac_parse_data(dwmac, pdev); - if (ret) { - dev_err(&pdev->dev, "Unable to parse OF data\n"); -- return ERR_PTR(ret); -+ return ret; - } - -- return dwmac; -+ dwmac->fix_retime_src = data->fix_retime_src; -+ -+ plat_dat->bsp_priv = dwmac; -+ plat_dat->init = sti_dwmac_init; -+ plat_dat->exit = sti_dwmac_exit; -+ plat_dat->fix_mac_speed = data->fix_retime_src; -+ -+ ret = sti_dwmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - } - --const struct stmmac_of_data stih4xx_dwmac_data = { -- .fix_mac_speed = stih4xx_fix_retime_src, -- .setup = sti_dwmac_setup, -- .init = stix4xx_init, -- .exit = sti_dwmac_exit, -+static const struct sti_dwmac_of_data stih4xx_dwmac_data = { -+ .fix_retime_src = stih4xx_fix_retime_src, -+}; -+ -+static const struct sti_dwmac_of_data stid127_dwmac_data = { -+ .fix_retime_src = stid127_fix_retime_src, - }; - --const struct stmmac_of_data stid127_dwmac_data = { -- .fix_mac_speed = stid127_fix_retime_src, -- .setup = sti_dwmac_setup, -- .init = stid127_init, -- .exit = sti_dwmac_exit, -+static const struct of_device_id sti_dwmac_match[] = { -+ { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, -+ { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data}, -+ { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, -+ { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, -+ { } - }; -+MODULE_DEVICE_TABLE(of, sti_dwmac_match); -+ -+static struct platform_driver sti_dwmac_driver = { -+ .probe = sti_dwmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "sti-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = sti_dwmac_match, -+ }, -+}; -+module_platform_driver(sti_dwmac_driver); -+ -+MODULE_AUTHOR("Srinivas Kandagatla "); -+MODULE_DESCRIPTION("STMicroelectronics DWMAC Specific Glue layer"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c -@@ -18,7 +18,9 @@ - - #include - #include -+#include - #include -+#include - #include - #include - -@@ -31,35 +33,6 @@ struct sunxi_priv_data { - struct regulator *regulator; - }; - --static void *sun7i_gmac_setup(struct platform_device *pdev) --{ -- struct sunxi_priv_data *gmac; -- struct device *dev = &pdev->dev; -- -- gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); -- if (!gmac) -- return ERR_PTR(-ENOMEM); -- -- gmac->interface = of_get_phy_mode(dev->of_node); -- -- gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); -- if (IS_ERR(gmac->tx_clk)) { -- dev_err(dev, "could not get tx clock\n"); -- return gmac->tx_clk; -- } -- -- /* Optional regulator for PHY */ -- gmac->regulator = devm_regulator_get_optional(dev, "phy"); -- if (IS_ERR(gmac->regulator)) { -- if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) -- return ERR_PTR(-EPROBE_DEFER); -- dev_info(dev, "no regulator found\n"); -- gmac->regulator = NULL; -- } -- -- return gmac; --} -- - #define SUN7I_GMAC_GMII_RGMII_RATE 125000000 - #define SUN7I_GMAC_MII_RATE 25000000 - -@@ -130,13 +103,76 @@ static void sun7i_fix_speed(void *priv, - } - } - --/* of_data specifying hardware features and callbacks. -- * hardware features were copied from Allwinner drivers. */ --const struct stmmac_of_data sun7i_gmac_data = { -- .has_gmac = 1, -- .tx_coe = 1, -- .fix_mac_speed = sun7i_fix_speed, -- .setup = sun7i_gmac_setup, -- .init = sun7i_gmac_init, -- .exit = sun7i_gmac_exit, -+static int sun7i_gmac_probe(struct platform_device *pdev) -+{ -+ struct plat_stmmacenet_data *plat_dat; -+ struct stmmac_resources stmmac_res; -+ struct sunxi_priv_data *gmac; -+ struct device *dev = &pdev->dev; -+ int ret; -+ -+ ret = stmmac_get_platform_resources(pdev, &stmmac_res); -+ if (ret) -+ return ret; -+ -+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); -+ if (IS_ERR(plat_dat)) -+ return PTR_ERR(plat_dat); -+ -+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); -+ if (!gmac) -+ return -ENOMEM; -+ -+ gmac->interface = of_get_phy_mode(dev->of_node); -+ -+ gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); -+ if (IS_ERR(gmac->tx_clk)) { -+ dev_err(dev, "could not get tx clock\n"); -+ return PTR_ERR(gmac->tx_clk); -+ } -+ -+ /* Optional regulator for PHY */ -+ gmac->regulator = devm_regulator_get_optional(dev, "phy"); -+ if (IS_ERR(gmac->regulator)) { -+ if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ dev_info(dev, "no regulator found\n"); -+ gmac->regulator = NULL; -+ } -+ -+ /* platform data specifying hardware features and callbacks. -+ * hardware features were copied from Allwinner drivers. */ -+ plat_dat->tx_coe = 1; -+ plat_dat->has_gmac = true; -+ plat_dat->bsp_priv = gmac; -+ plat_dat->init = sun7i_gmac_init; -+ plat_dat->exit = sun7i_gmac_exit; -+ plat_dat->fix_mac_speed = sun7i_fix_speed; -+ -+ ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv); -+ if (ret) -+ return ret; -+ -+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); -+} -+ -+static const struct of_device_id sun7i_dwmac_match[] = { -+ { .compatible = "allwinner,sun7i-a20-gmac" }, -+ { } - }; -+MODULE_DEVICE_TABLE(of, sun7i_dwmac_match); -+ -+static struct platform_driver sun7i_dwmac_driver = { -+ .probe = sun7i_gmac_probe, -+ .remove = stmmac_pltfr_remove, -+ .driver = { -+ .name = "sun7i-dwmac", -+ .pm = &stmmac_pltfr_pm_ops, -+ .of_match_table = sun7i_dwmac_match, -+ }, -+}; -+module_platform_driver(sun7i_dwmac_driver); -+ -+MODULE_AUTHOR("Chen-Yu Tsai "); -+MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c -+++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c -@@ -73,7 +73,7 @@ - #define MMC_RX_OCTETCOUNT_G 0x00000188 - #define MMC_RX_BROADCASTFRAME_G 0x0000018c - #define MMC_RX_MULTICASTFRAME_G 0x00000190 --#define MMC_RX_CRC_ERRROR 0x00000194 -+#define MMC_RX_CRC_ERROR 0x00000194 - #define MMC_RX_ALIGN_ERROR 0x00000198 - #define MMC_RX_RUN_ERROR 0x0000019C - #define MMC_RX_JABBER_ERROR 0x000001A0 -@@ -196,7 +196,7 @@ void dwmac_mmc_read(void __iomem *ioaddr - mmc->mmc_rx_octetcount_g += readl(ioaddr + MMC_RX_OCTETCOUNT_G); - mmc->mmc_rx_broadcastframe_g += readl(ioaddr + MMC_RX_BROADCASTFRAME_G); - mmc->mmc_rx_multicastframe_g += readl(ioaddr + MMC_RX_MULTICASTFRAME_G); -- mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERRROR); -+ mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERROR); - mmc->mmc_rx_align_error += readl(ioaddr + MMC_RX_ALIGN_ERROR); - mmc->mmc_rx_run_error += readl(ioaddr + MMC_RX_RUN_ERROR); - mmc->mmc_rx_jabber_error += readl(ioaddr + MMC_RX_JABBER_ERROR); ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h -@@ -34,6 +34,14 @@ - #include - #include - -+struct stmmac_resources { -+ void __iomem *addr; -+ const char *mac; -+ int wol_irq; -+ int lpi_irq; -+ int irq; -+}; -+ - struct stmmac_tx_info { - dma_addr_t buf; - bool map_as_page; -@@ -135,9 +143,9 @@ void stmmac_ptp_unregister(struct stmmac - int stmmac_resume(struct net_device *ndev); - int stmmac_suspend(struct net_device *ndev); - int stmmac_dvr_remove(struct net_device *ndev); --struct stmmac_priv *stmmac_dvr_probe(struct device *device, -- struct plat_stmmacenet_data *plat_dat, -- void __iomem *addr); -+int stmmac_dvr_probe(struct device *device, -+ struct plat_stmmacenet_data *plat_dat, -+ struct stmmac_resources *res); - void stmmac_disable_eee_mode(struct stmmac_priv *priv); - bool stmmac_eee_init(struct stmmac_priv *priv); - ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -52,6 +52,7 @@ - #include "stmmac_ptp.h" - #include "stmmac.h" - #include -+#include - - #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) - -@@ -816,18 +817,25 @@ static int stmmac_init_phy(struct net_de - priv->speed = 0; - priv->oldduplex = -1; - -- if (priv->plat->phy_bus_name) -- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", -- priv->plat->phy_bus_name, priv->plat->bus_id); -- else -- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", -- priv->plat->bus_id); -+ if (priv->plat->phy_node) { -+ phydev = of_phy_connect(dev, priv->plat->phy_node, -+ &stmmac_adjust_link, 0, interface); -+ } else { -+ if (priv->plat->phy_bus_name) -+ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", -+ priv->plat->phy_bus_name, priv->plat->bus_id); -+ else -+ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", -+ priv->plat->bus_id); - -- snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, -- priv->plat->phy_addr); -- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt); -+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, -+ priv->plat->phy_addr); -+ pr_debug("stmmac_init_phy: trying to attach to %s\n", -+ phy_id_fmt); - -- phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface); -+ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, -+ interface); -+ } - - if (IS_ERR_OR_NULL(phydev)) { - pr_err("%s: Could not attach to PHY\n", dev->name); -@@ -851,7 +859,7 @@ static int stmmac_init_phy(struct net_de - * device as well. - * Note: phydev->phy_id is the result of reading the UID PHY registers. - */ -- if (phydev->phy_id == 0) { -+ if (!priv->plat->phy_node && phydev->phy_id == 0) { - phy_disconnect(phydev); - return -ENODEV; - } -@@ -978,13 +986,11 @@ static int stmmac_init_rx_buffers(struct - { - struct sk_buff *skb; - -- skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN, -- flags); -+ skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); - if (!skb) { - pr_err("%s: Rx init fails; skb is NULL\n", __func__); - return -ENOMEM; - } -- skb_reserve(skb, NET_IP_ALIGN); - priv->rx_skbuff[i] = skb; - priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, - priv->dma_buf_sz, -@@ -2803,16 +2809,15 @@ static int stmmac_hw_init(struct stmmac_ - * stmmac_dvr_probe - * @device: device pointer - * @plat_dat: platform data pointer -- * @addr: iobase memory address -+ * @res: stmmac resource pointer - * Description: this is the main probe function used to - * call the alloc_etherdev, allocate the priv structure. - * Return: -- * on success the new private structure is returned, otherwise the error -- * pointer. -+ * returns 0 on success, otherwise errno. - */ --struct stmmac_priv *stmmac_dvr_probe(struct device *device, -- struct plat_stmmacenet_data *plat_dat, -- void __iomem *addr) -+int stmmac_dvr_probe(struct device *device, -+ struct plat_stmmacenet_data *plat_dat, -+ struct stmmac_resources *res) - { - int ret = 0; - struct net_device *ndev = NULL; -@@ -2820,7 +2825,7 @@ struct stmmac_priv *stmmac_dvr_probe(str - - ndev = alloc_etherdev(sizeof(struct stmmac_priv)); - if (!ndev) -- return ERR_PTR(-ENOMEM); -+ return -ENOMEM; - - SET_NETDEV_DEV(ndev, device); - -@@ -2831,8 +2836,17 @@ struct stmmac_priv *stmmac_dvr_probe(str - stmmac_set_ethtool_ops(ndev); - priv->pause = pause; - priv->plat = plat_dat; -- priv->ioaddr = addr; -- priv->dev->base_addr = (unsigned long)addr; -+ priv->ioaddr = res->addr; -+ priv->dev->base_addr = (unsigned long)res->addr; -+ -+ priv->dev->irq = res->irq; -+ priv->wol_irq = res->wol_irq; -+ priv->lpi_irq = res->lpi_irq; -+ -+ if (res->mac) -+ memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN); -+ -+ dev_set_drvdata(device, priv->dev); - - /* Verify driver arguments */ - stmmac_verify_args(); -@@ -2947,7 +2961,7 @@ struct stmmac_priv *stmmac_dvr_probe(str - } - } - -- return priv; -+ return 0; - - error_mdio_register: - unregister_netdev(ndev); -@@ -2960,7 +2974,7 @@ error_pclk_get: - error_clk_get: - free_netdev(ndev); - -- return ERR_PTR(ret); -+ return ret; - } - EXPORT_SYMBOL_GPL(stmmac_dvr_probe); - ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c -@@ -161,11 +161,16 @@ int stmmac_mdio_reset(struct mii_bus *bu - - if (!gpio_request(reset_gpio, "mdio-reset")) { - gpio_direction_output(reset_gpio, active_low ? 1 : 0); -- udelay(data->delays[0]); -+ if (data->delays[0]) -+ msleep(DIV_ROUND_UP(data->delays[0], 1000)); -+ - gpio_set_value(reset_gpio, active_low ? 0 : 1); -- udelay(data->delays[1]); -+ if (data->delays[1]) -+ msleep(DIV_ROUND_UP(data->delays[1], 1000)); -+ - gpio_set_value(reset_gpio, active_low ? 1 : 0); -- udelay(data->delays[2]); -+ if (data->delays[2]) -+ msleep(DIV_ROUND_UP(data->delays[2], 1000)); - } - } - #endif ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c -@@ -163,7 +163,7 @@ static int stmmac_pci_probe(struct pci_d - { - struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data; - struct plat_stmmacenet_data *plat; -- struct stmmac_priv *priv; -+ struct stmmac_resources res; - int i; - int ret; - -@@ -214,19 +214,12 @@ static int stmmac_pci_probe(struct pci_d - - pci_enable_msi(pdev); - -- priv = stmmac_dvr_probe(&pdev->dev, plat, pcim_iomap_table(pdev)[i]); -- if (IS_ERR(priv)) { -- dev_err(&pdev->dev, "%s: main driver probe failed\n", __func__); -- return PTR_ERR(priv); -- } -- priv->dev->irq = pdev->irq; -- priv->wol_irq = pdev->irq; -- -- pci_set_drvdata(pdev, priv->dev); -- -- dev_dbg(&pdev->dev, "STMMAC PCI driver registration completed\n"); -+ memset(&res, 0, sizeof(res)); -+ res.addr = pcim_iomap_table(pdev)[i]; -+ res.wol_irq = pdev->irq; -+ res.irq = pdev->irq; - -- return 0; -+ return stmmac_dvr_probe(&pdev->dev, plat, &res); - } - - /** ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -@@ -28,29 +28,11 @@ - #include - #include - #include -+#include - - #include "stmmac.h" - #include "stmmac_platform.h" - --static const struct of_device_id stmmac_dt_ids[] = { -- /* SoC specific glue layers should come before generic bindings */ -- { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_gmac_data}, -- { .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data}, -- { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data}, -- { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, -- { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data}, -- { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, -- { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, -- { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data }, -- { .compatible = "st,spear600-gmac"}, -- { .compatible = "snps,dwmac-3.610"}, -- { .compatible = "snps,dwmac-3.70a"}, -- { .compatible = "snps,dwmac-3.710"}, -- { .compatible = "snps,dwmac"}, -- { /* sentinel */ } --}; --MODULE_DEVICE_TABLE(of, stmmac_dt_ids); -- - #ifdef CONFIG_OF - - /** -@@ -122,37 +104,16 @@ static int dwmac1000_validate_ucast_entr - * this function is to read the driver parameters from device-tree and - * set some private fields that will be used by the main at runtime. - */ --static int stmmac_probe_config_dt(struct platform_device *pdev, -- struct plat_stmmacenet_data *plat, -- const char **mac) -+struct plat_stmmacenet_data * -+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) - { - struct device_node *np = pdev->dev.of_node; -+ struct plat_stmmacenet_data *plat; - struct stmmac_dma_cfg *dma_cfg; -- const struct of_device_id *device; -- -- if (!np) -- return -ENODEV; - -- device = of_match_device(stmmac_dt_ids, &pdev->dev); -- if (!device) -- return -ENODEV; -- -- if (device->data) { -- const struct stmmac_of_data *data = device->data; -- plat->has_gmac = data->has_gmac; -- plat->enh_desc = data->enh_desc; -- plat->tx_coe = data->tx_coe; -- plat->rx_coe = data->rx_coe; -- plat->bugged_jumbo = data->bugged_jumbo; -- plat->pmt = data->pmt; -- plat->riwt_off = data->riwt_off; -- plat->fix_mac_speed = data->fix_mac_speed; -- plat->bus_setup = data->bus_setup; -- plat->setup = data->setup; -- plat->free = data->free; -- plat->init = data->init; -- plat->exit = data->exit; -- } -+ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); -+ if (!plat) -+ return ERR_PTR(-ENOMEM); - - *mac = of_get_mac_address(np); - plat->interface = of_get_phy_mode(np); -@@ -168,13 +129,24 @@ static int stmmac_probe_config_dt(struct - /* Default to phy auto-detection */ - plat->phy_addr = -1; - -+ /* If we find a phy-handle property, use it as the PHY */ -+ plat->phy_node = of_parse_phandle(np, "phy-handle", 0); -+ -+ /* If phy-handle is not specified, check if we have a fixed-phy */ -+ if (!plat->phy_node && of_phy_is_fixed_link(np)) { -+ if ((of_phy_register_fixed_link(np) < 0)) -+ return ERR_PTR(-ENODEV); -+ -+ plat->phy_node = of_node_get(np); -+ } -+ - /* "snps,phy-addr" is not a standard property. Mark it as deprecated - * and warn of its use. Remove this when phy node support is added. - */ - if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0) - dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n"); - -- if (plat->phy_bus_name) -+ if (plat->phy_node || plat->phy_bus_name) - plat->mdio_bus_data = NULL; - else - plat->mdio_bus_data = -@@ -194,6 +166,12 @@ static int stmmac_probe_config_dt(struct - */ - plat->maxmtu = JUMBO_LEN; - -+ /* Set default value for multicast hash bins */ -+ plat->multicast_filter_bins = HASH_TABLE_SIZE; -+ -+ /* Set default value for unicast filter entries */ -+ plat->unicast_filter_entries = 1; -+ - /* - * Currently only the properties needed on SPEAr600 - * are provided. All other properties should be added -@@ -232,8 +210,10 @@ static int stmmac_probe_config_dt(struct - if (of_find_property(np, "snps,pbl", NULL)) { - dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), - GFP_KERNEL); -- if (!dma_cfg) -- return -ENOMEM; -+ if (!dma_cfg) { -+ of_node_put(np); -+ return ERR_PTR(-ENOMEM); -+ } - plat->dma_cfg = dma_cfg; - of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); - dma_cfg->fixed_burst = -@@ -250,45 +230,34 @@ static int stmmac_probe_config_dt(struct - pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set."); - } - -- return 0; -+ return plat; - } - #else --static int stmmac_probe_config_dt(struct platform_device *pdev, -- struct plat_stmmacenet_data *plat, -- const char **mac) -+struct plat_stmmacenet_data * -+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) - { -- return -ENOSYS; -+ return ERR_PTR(-ENOSYS); - } - #endif /* CONFIG_OF */ -+EXPORT_SYMBOL_GPL(stmmac_probe_config_dt); - --/** -- * stmmac_pltfr_probe - platform driver probe. -- * @pdev: platform device pointer -- * Description: platform_device probe function. It is to allocate -- * the necessary platform resources, invoke custom helper (if required) and -- * invoke the main probe function. -- */ --static int stmmac_pltfr_probe(struct platform_device *pdev) -+int stmmac_get_platform_resources(struct platform_device *pdev, -+ struct stmmac_resources *stmmac_res) - { -- int ret = 0; - struct resource *res; -- struct device *dev = &pdev->dev; -- void __iomem *addr = NULL; -- struct stmmac_priv *priv = NULL; -- struct plat_stmmacenet_data *plat_dat = NULL; -- const char *mac = NULL; -- int irq, wol_irq, lpi_irq; -+ -+ memset(stmmac_res, 0, sizeof(*stmmac_res)); - - /* Get IRQ information early to have an ability to ask for deferred - * probe if needed before we went too far with resource allocation. - */ -- irq = platform_get_irq_byname(pdev, "macirq"); -- if (irq < 0) { -- if (irq != -EPROBE_DEFER) { -- dev_err(dev, -+ stmmac_res->irq = platform_get_irq_byname(pdev, "macirq"); -+ if (stmmac_res->irq < 0) { -+ if (stmmac_res->irq != -EPROBE_DEFER) { -+ dev_err(&pdev->dev, - "MAC IRQ configuration information not found\n"); - } -- return irq; -+ return stmmac_res->irq; - } - - /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq -@@ -298,82 +267,23 @@ static int stmmac_pltfr_probe(struct pla - * In case the wake up interrupt is not passed from the platform - * so the driver will continue to use the mac irq (ndev->irq) - */ -- wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); -- if (wol_irq < 0) { -- if (wol_irq == -EPROBE_DEFER) -+ stmmac_res->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); -+ if (stmmac_res->wol_irq < 0) { -+ if (stmmac_res->wol_irq == -EPROBE_DEFER) - return -EPROBE_DEFER; -- wol_irq = irq; -+ stmmac_res->wol_irq = stmmac_res->irq; - } - -- lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); -- if (lpi_irq == -EPROBE_DEFER) -+ stmmac_res->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); -+ if (stmmac_res->lpi_irq == -EPROBE_DEFER) - return -EPROBE_DEFER; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- addr = devm_ioremap_resource(dev, res); -- if (IS_ERR(addr)) -- return PTR_ERR(addr); -- -- plat_dat = dev_get_platdata(&pdev->dev); -- -- if (!plat_dat) -- plat_dat = devm_kzalloc(&pdev->dev, -- sizeof(struct plat_stmmacenet_data), -- GFP_KERNEL); -- if (!plat_dat) { -- pr_err("%s: ERROR: no memory", __func__); -- return -ENOMEM; -- } -- -- /* Set default value for multicast hash bins */ -- plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; -+ stmmac_res->addr = devm_ioremap_resource(&pdev->dev, res); - -- /* Set default value for unicast filter entries */ -- plat_dat->unicast_filter_entries = 1; -- -- if (pdev->dev.of_node) { -- ret = stmmac_probe_config_dt(pdev, plat_dat, &mac); -- if (ret) { -- pr_err("%s: main dt probe failed", __func__); -- return ret; -- } -- } -- -- /* Custom setup (if needed) */ -- if (plat_dat->setup) { -- plat_dat->bsp_priv = plat_dat->setup(pdev); -- if (IS_ERR(plat_dat->bsp_priv)) -- return PTR_ERR(plat_dat->bsp_priv); -- } -- -- /* Custom initialisation (if needed)*/ -- if (plat_dat->init) { -- ret = plat_dat->init(pdev, plat_dat->bsp_priv); -- if (unlikely(ret)) -- return ret; -- } -- -- priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr); -- if (IS_ERR(priv)) { -- pr_err("%s: main driver probe failed", __func__); -- return PTR_ERR(priv); -- } -- -- /* Copy IRQ values to priv structure which is now avaialble */ -- priv->dev->irq = irq; -- priv->wol_irq = wol_irq; -- priv->lpi_irq = lpi_irq; -- -- /* Get MAC address if available (DT) */ -- if (mac) -- memcpy(priv->dev->dev_addr, mac, ETH_ALEN); -- -- platform_set_drvdata(pdev, priv->dev); -- -- pr_debug("STMMAC platform driver registration completed"); -- -- return 0; -+ return PTR_ERR_OR_ZERO(stmmac_res->addr); - } -+EXPORT_SYMBOL_GPL(stmmac_get_platform_resources); - - /** - * stmmac_pltfr_remove -@@ -381,7 +291,7 @@ static int stmmac_pltfr_probe(struct pla - * Description: this function calls the main to free the net resources - * and calls the platforms hook and release the resources (e.g. mem). - */ --static int stmmac_pltfr_remove(struct platform_device *pdev) -+int stmmac_pltfr_remove(struct platform_device *pdev) - { - struct net_device *ndev = platform_get_drvdata(pdev); - struct stmmac_priv *priv = netdev_priv(ndev); -@@ -390,11 +300,9 @@ static int stmmac_pltfr_remove(struct pl - if (priv->plat->exit) - priv->plat->exit(pdev, priv->plat->bsp_priv); - -- if (priv->plat->free) -- priv->plat->free(pdev, priv->plat->bsp_priv); -- - return ret; - } -+EXPORT_SYMBOL_GPL(stmmac_pltfr_remove); - - #ifdef CONFIG_PM_SLEEP - /** -@@ -438,21 +346,10 @@ static int stmmac_pltfr_resume(struct de - } - #endif /* CONFIG_PM_SLEEP */ - --static SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, -- stmmac_pltfr_suspend, stmmac_pltfr_resume); -- --static struct platform_driver stmmac_pltfr_driver = { -- .probe = stmmac_pltfr_probe, -- .remove = stmmac_pltfr_remove, -- .driver = { -- .name = STMMAC_RESOURCE_NAME, -- .pm = &stmmac_pltfr_pm_ops, -- .of_match_table = of_match_ptr(stmmac_dt_ids), -- }, --}; -- --module_platform_driver(stmmac_pltfr_driver); -+SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, stmmac_pltfr_suspend, -+ stmmac_pltfr_resume); -+EXPORT_SYMBOL_GPL(stmmac_pltfr_pm_ops); - --MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver"); -+MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet platform support"); - MODULE_AUTHOR("Giuseppe Cavallaro "); - MODULE_LICENSE("GPL"); ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h -@@ -19,11 +19,15 @@ - #ifndef __STMMAC_PLATFORM_H__ - #define __STMMAC_PLATFORM_H__ - --extern const struct stmmac_of_data meson6_dwmac_data; --extern const struct stmmac_of_data sun7i_gmac_data; --extern const struct stmmac_of_data stih4xx_dwmac_data; --extern const struct stmmac_of_data stid127_dwmac_data; --extern const struct stmmac_of_data socfpga_gmac_data; --extern const struct stmmac_of_data rk3288_gmac_data; -+#include "stmmac.h" -+ -+struct plat_stmmacenet_data * -+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac); -+ -+int stmmac_get_platform_resources(struct platform_device *pdev, -+ struct stmmac_resources *stmmac_res); -+ -+int stmmac_pltfr_remove(struct platform_device *pdev); -+extern const struct dev_pm_ops stmmac_pltfr_pm_ops; - - #endif /* __STMMAC_PLATFORM_H__ */ ---- a/include/linux/stmmac.h -+++ b/include/linux/stmmac.h -@@ -99,6 +99,7 @@ struct plat_stmmacenet_data { - int phy_addr; - int interface; - struct stmmac_mdio_bus_data *mdio_bus_data; -+ struct device_node *phy_node; - struct stmmac_dma_cfg *dma_cfg; - int clk_csr; - int has_gmac; -@@ -118,30 +119,8 @@ struct plat_stmmacenet_data { - int rx_fifo_size; - void (*fix_mac_speed)(void *priv, unsigned int speed); - void (*bus_setup)(void __iomem *ioaddr); -- void *(*setup)(struct platform_device *pdev); -- void (*free)(struct platform_device *pdev, void *priv); - int (*init)(struct platform_device *pdev, void *priv); - void (*exit)(struct platform_device *pdev, void *priv); -- void *custom_cfg; -- void *custom_data; - void *bsp_priv; - }; -- --/* of_data for SoC glue layer device tree bindings */ -- --struct stmmac_of_data { -- int has_gmac; -- int enh_desc; -- int tx_coe; -- int rx_coe; -- int bugged_jumbo; -- int pmt; -- int riwt_off; -- void (*fix_mac_speed)(void *priv, unsigned int speed); -- void (*bus_setup)(void __iomem *ioaddr); -- void *(*setup)(struct platform_device *pdev); -- void (*free)(struct platform_device *pdev, void *priv); -- int (*init)(struct platform_device *pdev, void *priv); -- void (*exit)(struct platform_device *pdev, void *priv); --}; - #endif diff --git a/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch deleted file mode 100644 index 5126e9061e..0000000000 --- a/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch +++ /dev/null @@ -1,146 +0,0 @@ -From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Mon, 11 May 2015 16:32:09 -0700 -Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149 - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++- - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++ - 2 files changed, 85 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -19,8 +19,9 @@ - }; - }; - -- alias { -+ aliases { - serial0 = &uart4; -+ mdio-gpio0 = &mdio0; - }; - - chosen { -@@ -65,6 +66,15 @@ - bias-bus-hold; - }; - }; -+ -+ mdio0_pins: mdio0_pins { -+ mux { -+ pins = "gpio0", "gpio1"; -+ function = "gpio"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi@16300000 { -@@ -135,6 +145,34 @@ - - linux,part-probe = "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 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>; -+ }; -+ }; - }; - }; - ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -16,6 +16,7 @@ - - alias { - serial0 = &uart2; -+ mdio-gpio0 = &mdio0; - }; - - chosen { -@@ -38,6 +39,15 @@ - bias-none; - }; - }; -+ -+ mdio0_pins: mdio0_pins { -+ mux { -+ pins = "gpio0", "gpio1"; -+ function = "gpio"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi2: gsbi@12480000 { -@@ -140,5 +150,44 @@ - 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 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>; -+ }; -+ -+ phy6: ethernet-phy@6 { -+ device_type = "ethernet-phy"; -+ reg = <6>; -+ }; -+ -+ phy7: ethernet-phy@7 { -+ device_type = "ethernet-phy"; -+ reg = <7>; -+ }; -+ }; - }; - }; diff --git a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch deleted file mode 100644 index 37dfaabf05..0000000000 --- a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch +++ /dev/null @@ -1,216 +0,0 @@ -From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Mon, 11 May 2015 12:29:18 -0700 -Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms - -Signed-off-by: Mathieu Olivari ---- - arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++ - arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++ - arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++ - 3 files changed, 160 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts -@@ -75,6 +75,16 @@ - 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 { -@@ -173,6 +183,31 @@ - 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; -+ }; -+ }; - }; - }; - ---- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts -+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts -@@ -48,6 +48,14 @@ - bias-disable; - }; - }; -+ -+ rgmii0_pins: rgmii0_pins { -+ mux { -+ pins = "gpio2", "gpio66"; -+ drive-strength = <8>; -+ bias-disable; -+ }; -+ }; - }; - - gsbi2: gsbi@12480000 { -@@ -189,5 +197,40 @@ - 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>; -+ }; - }; - }; ---- a/arch/arm/boot/dts/qcom-ipq8064.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -679,6 +679,92 @@ - - status = "disabled"; - }; -+ -+ nss_common: syscon@03000000 { -+ compatible = "syscon"; -+ reg = <0x03000000 0x0000FFFF>; -+ }; -+ -+ qsgmii_csr: syscon@1bb00000 { -+ compatible = "syscon"; -+ reg = <0x1bb00000 0x000001FF>; -+ }; -+ -+ gmac0: ethernet@37000000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37000000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE1_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE1_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac1: ethernet@37200000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37200000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE2_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE2_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac2: ethernet@37400000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37400000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE3_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE3_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; -+ -+ gmac3: ethernet@37600000 { -+ device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; -+ reg = <0x37600000 0x200000>; -+ interrupts = ; -+ interrupt-names = "macirq"; -+ -+ qcom,nss-common = <&nss_common>; -+ qcom,qsgmii-csr = <&qsgmii_csr>; -+ -+ clocks = <&gcc GMAC_CORE4_CLK>; -+ clock-names = "stmmaceth"; -+ -+ resets = <&gcc GMAC_CORE4_RESET>; -+ reset-names = "stmmaceth"; -+ -+ status = "disabled"; -+ }; - }; - - sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.1/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch b/target/linux/ipq806x/patches-4.1/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch deleted file mode 100644 index d385c9a36c..0000000000 --- a/target/linux/ipq806x/patches-4.1/709-stmac-platform-add-support-for-retreiving-mac-from-m.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 5bf2dabde1fa3af0c9082b42b6847ef3fd198b13 Mon Sep 17 00:00:00 2001 -From: Jonas Gorski -Date: Sun, 9 Aug 2015 12:53:55 +0200 -Subject: [PATCH] stmac: platform: add support for retreiving mac from mtd - ---- - drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -@@ -116,6 +116,19 @@ stmmac_probe_config_dt(struct platform_d - return ERR_PTR(-ENOMEM); - - *mac = of_get_mac_address(np); -+ if (!*mac) { -+ u8 mtd_mac[ETH_ALEN]; -+ int ret; -+ -+ ret = of_get_mac_address_mtd(np, mtd_mac); -+ if (ret == -EPROBE_DEFER) -+ return ERR_PTR(ret); -+ -+ if (is_valid_ether_addr(mtd_mac)) -+ *mac = devm_kmemdup(&pdev->dev, mtd_mac, ETH_ALEN, -+ GFP_KERNEL); -+ } -+ - plat->interface = of_get_phy_mode(np); - - /* Get max speed of operation from device tree */ diff --git a/target/linux/ipq806x/patches-4.1/710-stmmac-fix-ipq806x-DMA-configuration.patch b/target/linux/ipq806x/patches-4.1/710-stmmac-fix-ipq806x-DMA-configuration.patch deleted file mode 100644 index c99f60768f..0000000000 --- a/target/linux/ipq806x/patches-4.1/710-stmmac-fix-ipq806x-DMA-configuration.patch +++ /dev/null @@ -1,117 +0,0 @@ ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c -@@ -259,6 +259,7 @@ static int ipq806x_gmac_probe(struct pla - { - struct plat_stmmacenet_data *plat_dat; - struct stmmac_resources stmmac_res; -+ struct stmmac_dma_cfg *dma_cfg; - struct device *dev = &pdev->dev; - struct ipq806x_gmac *gmac; - int val; -@@ -348,6 +349,17 @@ static int ipq806x_gmac_probe(struct pla - plat_dat->bsp_priv = gmac; - plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; - -+ dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), -+ GFP_KERNEL); -+ -+ dma_cfg->pbl = 32; -+ dma_cfg->aal = 1; -+ dma_cfg->burst_len = DMA_AXI_BLEN_16 | -+ (7 << DMA_AXI_RD_OSR_LMT_SHIFT) | -+ (7 << DMA_AXI_WR_OSR_LMT_SHIFT); -+ -+ plat_dat->dma_cfg = dma_cfg; -+ - return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - } - ---- a/include/linux/stmmac.h -+++ b/include/linux/stmmac.h -@@ -73,6 +73,9 @@ - | DMA_AXI_BLEN_32 | DMA_AXI_BLEN_64 \ - | DMA_AXI_BLEN_128 | DMA_AXI_BLEN_256) - -+#define DMA_AXI_RD_OSR_LMT_SHIFT 16 -+#define DMA_AXI_WR_OSR_LMT_SHIFT 20 -+ - /* Platfrom data for platform device structure's platform_data field */ - - struct stmmac_mdio_bus_data { -@@ -88,6 +91,7 @@ struct stmmac_mdio_bus_data { - - struct stmmac_dma_cfg { - int pbl; -+ int aal; - int fixed_burst; - int mixed_burst; - int burst_len; ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c -@@ -31,7 +31,8 @@ - #include "dwmac_dma.h" - - static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds) -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, -+ int aal) - { - u32 value = readl(ioaddr + DMA_BUS_MODE); - int limit; -@@ -62,6 +63,10 @@ static int dwmac1000_dma_init(void __iom - value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) | - (pbl << DMA_BUS_MODE_RPBL_SHIFT)); - -+ /* Address Aligned Beats */ -+ if (aal) -+ value |= DMA_BUS_MODE_AAL; -+ - /* Set the Fixed burst mode */ - if (fb) - value |= DMA_BUS_MODE_FB; ---- a/drivers/net/ethernet/stmicro/stmmac/common.h -+++ b/drivers/net/ethernet/stmicro/stmmac/common.h -@@ -352,7 +352,7 @@ extern const struct stmmac_desc_ops ndes - struct stmmac_dma_ops { - /* DMA core initialization */ - int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds); -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, int aal); - /* Dump DMA registers */ - void (*dump_regs) (void __iomem *ioaddr); - /* Set tx/rx threshold in the csr6 register ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c -@@ -33,7 +33,8 @@ - #include "dwmac_dma.h" - - static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, -- int burst_len, u32 dma_tx, u32 dma_rx, int atds) -+ int burst_len, u32 dma_tx, u32 dma_rx, int atds, -+ int aal) - { - u32 value = readl(ioaddr + DMA_BUS_MODE); - int limit; ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -1639,9 +1639,11 @@ static int stmmac_init_dma_engine(struct - int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, burst_len = 0; - int mixed_burst = 0; - int atds = 0; -+ int aal = 0; - - if (priv->plat->dma_cfg) { - pbl = priv->plat->dma_cfg->pbl; -+ aal = priv->plat->dma_cfg->aal; - fixed_burst = priv->plat->dma_cfg->fixed_burst; - mixed_burst = priv->plat->dma_cfg->mixed_burst; - burst_len = priv->plat->dma_cfg->burst_len; -@@ -1652,7 +1654,7 @@ static int stmmac_init_dma_engine(struct - - return priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst, - burst_len, priv->dma_tx_phy, -- priv->dma_rx_phy, atds); -+ priv->dma_rx_phy, atds, aal); - } - - /** diff --git a/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch new file mode 100644 index 0000000000..9c33bad5c1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/020-add-ap148-bootargs.patch @@ -0,0 +1,61 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -4,14 +4,6 @@ + model = "Qualcomm IPQ8064/AP148"; + compatible = "qcom,ipq8064-ap148", "qcom,ipq8064"; + +- aliases { +- serial0 = &gsbi4_serial; +- }; +- +- chosen { +- stdout-path = "serial0:115200n8"; +- }; +- + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; +@@ -22,6 +14,14 @@ + }; + }; + ++ alias { ++ serial0 = &uart4; ++ }; ++ ++ chosen { ++ linux,stdout-path = "serial0:115200n8"; ++ }; ++ + soc { + pinmux@800000 { + i2c4_pins: i2c4_pinmux { +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -159,7 +159,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@12490000 { ++ uart2: serial@12490000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x12490000 0x1000>, + <0x12480000 0x1000>; +@@ -197,7 +197,7 @@ + + syscon-tcsr = <&tcsr>; + +- gsbi4_serial: serial@16340000 { ++ uart4: serial@16340000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x16340000 0x1000>, + <0x16300000 0x1000>; +@@ -234,7 +234,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@1a240000 { ++ uart5: serial@1a240000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x1a240000 0x1000>, + <0x1a200000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch new file mode 100644 index 0000000000..bfdb30fe14 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/021-add-ap148-partitions.patch @@ -0,0 +1,19 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -77,15 +77,7 @@ + spi-max-frequency = <50000000>; + reg = <0>; + +- partition@0 { +- label = "rootfs"; +- reg = <0x0 0x1000000>; +- }; +- +- partition@1 { +- label = "scratch"; +- reg = <0x1000000 0x1000000>; +- }; ++ linux,part-probe = "qcom-smem"; + }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch new file mode 100644 index 0000000000..4d3e827b51 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/022-add-db149-dts.patch @@ -0,0 +1,160 @@ +From f26cc3733bdd697bd81ae505fc133fa7c9b6ea19 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 7 Apr 2015 19:58:58 -0700 +Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree + +Add basic DB149 (IPQ806x based platform) device-tree. It supports UART, +SATA, USB2, USB3 and NOR flash. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 132 +++++++++++++++++++++++++++++++ + 2 files changed, 133 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -506,6 +506,7 @@ + qcom-apq8084-ifc6540.dtb \ + qcom-apq8084-mtp.dtb \ + qcom-ipq8064-ap148.dtb \ ++ qcom-ipq8064-db149.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-sony-xperia-honami.dtb +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -0,0 +1,132 @@ ++#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; ++ }; ++ ++ 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; ++ }; ++ }; ++ }; ++ ++ gsbi2: gsbi@12480000 { ++ qcom,mode = ; ++ status = "ok"; ++ uart2: serial@12490000 { ++ status = "ok"; ++ }; ++ }; ++ ++ gsbi5: gsbi@1a200000 { ++ qcom,mode = ; ++ 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"; ++ }; ++ }; ++}; diff --git a/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch new file mode 100644 index 0000000000..b8c527b6ae --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch @@ -0,0 +1,52 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -47,14 +47,12 @@ + status = "ok"; + }; + +- i2c4: i2c@16380000 { +- status = "ok"; +- +- clock-frequency = <200000>; +- +- pinctrl-0 = <&i2c4_pins>; +- pinctrl-names = "default"; +- }; ++ /* ++ * 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 { +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -294,7 +294,7 @@ + .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 @@ + }, + .num_parents = 1, + .ops = &clk_branch_ops, +- .flags = CLK_SET_RATE_PARENT, ++ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -890,7 +890,7 @@ + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_h_clk", + .ops = &clk_branch_ops, +- .flags = CLK_IS_ROOT, ++ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, + }, + }, + }; diff --git a/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch new file mode 100644 index 0000000000..f026ed9394 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/024-ap148-add-memory-node.patch @@ -0,0 +1,14 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -4,6 +4,11 @@ + 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>; diff --git a/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch new file mode 100644 index 0000000000..e1d317de47 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/033-ARM-qcom-add-SFPB-nodes-to-IPQ806x-dts.patch @@ -0,0 +1,33 @@ +From c7c482da19a5e4ba7101198c21c2434056b0b2da Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 13 Aug 2015 09:45:26 -0700 +Subject: [PATCH 1/3] ARM: qcom: add SFPB nodes to IPQ806x dts + +Add one new node to the ipq806x.dtsi file to declare & register the +hardware spinlock devices. This mechanism is required to be used by +other drivers such as SMEM. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -329,5 +329,16 @@ + #reset-cells = <1>; + }; + ++ sfpb_mutex_block: syscon@1200600 { ++ compatible = "syscon"; ++ reg = <0x01200600 0x100>; ++ }; ++ }; ++ ++ sfpb_mutex: sfpb-mutex { ++ compatible = "qcom,sfpb-mutex"; ++ syscon = <&sfpb_mutex_block 4 4>; ++ ++ #hwlock-cells = <1>; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch new file mode 100644 index 0000000000..06b14052ef --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/036-ARM-qcom-add-SMEM-device-node-to-IPQ806x-dts.patch @@ -0,0 +1,36 @@ +From f212be3a6134db8dd7c5f6f0987536a669401fae Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Fri, 14 Aug 2015 11:17:20 -0700 +Subject: [PATCH 2/3] ARM: qcom: add SMEM device node to IPQ806x dts + +SMEM is used on IPQ806x to store various board related information such +as boot device and flash partition layout. We'll declare it as a device +so we can make use of it thanks to the new SMEM soc driver. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064.dtsi | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -55,7 +55,7 @@ + no-map; + }; + +- smem@41000000 { ++ smem: smem@41000000 { + reg = <0x41000000 0x200000>; + no-map; + }; +@@ -341,4 +341,10 @@ + + #hwlock-cells = <1>; + }; ++ ++ smem { ++ compatible = "qcom,smem"; ++ memory-region = <&smem>; ++ hwlocks = <&sfpb_mutex 3>; ++ }; + }; diff --git a/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch new file mode 100644 index 0000000000..d80eb86056 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/037-mtd-add-SMEM-parser-for-QCOM-platforms.patch @@ -0,0 +1,282 @@ +From 61e8e1b1af77f24339da3f0822a76fa65ed635c6 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Thu, 13 Aug 2015 09:53:14 -0700 +Subject: [PATCH] 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 +Signed-off-by: Ram Chandra Jangir +--- + 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 + +diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig +index a03ad29..debc887 100644 +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -190,6 +190,13 @@ + 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" + + # +diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile +index 99bb9a1..2a44a64 100644 +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -16,6 +16,7 @@ + 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 + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o +diff --git a/drivers/mtd/qcom_smem_part.c b/drivers/mtd/qcom_smem_part.c +new file mode 100644 +index 0000000..f9c1bca +--- /dev/null ++++ b/drivers/mtd/qcom_smem_part.c +@@ -0,0 +1,228 @@ ++/* ++ * 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 ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* 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, ++ 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 = data->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 struct mtd_part_parser qcom_smem_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_qcom_smem_partitions, ++ .name = "qcom-smem", ++}; ++ ++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 "); ++MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); diff --git a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch new file mode 100644 index 0000000000..68f2b39813 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch @@ -0,0 +1,510 @@ +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -390,4 +390,15 @@ + Enable this to support the Broadcom Cygnus PCIe PHY. + If unsure, say N. + ++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. ++ + endmenu +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210) += + obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o + obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o ++obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o +--- /dev/null ++++ b/drivers/phy/phy-qcom-dwc3.c +@@ -0,0 +1,482 @@ ++/* Copyright (c) 2013-2014, 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * 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) ++ ++/* 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) ++ ++struct qcom_dwc3_usb_phy { ++ void __iomem *base; ++ struct device *dev; ++ struct phy *phy; ++ ++ int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3); ++ int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3); ++ ++ struct clk *xo_clk; ++ struct clk *ref_clk; ++}; ++ ++/** ++ * 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(void __iomem *base, u32 addr, u32 val) ++{ ++ int ret; ++ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG); ++ if (ret) ++ goto err_wait; ++ ++ writel(val, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG); ++ if (ret) ++ goto err_wait; ++ ++ writel(0x1, base + CR_PROTOCOL_WRITE_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG); ++ ++err_wait: ++ 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; ++ bool first_read = true; ++ ++ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); ++ writel(0x1, 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. ++ */ ++retry: ++ writel(0x1, base + CR_PROTOCOL_READ_REG); ++ ++ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG); ++ if (ret) ++ goto err_wait; ++ ++ if (first_read) { ++ readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ first_read = false; ++ goto retry; ++ } ++ ++ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG); ++ ++err_wait: ++ return ret; ++} ++ ++static int qcom_dwc3_phy_power_on(struct phy *phy) ++{ ++ int ret; ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ 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; ++} ++ ++static int qcom_dwc3_phy_power_off(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_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3) ++{ ++ u32 val; ++ ++ /* ++ * 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_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3) ++{ ++ int ret; ++ u32 data = 0; ++ ++ /* reset phy */ ++ data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET, ++ phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ usleep_range(2000, 2200); ++ writel_relaxed(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_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ msleep(30); ++ ++ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; ++ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ ++ /* ++ * 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 to 3 ++ * 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 |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT; ++ data |= RX_OVRD_IN_HI_RX_EQ_OVRD; ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base, ++ 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 to 22 ++ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127 ++ * 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 |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT; ++ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; ++ data |= 0x7f; ++ data |= TX_OVRD_DRV_LO_EN; ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base, ++ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); ++ if (ret) ++ goto err_phy_trans; ++ ++ /* ++ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows ++ * TX_FULL_SWING [26:20] amplitude to 127 ++ * TX_DEEMPH_3_5DB [13:8] to 22 ++ * LOS_BIAS [2:0] to 0x5 ++ */ ++ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, ++ 0x07f03f07, 0x07f01605); ++ ++err_phy_trans: ++ return ret; ++} ++ ++static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3) ++{ ++ /* 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 ++ * 4. Disable SSPHY ref clk ++ */ ++ 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, ++ 0x0, SSUSB_CTRL_TEST_POWERDOWN); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_phy_init(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ if (phy_dwc3->phy_init) ++ return phy_dwc3->phy_init(phy_dwc3); ++ ++ return 0; ++} ++ ++static int qcom_dwc3_phy_exit(struct phy *phy) ++{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); ++ ++ if (phy_dwc3->phy_exit) ++ return qcom_dwc3_ss_phy_exit(phy_dwc3); ++ ++ return 0; ++} ++ ++static struct phy_ops qcom_dwc3_phy_ops = { ++ .init = qcom_dwc3_phy_init, ++ .exit = qcom_dwc3_phy_exit, ++ .power_on = qcom_dwc3_phy_power_on, ++ .power_off = qcom_dwc3_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct of_device_id qcom_dwc3_phy_table[] = { ++ { .compatible = "qcom,dwc3-hs-usb-phy", }, ++ { .compatible = "qcom,dwc3-ss-usb-phy", }, ++ { /* 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 resource *res; ++ ++ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); ++ if (!phy_dwc3) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy_dwc3); ++ ++ 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); ++ } ++ ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "qcom,dwc3-hs-usb-phy")) { ++ clk_set_rate(phy_dwc3->ref_clk, 60000000); ++ phy_dwc3->phy_init = qcom_dwc3_hs_phy_init; ++ } else if (of_device_is_compatible(pdev->dev.of_node, ++ "qcom,dwc3-ss-usb-phy")) { ++ phy_dwc3->phy_init = qcom_dwc3_ss_phy_init; ++ phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit; ++ clk_set_rate(phy_dwc3->ref_clk, 125000000); ++ } else { ++ dev_err(phy_dwc3->dev, "Unknown phy\n"); ++ return -EINVAL; ++ } ++ ++ 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; ++ } ++ ++ phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops); ++ ++ if (IS_ERR(phy_dwc3->phy)) ++ return PTR_ERR(phy_dwc3->phy); ++ ++ phy_set_drvdata(phy_dwc3->phy, 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 "); ++MODULE_AUTHOR("Ivan T. Ivanov "); ++MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); diff --git a/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch new file mode 100644 index 0000000000..6e6f10d549 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch @@ -0,0 +1,125 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -92,5 +92,29 @@ + 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"; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -333,6 +333,90 @@ + compatible = "syscon"; + reg = <0x01200600 0x100>; + }; ++ ++ hs_phy_1: phy@100f8800 { ++ compatible = "qcom,dwc3-hs-usb-phy"; ++ reg = <0x100f8800 0x30>; ++ clocks = <&gcc USB30_1_UTMI_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_1: phy@100f8830 { ++ compatible = "qcom,dwc3-ss-usb-phy"; ++ reg = <0x100f8830 0x30>; ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ hs_phy_0: phy@110f8800 { ++ compatible = "qcom,dwc3-hs-usb-phy"; ++ reg = <0x110f8800 0x30>; ++ clocks = <&gcc USB30_0_UTMI_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ ss_phy_0: phy@110f8830 { ++ compatible = "qcom,dwc3-ss-usb-phy"; ++ reg = <0x110f8830 0x30>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ usb3_0: usb30@0 { ++ compatible = "qcom,dwc3"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ status = "disabled"; ++ ++ dwc3@11000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x11000000 0xcd00>; ++ interrupts = <0 110 0x4>; ++ phys = <&hs_phy_0>, <&ss_phy_0>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ tx-fifo-resize; ++ dr_mode = "host"; ++ }; ++ }; ++ ++ usb3_1: usb30@1 { ++ compatible = "qcom,dwc3"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ status = "disabled"; ++ ++ dwc3@10000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x10000000 0xcd00>; ++ interrupts = <0 205 0x4>; ++ phys = <&hs_phy_1>, <&ss_phy_1>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ tx-fifo-resize; ++ dr_mode = "host"; ++ }; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch new file mode 100644 index 0000000000..41f91fae7c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch @@ -0,0 +1,263 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings +From: Stanimir Varbanov +X-Patchwork-Id: 6326181 +Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com> +To: Rob Herring , Kumar Gala , + Mark Rutland , + Grant Likely , + Bjorn Helgaas , + Kishon Vijay Abraham I , + Russell King , Arnd Bergmann +Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, + linux-pci@vger.kernel.org, Mathieu Olivari , + Srinivas Kandagatla , + Stanimir Varbanov +Date: Mon, 4 May 2015 15:42:16 +0300 + +Document Qualcomm PCIe driver devicetree bindings. + +Signed-off-by: Stanimir Varbanov + +--- +.../devicetree/bindings/pci/qcom,pcie.txt | 231 ++++++++++++++++++++ + 1 files changed, 231 insertions(+), 0 deletions(-) + create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt +@@ -0,0 +1,231 @@ ++* Qualcomm PCI express root complex ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: Value shall include ++ - "qcom,pcie-v0" for apq/ipq8064 ++ - "qcom,pcie-v1" for apq8084 ++ ++- reg: ++ Usage: required ++ Value type: ++ Definition: Register ranges as listed in the reg-names property ++ ++- reg-names: ++ Usage: required ++ Value type: ++ Definition: Must include the following entries ++ - "parf" Qualcomm specific registers ++ - "dbi" Designware PCIe registers ++ - "elbi" External local bus interface registers ++ - "config" PCIe configuration space ++ ++- device_type: ++ Usage: required ++ Value type: ++ Definition: Should be "pci". As specified in designware-pcie.txt ++ ++- #address-cells: ++ Usage: required ++ Value type: ++ Definition: Should be set to 3. As specified in designware-pcie.txt ++ ++- #size-cells: ++ Usage: required ++ Value type: ++ Definition: Should be set 2. As specified in designware-pcie.txt ++ ++- ranges: ++ Usage: required ++ Value type: ++ Definition: As specified in designware-pcie.txt ++ ++- interrupts: ++ Usage: required ++ Value type: ++ Definition: MSI interrupt ++ ++- interrupt-names: ++ Usage: required ++ Value type: ++ Definition: Should contain "msi" ++ ++- #interrupt-cells: ++ Usage: required ++ Value type: ++ Definition: Should be 1. As specified in designware-pcie.txt ++ ++- interrupt-map-mask: ++ Usage: required ++ Value type: ++ Definition: As specified in designware-pcie.txt ++ ++- interrupt-map: ++ Usage: required ++ Value type: ++ Definition: As specified in designware-pcie.txt ++ ++- clocks: ++ Usage: required ++ Value type: ++ Definition: List of phandle and clock specifier pairs as listed ++ in clock-names property ++ ++- clock-names: ++ Usage: required ++ Value type: ++ Definition: Should contain the following entries ++ * should be populated for v0 and v1 ++ - "iface" Configuration AHB clock ++ ++ * should be populated for v0 ++ - "core" Clocks the pcie hw block ++ - "phy" Clocks the pcie PHY block ++ ++ * should be populated for v1 ++ - "aux" Auxiliary (AUX) clock ++ - "bus_master" Master AXI clock ++ - "bus_slave" Slave AXI clock ++ ++- resets: ++ Usage: required ++ Value type: ++ Definition: List of phandle and reset specifier pairs as listed ++ in reset-names property ++ ++- reset-names: ++ Usage: required ++ Value type: ++ Definition: Should contain the following entries ++ * should be populated for v0 ++ - "axi" AXI reset ++ - "ahb" AHB reset ++ - "por" POR reset ++ - "pci" PCI reset ++ - "phy" PHY reset ++ ++ * should be populated for v1 ++ - "core" Core reset ++ ++- power-domains: ++ Usage: required (for v1 only) ++ Value type: ++ Definition: A phandle and power domain specifier pair to the ++ power domain which is responsible for collapsing ++ and restoring power to the peripheral ++ ++- -supply: ++ Usage: required ++ Value type: ++ Definition: List of phandles to the power supply regulator(s) ++ * should be populated for v0 and v1 ++ - "vdda" core analog power supply ++ ++ * should be populated for v0 ++ - "vdda_phy" analog power supply for PHY ++ - "vdda_refclk" analog power supply for IC which generate ++ reference clock ++ ++- phys: ++ Usage: required (for v1 only) ++ Value type: ++ Definition: List of phandle(s) as listed in phy-names property ++ ++- phy-names: ++ Usage: required (for v1 only) ++ Value type: ++ Definition: Should contain "pciephy" ++ ++- -gpio: ++ Usage: optional ++ Value type: ++ Definition: List of phandle and gpio specifier pairs. Should contain ++ - "perst" PCIe endpoint reset signal line ++ - "pewake" PCIe endpoint wake signal line ++ ++- pinctrl-0: ++ Usage: required ++ Value type: ++ Definition: List of phandles pointing at a pin(s) configuration ++ ++- pinctrl-names ++ Usage: required ++ Value type: ++ Definition: List of names of pinctrl-0 state ++ ++* Example for v0 ++ pcie0: pci@1b500000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ 0x0ff00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */ ++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ resets = <&gcc PCIE_ACLK_RESET>, ++ <&gcc PCIE_HCLK_RESET>, ++ <&gcc PCIE_POR_RESET>, ++ <&gcc PCIE_PCI_RESET>, ++ <&gcc PCIE_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ }; ++ ++* Example for v1 ++ pcie0@fc520000 { ++ compatible = "qcom,pcie-v1"; ++ reg = <0xfc520000 0x2000>, ++ <0xff000000 0x1000>, ++ <0xff001000 0x1000>, ++ <0xff002000 0x2000>; ++ reg-names = "parf", "dbi", "elbi", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */ ++ 0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>, ++ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, ++ <&gcc GCC_PCIE_0_SLV_AXI_CLK>, ++ <&gcc GCC_PCIE_0_AUX_CLK>; ++ clock-names = "iface", "master_bus", "slave_bus", "aux"; ++ resets = <&gcc GCC_PCIE_0_BCR>; ++ reset-names = "core"; ++ power-domains = <&gcc PCIE0_GDSC>; ++ vdda-supply = <&pma8084_l3>; ++ phys = <&pciephy0>; ++ phy-names = "pciephy"; ++ perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>; ++ pinctrl-0 = <&pcie0_pins_default>; ++ pinctrl-names = "default"; ++ }; diff --git a/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch new file mode 100644 index 0000000000..ad1a1b9cb3 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch @@ -0,0 +1,752 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver +From: Stanimir Varbanov +X-Patchwork-Id: 6326161 +Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com> +To: Rob Herring , Kumar Gala , + Mark Rutland , + Grant Likely , + Bjorn Helgaas , + Kishon Vijay Abraham I , + Russell King , Arnd Bergmann +Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, + linux-pci@vger.kernel.org, Mathieu Olivari , + Srinivas Kandagatla , + Stanimir Varbanov +Date: Mon, 4 May 2015 15:42:17 +0300 + +The PCIe driver reuse the Designware common code for host +and MSI initialization, and also program the Qualcomm +application specific registers. + +Signed-off-by: Stanimir Varbanov + +--- +MAINTAINERS | 7 + + drivers/pci/host/Kconfig | 9 + + drivers/pci/host/Makefile | 1 + + drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 694 insertions(+), 0 deletions(-) + create mode 100644 drivers/pci/host/pcie-qcom.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -8253,6 +8253,13 @@ + F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt + F: drivers/pci/host/pcie-hisi.c + ++PCIE DRIVER FOR QUALCOMM MSM ++M: Stanimir Varbanov ++L: linux-pci@vger.kernel.org ++L: linux-arm-msm@vger.kernel.org ++S: Maintained ++F: drivers/pci/host/*qcom* ++ + PCMCIA SUBSYSTEM + P: Linux PCMCIA Team + L: linux-pcmcia@lists.infradead.org +--- a/drivers/pci/host/Kconfig ++++ b/drivers/pci/host/Kconfig +@@ -173,4 +173,13 @@ + help + Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC + ++config PCIE_QCOM ++ bool "Qualcomm PCIe controller" ++ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST) ++ select PCIE_DW ++ select PCIEPORTBUS ++ help ++ Say Y here to enable PCIe controller support on Qualcomm SoCs. The ++ PCIe controller use Designware core plus Qualcomm specific hardware ++ wrappers. + endmenu +--- /dev/null ++++ b/drivers/pci/host/pcie-qcom.c +@@ -0,0 +1,676 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pcie-designware.h" ++ ++#define PCIE20_PARF_PHY_CTRL 0x40 ++#define PCIE20_PARF_PHY_REFCLK 0x4C ++#define PCIE20_PARF_DBI_BASE_ADDR 0x168 ++#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c ++#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 ++ ++#define PCIE20_ELBI_SYS_CTRL 0x04 ++#define PCIE20_ELBI_SYS_STTS 0x08 ++#define XMLH_LINK_UP BIT(10) ++ ++#define PCIE20_CAP 0x70 ++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) ++ ++#define PERST_DELAY_MIN_US 1000 ++#define PERST_DELAY_MAX_US 1005 ++ ++#define LINKUP_DELAY_MIN_US 5000 ++#define LINKUP_DELAY_MAX_US 5100 ++#define LINKUP_RETRIES_COUNT 20 ++ ++#define PCIE_V0 0 /* apq8064 */ ++#define PCIE_V1 1 /* apq8084 */ ++ ++struct qcom_pcie_resources_v0 { ++ struct clk *iface_clk; ++ struct clk *core_clk; ++ struct clk *phy_clk; ++ struct reset_control *pci_reset; ++ struct reset_control *axi_reset; ++ struct reset_control *ahb_reset; ++ struct reset_control *por_reset; ++ struct reset_control *phy_reset; ++ struct regulator *vdda; ++ struct regulator *vdda_phy; ++ struct regulator *vdda_refclk; ++}; ++ ++struct qcom_pcie_resources_v1 { ++ struct clk *iface; ++ struct clk *aux; ++ struct clk *master_bus; ++ struct clk *slave_bus; ++ struct reset_control *core; ++ struct regulator *vdda; ++}; ++ ++union pcie_resources { ++ struct qcom_pcie_resources_v0 v0; ++ struct qcom_pcie_resources_v1 v1; ++}; ++ ++struct qcom_pcie { ++ struct pcie_port pp; ++ struct device *dev; ++ union pcie_resources res; ++ void __iomem *parf; ++ void __iomem *dbi; ++ void __iomem *elbi; ++ struct phy *phy; ++ struct gpio_desc *reset; ++ unsigned int version; ++}; ++ ++#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) ++ ++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_deassert(struct qcom_pcie *pcie, int assert) ++{ ++ int val, active_low; ++ ++ if (IS_ERR_OR_NULL(pcie->reset)) ++ return; ++ ++ active_low = gpiod_is_active_low(pcie->reset); ++ ++ if (assert) ++ val = !!active_low; ++ else ++ val = !active_low; ++ ++ gpiod_set_value(pcie->reset, val); ++ ++ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); ++} ++ ++static void qcom_ep_reset_assert(struct qcom_pcie *pcie) ++{ ++ qcom_ep_reset_assert_deassert(pcie, 1); ++} ++ ++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) ++{ ++ qcom_ep_reset_assert_deassert(pcie, 0); ++} ++ ++static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) ++{ ++ struct pcie_port *pp = arg; ++ ++ return dw_handle_msi_irq(pp); ++} ++ ++static int qcom_pcie_link_up(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS); ++ ++ return val & BIT(29) ? 1 : 0; ++} ++ ++static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ ++ reset_control_assert(res->pci_reset); ++ reset_control_assert(res->axi_reset); ++ reset_control_assert(res->ahb_reset); ++ reset_control_assert(res->por_reset); ++ reset_control_assert(res->pci_reset); ++ clk_disable_unprepare(res->iface_clk); ++ clk_disable_unprepare(res->core_clk); ++ clk_disable_unprepare(res->phy_clk); ++ regulator_disable(res->vdda); ++ regulator_disable(res->vdda_phy); ++ regulator_disable(res->vdda_refclk); ++} ++ ++static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ ++ reset_control_assert(res->core); ++ clk_disable_unprepare(res->slave_bus); ++ clk_disable_unprepare(res->master_bus); ++ clk_disable_unprepare(res->iface); ++ clk_disable_unprepare(res->aux); ++ regulator_disable(res->vdda); ++} ++ ++static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ ret = regulator_enable(res->vdda); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda regulator\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(res->vdda_refclk); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda_refclk regulator\n"); ++ goto err_refclk; ++ } ++ ++ ret = regulator_enable(res->vdda_phy); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda_phy regulator\n"); ++ goto err_vdda_phy; ++ } ++ ++ ret = clk_prepare_enable(res->iface_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable iface clock\n"); ++ goto err_iface; ++ } ++ ++ 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 = reset_control_deassert(res->ahb_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert ahb reset\n"); ++ goto err_reset_ahb; ++ } ++ ++ return 0; ++ ++err_reset_ahb: ++ 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_iface: ++ regulator_disable(res->vdda_phy); ++err_vdda_phy: ++ regulator_disable(res->vdda_refclk); ++err_refclk: ++ regulator_disable(res->vdda); ++ return ret; ++} ++ ++static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ ret = reset_control_deassert(res->core); ++ if (ret) { ++ dev_err(dev, "cannot deassert core reset\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(res->aux); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable aux clock\n"); ++ goto err_res; ++ } ++ ++ ret = clk_prepare_enable(res->iface); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable iface clock\n"); ++ goto err_aux; ++ } ++ ++ ret = clk_prepare_enable(res->master_bus); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable master_bus clock\n"); ++ goto err_iface; ++ } ++ ++ ret = clk_prepare_enable(res->slave_bus); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable slave_bus clock\n"); ++ goto err_master; ++ } ++ ++ ret = regulator_enable(res->vdda); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda regulator\n"); ++ goto err_slave; ++ } ++ ++ return 0; ++ ++err_slave: ++ clk_disable_unprepare(res->slave_bus); ++err_master: ++ clk_disable_unprepare(res->master_bus); ++err_iface: ++ clk_disable_unprepare(res->iface); ++err_aux: ++ clk_disable_unprepare(res->aux); ++err_res: ++ reset_control_assert(res->core); ++ ++ return ret; ++} ++ ++static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ ++ res->vdda = devm_regulator_get(dev, "vdda"); ++ if (IS_ERR(res->vdda)) ++ return PTR_ERR(res->vdda); ++ ++ res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); ++ if (IS_ERR(res->vdda_phy)) ++ return PTR_ERR(res->vdda_phy); ++ ++ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); ++ if (IS_ERR(res->vdda_refclk)) ++ return PTR_ERR(res->vdda_refclk); ++ ++ res->iface_clk = devm_clk_get(dev, "iface"); ++ if (IS_ERR(res->iface_clk)) ++ return PTR_ERR(res->iface_clk); ++ ++ res->core_clk = devm_clk_get(dev, "core"); ++ if (IS_ERR(res->core_clk)) ++ return PTR_ERR(res->core_clk); ++ ++ res->phy_clk = devm_clk_get(dev, "phy"); ++ if (IS_ERR(res->phy_clk)) ++ return PTR_ERR(res->phy_clk); ++ ++ res->pci_reset = devm_reset_control_get(dev, "pci"); ++ if (IS_ERR(res->pci_reset)) ++ return PTR_ERR(res->pci_reset); ++ ++ res->axi_reset = devm_reset_control_get(dev, "axi"); ++ if (IS_ERR(res->axi_reset)) ++ return PTR_ERR(res->axi_reset); ++ ++ res->ahb_reset = devm_reset_control_get(dev, "ahb"); ++ if (IS_ERR(res->ahb_reset)) ++ return PTR_ERR(res->ahb_reset); ++ ++ res->por_reset = devm_reset_control_get(dev, "por"); ++ if (IS_ERR(res->por_reset)) ++ return PTR_ERR(res->por_reset); ++ ++ res->phy_reset = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(res->phy_reset)) ++ return PTR_ERR(res->phy_reset); ++ ++ return 0; ++} ++ ++static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ struct device *dev = pcie->dev; ++ ++ res->vdda = devm_regulator_get(dev, "vdda"); ++ if (IS_ERR(res->vdda)) ++ return PTR_ERR(res->vdda); ++ ++ res->iface = devm_clk_get(dev, "iface"); ++ if (IS_ERR(res->iface)) ++ return PTR_ERR(res->iface); ++ ++ res->aux = devm_clk_get(dev, "aux"); ++ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ else if (IS_ERR(res->aux)) ++ res->aux = NULL; ++ ++ res->master_bus = devm_clk_get(dev, "master_bus"); ++ if (IS_ERR(res->master_bus)) ++ return PTR_ERR(res->master_bus); ++ ++ res->slave_bus = devm_clk_get(dev, "slave_bus"); ++ if (IS_ERR(res->slave_bus)) ++ return PTR_ERR(res->slave_bus); ++ ++ res->core = devm_reset_control_get(dev, "core"); ++ if (IS_ERR(res->core)) ++ return PTR_ERR(res->core); ++ ++ return 0; ++} ++ ++static int qcom_pcie_enable_link_training(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ struct device *dev = pp->dev; ++ int retries; ++ u32 val; ++ ++ /* enable link training */ ++ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); ++ ++ /* wait for up to 100ms for the link to come up */ ++ retries = LINKUP_RETRIES_COUNT; ++ do { ++ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS); ++ if (val & XMLH_LINK_UP) ++ break; ++ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US); ++ } while (retries--); ++ ++ if (retries < 0 || !dw_pcie_link_up(pp)) { ++ dev_err(dev, "link initialization failed\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void qcom_pcie_host_init_v1(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ int ret; ++ ++ qcom_ep_reset_assert(pcie); ++ ++ ret = qcom_pcie_enable_resources_v1(pcie); ++ if (ret) ++ return; ++ ++ /* change DBI base address */ ++ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, ++ 0, BIT(31)); ++ ++ ret = phy_init(pcie->phy); ++ if (ret) ++ goto err_res; ++ ++ ret = phy_power_on(pcie->phy); ++ if (ret) ++ goto err_phy; ++ ++ dw_pcie_setup_rc(pp); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ dw_pcie_msi_init(pp); ++ ++ qcom_ep_reset_deassert(pcie); ++ ++ ret = qcom_pcie_enable_link_training(pp); ++ if (ret) ++ goto err; ++ ++ return; ++ ++err: ++ qcom_ep_reset_assert(pcie); ++ phy_power_off(pcie->phy); ++err_phy: ++ phy_exit(pcie->phy); ++err_res: ++ qcom_pcie_disable_resources_v1(pcie); ++} ++ ++static void qcom_pcie_host_init_v0(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ qcom_ep_reset_assert(pcie); ++ ++ ret = qcom_pcie_enable_resources_v0(pcie); ++ if (ret) ++ return; ++ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); ++ ++ /* enable external reference clock */ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); ++ ++ ret = reset_control_deassert(res->phy_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert phy reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->pci_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert pci reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->por_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert por reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->axi_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert axi reset\n"); ++ return; ++ } ++ ++ /* wait 150ms for clock acquisition */ ++ usleep_range(10000, 15000); ++ ++ dw_pcie_setup_rc(pp); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ dw_pcie_msi_init(pp); ++ ++ qcom_ep_reset_deassert(pcie); ++ ++ ret = qcom_pcie_enable_link_training(pp); ++ if (ret) ++ goto err; ++ ++ return; ++err: ++ qcom_ep_reset_assert(pcie); ++ qcom_pcie_disable_resources_v0(pcie); ++} ++ ++static void qcom_pcie_host_init(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ ++ if (pcie->version == PCIE_V0) ++ return qcom_pcie_host_init_v0(pp); ++ else ++ return qcom_pcie_host_init_v1(pp); ++} ++ ++static int ++qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) ++{ ++ /* the device class is not reported correctly from the register */ ++ if (where == PCI_CLASS_REVISION && size == 4) { ++ *val = readl(pp->dbi_base + PCI_CLASS_REVISION); ++ *val &= ~(0xffff << 16); ++ *val |= PCI_CLASS_BRIDGE_PCI << 16; ++ return PCIBIOS_SUCCESSFUL; ++ } ++ ++ return dw_pcie_cfg_read(pp->dbi_base + where, size, val); ++} ++ ++static struct pcie_host_ops qcom_pcie_ops = { ++ .link_up = qcom_pcie_link_up, ++ .host_init = qcom_pcie_host_init, ++ .rd_own_conf = qcom_pcie_rd_own_conf, ++}; ++ ++static const struct of_device_id qcom_pcie_match[] = { ++ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 }, ++ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 }, ++ { } ++}; ++ ++static int qcom_pcie_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *match; ++ struct resource *res; ++ struct qcom_pcie *pcie; ++ struct pcie_port *pp; ++ int ret; ++ ++ match = of_match_node(qcom_pcie_match, dev->of_node); ++ if (!match) ++ return -ENXIO; ++ ++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); ++ if (!pcie) ++ return -ENOMEM; ++ ++ pcie->version = (unsigned int)match->data; ++ ++ pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); ++ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER) ++ return PTR_ERR(pcie->reset); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); ++ pcie->parf = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->parf)) ++ return PTR_ERR(pcie->parf); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); ++ pcie->dbi = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->dbi)) ++ return PTR_ERR(pcie->dbi); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); ++ pcie->elbi = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->elbi)) ++ return PTR_ERR(pcie->elbi); ++ ++ pcie->phy = devm_phy_optional_get(dev, "pciephy"); ++ if (IS_ERR(pcie->phy)) ++ return PTR_ERR(pcie->phy); ++ ++ pcie->dev = dev; ++ ++ if (pcie->version == PCIE_V0) ++ ret = qcom_pcie_get_resources_v0(pcie); ++ else ++ ret = qcom_pcie_get_resources_v1(pcie); ++ ++ if (ret) ++ return ret; ++ ++ pp = &pcie->pp; ++ pp->dev = dev; ++ pp->dbi_base = pcie->dbi; ++ pp->root_bus_nr = -1; ++ pp->ops = &qcom_pcie_ops; ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) { ++ pp->msi_irq = platform_get_irq_byname(pdev, "msi"); ++ if (pp->msi_irq < 0) { ++ dev_err(dev, "cannot get msi irq\n"); ++ return pp->msi_irq; ++ } ++ ++ ret = devm_request_irq(dev, pp->msi_irq, ++ qcom_pcie_msi_irq_handler, ++ IRQF_SHARED, "qcom-pcie-msi", pp); ++ if (ret) { ++ dev_err(dev, "cannot request msi irq\n"); ++ return ret; ++ } ++ } ++ ++ ret = dw_pcie_host_init(pp); ++ if (ret) { ++ dev_err(dev, "cannot initialize host\n"); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, pcie); ++ ++ return 0; ++} ++ ++static int qcom_pcie_remove(struct platform_device *pdev) ++{ ++ struct qcom_pcie *pcie = platform_get_drvdata(pdev); ++ ++ qcom_ep_reset_assert(pcie); ++ phy_power_off(pcie->phy); ++ phy_exit(pcie->phy); ++ if (pcie->version == PCIE_V0) ++ qcom_pcie_disable_resources_v0(pcie); ++ else ++ qcom_pcie_disable_resources_v1(pcie); ++ ++ return 0; ++} ++ ++static struct platform_driver qcom_pcie_driver = { ++ .probe = qcom_pcie_probe, ++ .remove = qcom_pcie_remove, ++ .driver = { ++ .name = "qcom-pcie", ++ .of_match_table = qcom_pcie_match, ++ }, ++}; ++ ++module_platform_driver(qcom_pcie_driver); ++ ++MODULE_AUTHOR("Stanimir Varbanov "); ++MODULE_DESCRIPTION("Qualcomm PCIe root complex driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-pcie"); +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -20,3 +20,4 @@ + obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o + obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o + obj-$(CONFIG_PCI_HISI) += pcie-hisi.o ++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o diff --git a/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch new file mode 100644 index 0000000000..1246bfeda9 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch @@ -0,0 +1,244 @@ +From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 21 Apr 2015 19:01:42 -0700 +Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms + +qcom-pcie driver now supports version 0 of the controller. This change +adds the corresponding entries to the IPQ806x dtsi file and +corresponding platform (AP148). + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 30 ++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 124 +++++++++++++++++++++++++++++++ + 2 files changed, 154 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -116,5 +116,15 @@ + usb30@1 { + status = "ok"; + }; ++ ++ pcie0: pci@1b500000 { ++ status = "ok"; ++ phy-tx0-term-offset = <7>; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ phy-tx0-term-offset = <7>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -128,5 +128,17 @@ + usb30@1 { + status = "ok"; + }; ++ ++ pcie0: pci@1b500000 { ++ status = "ok"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ }; ++ ++ pcie2: pci@1b900000 { ++ status = "ok"; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -4,6 +4,9 @@ + #include + #include + #include ++#include ++#include ++#include + + / { + model = "Qualcomm IPQ8064"; +@@ -99,6 +102,33 @@ + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0 16 0x4>; ++ ++ pcie0_pins: pcie0_pinmux { ++ mux { ++ pins = "gpio3"; ++ function = "pcie1_rst"; ++ drive-strength = <12>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie1_pins: pcie1_pinmux { ++ mux { ++ pins = "gpio48"; ++ function = "pcie2_rst"; ++ drive-strength = <12>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie2_pins: pcie2_pinmux { ++ mux { ++ pins = "gpio63"; ++ function = "pcie3_rst"; ++ drive-strength = <12>; ++ bias-disable; ++ }; ++ }; + }; + + intc: interrupt-controller@2000000 { +@@ -417,6 +447,144 @@ + dr_mode = "host"; + }; + }; ++ ++ pcie0: pci@1b500000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ 0x0ff00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0x0fe00000 0x0fe00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x08000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ ++ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_ACLK_RESET>, ++ <&gcc PCIE_HCLK_RESET>, ++ <&gcc PCIE_POR_RESET>, ++ <&gcc PCIE_PCI_RESET>, ++ <&gcc PCIE_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ pinctrl-0 = <&pcie0_pins>; ++ pinctrl-names = "default"; ++ ++ perst-gpio = <&qcom_pinmux 3 GPIO_ACTIVE_LOW>; ++ ++ status = "disabled"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b700000 0x1000 ++ 0x1b702000 0x80 ++ 0x1b800000 0x100 ++ 0x31f00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <1>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0x31e00000 0x31e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x2e000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ ++ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_1_A_CLK>, ++ <&gcc PCIE_1_H_CLK>, ++ <&gcc PCIE_1_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_1_ACLK_RESET>, ++ <&gcc PCIE_1_HCLK_RESET>, ++ <&gcc PCIE_1_POR_RESET>, ++ <&gcc PCIE_1_PCI_RESET>, ++ <&gcc PCIE_1_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ pinctrl-0 = <&pcie1_pins>; ++ pinctrl-names = "default"; ++ ++ perst-gpio = <&qcom_pinmux 48 GPIO_ACTIVE_LOW>; ++ ++ status = "disabled"; ++ }; ++ ++ pcie2: pci@1b900000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b900000 0x1000 ++ 0x1b902000 0x80 ++ 0x1ba00000 0x100 ++ 0x35f00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <2>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0x35e00000 0x35e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x32000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */ ++ ++ interrupts = ; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_2_A_CLK>, ++ <&gcc PCIE_2_H_CLK>, ++ <&gcc PCIE_2_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_2_ACLK_RESET>, ++ <&gcc PCIE_2_HCLK_RESET>, ++ <&gcc PCIE_2_POR_RESET>, ++ <&gcc PCIE_2_PCI_RESET>, ++ <&gcc PCIE_2_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ pinctrl-0 = <&pcie2_pins>; ++ pinctrl-names = "default"; ++ ++ perst-gpio = <&qcom_pinmux 63 GPIO_ACTIVE_LOW>; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch new file mode 100644 index 0000000000..e2d31354ed --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch @@ -0,0 +1,29 @@ +From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 21 Apr 2015 19:09:07 -0700 +Subject: [PATCH 9/9] 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 +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -5,6 +5,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. + diff --git a/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch new file mode 100644 index 0000000000..6328113c06 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/114-pcie-add-ctlr-init.patch @@ -0,0 +1,311 @@ +--- a/drivers/pci/host/pcie-qcom.c ++++ b/drivers/pci/host/pcie-qcom.c +@@ -29,8 +29,53 @@ + + #include "pcie-designware.h" + ++/* DBI registers */ ++#define PCIE20_CAP 0x70 ++#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 ++ ++/* 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_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_CONFIG_BITS 0x50 ++#define PHY_RX0_EQ(x) (x << 24) ++ + #define PCIE20_PARF_DBI_BASE_ADDR 0x168 + #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c + #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 +@@ -39,9 +84,6 @@ + #define PCIE20_ELBI_SYS_STTS 0x08 + #define XMLH_LINK_UP BIT(10) + +-#define PCIE20_CAP 0x70 +-#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) +- + #define PERST_DELAY_MIN_US 1000 + #define PERST_DELAY_MAX_US 1005 + +@@ -56,14 +98,18 @@ struct qcom_pcie_resources_v0 { + 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; + 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; ++ uint8_t phy_tx0_term_offset; + }; + + struct qcom_pcie_resources_v1 { +@@ -106,20 +152,10 @@ writel_masked(void __iomem *addr, u32 cl + + static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) + { +- int val, active_low; +- + if (IS_ERR_OR_NULL(pcie->reset)) + return; + +- active_low = gpiod_is_active_low(pcie->reset); +- +- if (assert) +- val = !!active_low; +- else +- val = !active_low; +- +- gpiod_set_value(pcie->reset, val); +- ++ gpiod_set_value(pcie->reset, assert); + usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); + } + +@@ -156,10 +192,13 @@ static void qcom_pcie_disable_resources_ + reset_control_assert(res->axi_reset); + reset_control_assert(res->ahb_reset); + reset_control_assert(res->por_reset); +- reset_control_assert(res->pci_reset); ++ reset_control_assert(res->phy_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); ++ 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); +@@ -201,6 +240,12 @@ static int qcom_pcie_enable_resources_v0 + goto err_vdda_phy; + } + ++ ret = reset_control_deassert(res->ext_reset); ++ if (ret) { ++ 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"); +@@ -219,21 +264,40 @@ static int qcom_pcie_enable_resources_v0 + goto err_clk_phy; + } + ++ ret = clk_prepare_enable(res->aux_clk); ++ if (ret) { ++ 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); + if (ret) { + dev_err(dev, "cannot deassert ahb reset\n"); + goto err_reset_ahb; + } ++ udelay(1); + + return 0; + + err_reset_ahb: ++ 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_iface: ++ reset_control_assert(res->ext_reset); ++err_reset_ext: + regulator_disable(res->vdda_phy); + err_vdda_phy: + regulator_disable(res->vdda_refclk); +@@ -329,6 +393,14 @@ static int qcom_pcie_get_resources_v0(st + 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(dev, "pci"); + if (IS_ERR(res->pci_reset)) + return PTR_ERR(res->pci_reset); +@@ -349,6 +421,14 @@ static int qcom_pcie_get_resources_v0(st + if (IS_ERR(res->phy_reset)) + return PTR_ERR(res->phy_reset); + ++ res->ext_reset = devm_reset_control_get(dev, "ext"); ++ 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; ++ + return 0; + } + +@@ -461,6 +541,57 @@ err_res: + qcom_pcie_disable_resources_v1(pcie); + } + ++static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev) ++{ ++ struct pcie_port *pp = &pcie->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->dbi + PCIE20_PLR_IATU_VIEWPORT); ++ ++ writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->dbi + PCIE20_PLR_IATU_CTRL1); ++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); ++ writel(pp->cfg0_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); ++ writel((pp->cfg0_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); ++ writel((pp->cfg0_base + pp->cfg0_size - 1), ++ pcie->dbi + PCIE20_PLR_IATU_LAR); ++ writel(busdev, pcie->dbi + PCIE20_PLR_IATU_LTAR); ++ writel(0, pcie->dbi + PCIE20_PLR_IATU_UTAR); ++} ++ ++static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie) ++{ ++ struct pcie_port *pp = &pcie->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->dbi + PCIE20_PLR_IATU_VIEWPORT); ++ ++ writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->dbi + PCIE20_PLR_IATU_CTRL1); ++ writel(PCIE20_PLR_IATU_ENABLE, pcie->dbi + PCIE20_PLR_IATU_CTRL2); ++ writel(pp->mem_base, pcie->dbi + PCIE20_PLR_IATU_LBAR); ++ writel((pp->mem_base >> 32), pcie->dbi + PCIE20_PLR_IATU_UBAR); ++ writel(pp->mem_base + pp->mem_size - 1, ++ pcie->dbi + PCIE20_PLR_IATU_LAR); ++ writel(pp->mem_bus_addr, pcie->dbi + PCIE20_PLR_IATU_LTAR); ++ writel(upper_32_bits(pp->mem_bus_addr), ++ pcie->dbi + PCIE20_PLR_IATU_UTAR); ++ ++ /* 256B PCIE buffer setting */ ++ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); ++ writel(0x1, pcie->dbi + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); ++} ++ + static void qcom_pcie_host_init_v0(struct pcie_port *pp) + { + struct qcom_pcie *pcie = to_qcom_pcie(pp); +@@ -470,15 +601,34 @@ static void qcom_pcie_host_init_v0(struc + + qcom_ep_reset_assert(pcie); + ++ reset_control_assert(res->ahb_reset); ++ + ret = qcom_pcie_enable_resources_v0(pcie); + if (ret) + return; + + writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); + +- /* enable external reference clock */ +- writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); ++ /* 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); + ++ /* De-assert PHY, PCIe, POR and AXI resets */ + ret = reset_control_deassert(res->phy_reset); + if (ret) { + dev_err(dev, "cannot deassert phy reset\n"); +@@ -517,6 +667,9 @@ static void qcom_pcie_host_init_v0(struc + if (ret) + goto err; + ++ qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR); ++ qcom_pcie_prog_viewport_mem2_outbound(pcie); ++ + return; + err: + qcom_ep_reset_assert(pcie); diff --git a/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch new file mode 100644 index 0000000000..8ceace9523 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/115-add-pcie-aux-clk-dts.patch @@ -0,0 +1,80 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -476,15 +476,21 @@ + + clocks = <&gcc PCIE_A_CLK>, + <&gcc PCIE_H_CLK>, +- <&gcc PCIE_PHY_CLK>; +- clock-names = "core", "iface", "phy"; ++ <&gcc PCIE_PHY_CLK>, ++ <&gcc PCIE_AUX_CLK>, ++ <&gcc PCIE_ALT_REF_CLK>; ++ clock-names = "core", "iface", "phy", "aux", "ref"; ++ ++ assigned-clocks = <&gcc PCIE_ALT_REF_CLK>; ++ assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_ACLK_RESET>, + <&gcc PCIE_HCLK_RESET>, + <&gcc PCIE_POR_RESET>, + <&gcc PCIE_PCI_RESET>, +- <&gcc PCIE_PHY_RESET>; +- reset-names = "axi", "ahb", "por", "pci", "phy"; ++ <&gcc PCIE_PHY_RESET>, ++ <&gcc PCIE_EXT_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie0_pins>; + pinctrl-names = "default"; +@@ -522,15 +528,21 @@ + + clocks = <&gcc PCIE_1_A_CLK>, + <&gcc PCIE_1_H_CLK>, +- <&gcc PCIE_1_PHY_CLK>; +- clock-names = "core", "iface", "phy"; ++ <&gcc PCIE_1_PHY_CLK>, ++ <&gcc PCIE_1_AUX_CLK>, ++ <&gcc PCIE_1_ALT_REF_CLK>; ++ clock-names = "core", "iface", "phy", "aux", "ref"; ++ ++ assigned-clocks = <&gcc PCIE_1_ALT_REF_CLK>; ++ assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_1_ACLK_RESET>, + <&gcc PCIE_1_HCLK_RESET>, + <&gcc PCIE_1_POR_RESET>, + <&gcc PCIE_1_PCI_RESET>, +- <&gcc PCIE_1_PHY_RESET>; +- reset-names = "axi", "ahb", "por", "pci", "phy"; ++ <&gcc PCIE_1_PHY_RESET>, ++ <&gcc PCIE_1_EXT_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie1_pins>; + pinctrl-names = "default"; +@@ -568,15 +580,21 @@ + + clocks = <&gcc PCIE_2_A_CLK>, + <&gcc PCIE_2_H_CLK>, +- <&gcc PCIE_2_PHY_CLK>; +- clock-names = "core", "iface", "phy"; ++ <&gcc PCIE_2_PHY_CLK>, ++ <&gcc PCIE_2_AUX_CLK>, ++ <&gcc PCIE_2_ALT_REF_CLK>; ++ clock-names = "core", "iface", "phy", "aux", "ref"; ++ ++ assigned-clocks = <&gcc PCIE_2_ALT_REF_CLK>; ++ assigned-clock-rates = <100000000>; + + resets = <&gcc PCIE_2_ACLK_RESET>, + <&gcc PCIE_2_HCLK_RESET>, + <&gcc PCIE_2_POR_RESET>, + <&gcc PCIE_2_PCI_RESET>, +- <&gcc PCIE_2_PHY_RESET>; +- reset-names = "axi", "ahb", "por", "pci", "phy"; ++ <&gcc PCIE_2_PHY_RESET>, ++ <&gcc PCIE_2_EXT_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; + + pinctrl-0 = <&pcie2_pins>; + pinctrl-names = "default"; diff --git a/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch new file mode 100644 index 0000000000..7daa93163b --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/126-add-rpm-to-ipq8064-dts.patch @@ -0,0 +1,87 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -2,6 +2,7 @@ + + #include "skeleton.dtsi" + #include ++#include + #include + #include + #include +@@ -93,6 +94,63 @@ + reg-names = "lpass-lpaif"; + }; + ++ rpm@108000 { ++ compatible = "qcom,rpm-ipq8064"; ++ reg = <0x108000 0x1000>; ++ qcom,ipc = <&l2cc 0x8 2>; ++ ++ interrupts = <0 19 0>, ++ <0 21 0>, ++ <0 22 0>; ++ interrupt-names = "ack", ++ "err", ++ "wakeup"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ smb208_s1a: smb208-s1a { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ ++ }; ++ ++ smb208_s1b: smb208-s1b { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2a: smb208-s2a { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2b: smb208-s2b { ++ compatible = "qcom,rpm-smb208"; ++ reg = ; ++ ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ }; ++ + qcom_pinmux: pinmux@800000 { + compatible = "qcom,ipq8064-pinctrl"; + reg = <0x800000 0x4000>; +@@ -164,6 +222,12 @@ + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; + }; + ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc", "syscon"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; ++ }; ++ + saw0: regulator@2089000 { + compatible = "qcom,saw2"; + reg = <0x02089000 0x1000>, <0x02009000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch new file mode 100644 index 0000000000..01bd7497d1 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/133-ARM-Add-Krait-L2-register-accessor-functions.patch @@ -0,0 +1,144 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions +From: Stephen Boyd +X-Patchwork-Id: 6063051 +Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , + Mark Rutland , Russell King , + Courtney Cavin +Date: Fri, 20 Mar 2015 23:45:20 -0700 + +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 +Cc: Russell King +Cc: Courtney Cavin +Signed-off-by: Stephen Boyd + +--- +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 +@@ -9,6 +9,9 @@ + 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-$(CONFIG_ICST) += icst.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 ++#include ++ ++#include ++#include ++ ++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 diff --git a/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch new file mode 100644 index 0000000000..acf5820e6c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/134-clk-mux-Split-out-register-accessors-for-reuse.patch @@ -0,0 +1,184 @@ +From 4c28a15ea536281c8d619e5c6716ade914c79a6e Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Fri, 20 Mar 2015 23:45:21 -0700 +Subject: [PATCH 1/2] clk: mux: Split out register accessors for reuse + +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 +Signed-off-by: Ram Chandra Jangir +--- + drivers/clk/clk-mux.c | 74 +++++++++++++++++++++++++++----------------- + include/linux/clk-provider.h | 9 ++++-- + 2 files changed, 53 insertions(+), 30 deletions(-) + +diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c +index 7129c86..b03a34d 100644 +--- a/drivers/clk/clk-mux.c ++++ b/drivers/clk/clk-mux.c +@@ -28,35 +28,24 @@ + + #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) + +-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) +@@ -64,24 +53,53 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) + + 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; + +- else { +- if (mux->flags & CLK_MUX_INDEX_BIT) +- index = 1 << index; ++ 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 (mux->flags & CLK_MUX_INDEX_ONE) +- index++; ++ if (table) { ++ val = table[val]; ++ } else { ++ if (flags & CLK_MUX_INDEX_BIT) ++ val = 1 << 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 +@@ -105,7 +123,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) + } + + 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, + }; +@@ -120,7 +138,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, + 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 *clk; +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index c56988a..b6b17b5 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -432,7 +432,7 @@ void clk_unregister_divider(struct clk *clk); + struct clk_mux { + struct clk_hw hw; + void __iomem *reg; +- u32 *table; ++ unsigned int *table; + u32 mask; + u8 shift; + u8 flags; +@@ -448,6 +448,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, +@@ -458,7 +463,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, + 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); + +-- +2.7.2 + diff --git a/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch new file mode 100644 index 0000000000..5df0a5613e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/135-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch @@ -0,0 +1,127 @@ +From 39d42ce5031d2a4f92fa203b87acfbab340b15a2 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Fri, 20 Mar 2015 23:45:22 -0700 +Subject: [PATCH 2/2] clk: Avoid sending high rates to downstream clocks during + set_rate + +If a clock is on and we call clk_set_rate() on it we may get into +a situation where the clock temporarily increases in rate +dramatically while we walk the tree and call .set_rate() ops. For +example, consider a case where a PLL feeds into a divider. +Initially the divider is set to divide by 1 and the PLL is +running fairly slow (100MHz). The downstream consumer of the +divider output can only handle rates =< 400 MHz, but the divider +can only choose between divisors of 1 and 4. + + +-----+ +----------------+ + | PLL |-->| div 1 or div 4 |---> consumer device + +-----+ +----------------+ + +To achieve a rate of 400MHz on the output of the divider, we +would have to set the rate of the PLL to 1.6 GHz and then divide +it by 4. The current code would set the PLL to 1.6GHz first while +the divider is still set to 1, thus causing the downstream +consumer of the clock to receive a few clock cycles of 1.6GHz +clock (far beyond it's maximum acceptable rate). We should be +changing the divider first before increasing the PLL rate to +avoid this problem. + +Therefore, set the rate of any child clocks that are increasing +in rate from their current rate so that they can increase their +dividers if necessary. We assume that there isn't such a thing as +minimum rate requirements. + +Signed-off-by: Stephen Boyd +Signed-off-by: Ram Chandra Jangir +--- + drivers/clk/clk.c | 34 ++++++++++++++++++++++------------ + 1 file changed, 22 insertions(+), 12 deletions(-) + +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index f13c3f4..8404c3c 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -1427,21 +1427,24 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core, + * walk down a subtree and set the new rates notifying the rate + * change on the way + */ +-static void clk_change_rate(struct clk_core *core) ++static void ++clk_change_rate(struct clk_core *core, unsigned long best_parent_rate) + { + struct clk_core *child; + struct hlist_node *tmp; + unsigned long old_rate; +- unsigned long best_parent_rate = 0; + bool skip_set_rate = false; + struct clk_core *old_parent; + +- old_rate = core->rate; ++ hlist_for_each_entry(child, &core->children, child_node) { ++ /* Skip children who will be reparented to another clock */ ++ if (child->new_parent && child->new_parent != core) ++ continue; ++ if (child->new_rate > child->rate) ++ clk_change_rate(child, core->new_rate); ++ } + +- if (core->new_parent) +- best_parent_rate = core->new_parent->rate; +- else if (core->parent) +- best_parent_rate = core->parent->rate; ++ old_rate = core->rate; + + if (core->new_parent && core->new_parent != core->parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); +@@ -1467,7 +1470,7 @@ static void clk_change_rate(struct clk_core *core) + + trace_clk_set_rate_complete(core, core->new_rate); + +- core->rate = clk_recalc(core, best_parent_rate); ++ core->rate = core->new_rate; + + if (core->notifier_count && old_rate != core->rate) + __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); +@@ -1483,12 +1486,13 @@ static void clk_change_rate(struct clk_core *core) + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != core) + continue; +- clk_change_rate(child); ++ if (child->new_rate != child->rate) ++ clk_change_rate(child, core->new_rate); + } + + /* handle the new child who might not be in core->children yet */ +- if (core->new_child) +- clk_change_rate(core->new_child); ++ if (core->new_child && core->new_child->new_rate != core->new_child->rate) ++ clk_change_rate(core->new_child, core->new_rate); + } + + static int clk_core_set_rate_nolock(struct clk_core *core, +@@ -1497,6 +1501,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core, + struct clk_core *top, *fail_clk; + unsigned long rate = req_rate; + int ret = 0; ++ unsigned long parent_rate; + + if (!core) + return 0; +@@ -1522,8 +1527,13 @@ static int clk_core_set_rate_nolock(struct clk_core *core, + return -EBUSY; + } + ++ if (top->parent) ++ parent_rate = top->parent->rate; ++ else ++ parent_rate = 0; ++ + /* change the rates */ +- clk_change_rate(top); ++ clk_change_rate(top, parent_rate); + + core->req_rate = req_rate; + +-- +2.7.2 + diff --git a/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch new file mode 100644 index 0000000000..e969f6bca4 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/136-clk-Add-safe-switch-hook.patch @@ -0,0 +1,158 @@ +From f7a00ea959be31f9b742042294a359d508edce94 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Fri, 20 Mar 2015 23:45:23 -0700 +Subject: [PATCH] clk: Add safe switch hook + +Sometimes clocks can't accept their parent source turning off +while the source is reprogrammed to a different rate. Most +notably CPU clocks require a way to switch away from the current +PLL they're running on, reprogram that PLL to a new rate, and +then switch back to the PLL with the new rate once they're done. +Add a hook that drivers can implement allowing them to return a +'safe parent' that they can switch their parent to while the +upstream source is reprogrammed to support this. + +Signed-off-by: Stephen Boyd +Signed-off-by: Ram Chandra Jangir +--- + drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------ + include/linux/clk-provider.h | 1 + + 2 files changed, 54 insertions(+), 8 deletions(-) + +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index 8404c3c..a29319a 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -51,9 +51,12 @@ struct clk_core { + struct clk_core **parents; + u8 num_parents; + u8 new_parent_index; ++ u8 safe_parent_index; + unsigned long rate; + unsigned long req_rate; ++ unsigned long old_rate; + unsigned long new_rate; ++ struct clk_core *safe_parent; + struct clk_core *new_parent; + struct clk_core *new_child; + unsigned long flags; +@@ -1271,7 +1274,8 @@ out: + static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, + struct clk_core *new_parent, u8 p_index) + { +- struct clk_core *child; ++ struct clk_core *child, *parent; ++ struct clk_hw *parent_hw; + + core->new_rate = new_rate; + core->new_parent = new_parent; +@@ -1281,6 +1285,18 @@ static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, + if (new_parent && new_parent != core->parent) + new_parent->new_child = core; + ++ if (core->ops->get_safe_parent) { ++ parent_hw = core->ops->get_safe_parent(core->hw); ++ if (parent_hw) { ++ parent = parent_hw->core; ++ p_index = clk_fetch_parent_index(core, parent); ++ core->safe_parent_index = p_index; ++ core->safe_parent = parent; ++ } ++ } else { ++ core->safe_parent = NULL; ++ } ++ + hlist_for_each_entry(child, &core->children, child_node) { + child->new_rate = clk_recalc(child, new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); +@@ -1393,14 +1409,43 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core, + unsigned long event) + { + struct clk_core *child, *tmp_clk, *fail_clk = NULL; ++ struct clk_core *old_parent; + int ret = NOTIFY_DONE; + +- if (core->rate == core->new_rate) ++ if (core->rate == core->new_rate && event != POST_RATE_CHANGE) + return NULL; + ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ if (core->safe_parent) ++ core->ops->set_parent(core->hw, core->safe_parent_index); ++ core->old_rate = core->rate; ++ break; ++ case POST_RATE_CHANGE: ++ if (core->safe_parent) { ++ old_parent = __clk_set_parent_before(core, ++ core->new_parent); ++ if (core->ops->set_rate_and_parent) { ++ core->ops->set_rate_and_parent(core->hw, ++ core->new_rate, ++ core->new_parent ? ++ core->new_parent->rate : 0, ++ core->new_parent_index); ++ } else if (core->ops->set_parent) { ++ core->ops->set_parent(core->hw, ++ core->new_parent_index); ++ } ++ __clk_set_parent_after(core, core->new_parent, ++ old_parent); ++ } ++ break; ++ } ++ + if (core->notifier_count) { +- ret = __clk_notify(core, event, core->rate, core->new_rate); +- if (ret & NOTIFY_STOP_MASK) ++ if (event != POST_RATE_CHANGE || core->old_rate != core->rate) ++ ret = __clk_notify(core, event, core->old_rate, ++ core->new_rate); ++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) + fail_clk = core; + } + +@@ -1446,7 +1491,8 @@ clk_change_rate(struct clk_core *core, unsigned long best_parent_rate) + + old_rate = core->rate; + +- if (core->new_parent && core->new_parent != core->parent) { ++ if (core->new_parent && core->new_parent != core->parent && ++ !core->safe_parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); + trace_clk_set_parent(core, core->new_parent); + +@@ -1472,9 +1518,6 @@ clk_change_rate(struct clk_core *core, unsigned long best_parent_rate) + + core->rate = core->new_rate; + +- if (core->notifier_count && old_rate != core->rate) +- __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); +- + if (core->flags & CLK_RECALC_NEW_RATES) + (void)clk_calc_new_rates(core, core->new_rate); + +@@ -1537,6 +1580,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core, + + core->req_rate = req_rate; + ++ clk_propagate_rate_change(top, POST_RATE_CHANGE); ++ + return ret; + } + +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index b6b17b5..5d49262 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -202,6 +202,7 @@ struct clk_ops { + struct clk_rate_request *req); + int (*set_parent)(struct clk_hw *hw, u8 index); + u8 (*get_parent)(struct clk_hw *hw); ++ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw); + int (*set_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); + int (*set_rate_and_parent)(struct clk_hw *hw, +-- +2.7.2 + diff --git a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch new file mode 100644 index 0000000000..a1b1f4b1f7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch @@ -0,0 +1,351 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) +From: Stephen Boyd +X-Patchwork-Id: 6063261 +Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar +Date: Fri, 20 Mar 2015 23:45:24 -0700 + +HFPLLs are the main frequency source for Krait CPU clocks. Add +support for changing the rate of these PLLs. + +Signed-off-by: Stephen Boyd + +--- +I'd really like to get rid of __clk_hfpll_init_once() if possible... + + 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 +@@ -8,6 +8,7 @@ + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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->clk); ++ ++ /* 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. ++ */ ++ udelay(10); ++ ++ /* 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 { ++ udelay(60); ++ } ++ ++ /* 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 ++#include ++#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 diff --git a/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch new file mode 100644 index 0000000000..5a452dbcbc --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch @@ -0,0 +1,206 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,06/13] clk: qcom: Add HFPLL driver +From: Stephen Boyd +X-Patchwork-Id: 6063231 +Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:25 -0700 + +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: +Signed-off-by: Stephen Boyd + +--- +.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++ + drivers/clk/qcom/Kconfig | 8 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++ + 4 files changed, 158 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: ++ Definition: must be "qcom,hfpll" ++ ++- reg: ++ Usage: required ++ Value type: ++ 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: ++ 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 +@@ -106,3 +106,11 @@ + Support for the multimedia clock controller on msm8974 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 +@@ -23,3 +23,4 @@ + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o ++obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +--- /dev/null ++++ b/drivers/clk/qcom/hfpll.c +@@ -0,0 +1,109 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 clk *clk; ++ 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); ++ ++ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++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"); diff --git a/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch new file mode 100644 index 0000000000..5b191b5597 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch @@ -0,0 +1,127 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs +From: Stephen Boyd +X-Patchwork-Id: 6063241 +Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar +Date: Fri, 20 Mar 2015 23:45:27 -0700 + +Describe the HFPLLs present on IPQ806X devices. + +Signed-off-by: Stephen Boyd + +--- +drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 83 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,85 @@ + }, + }; + ++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, +@@ -2837,6 +2917,9 @@ + [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[] = { diff --git a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch new file mode 100644 index 0000000000..522482d841 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch @@ -0,0 +1,272 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,09/13] clk: qcom: Add support for Krait clocks +From: Stephen Boyd +X-Patchwork-Id: 6063251 +Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar +Date: Fri, 20 Mar 2015 23:45:28 -0700 + +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 + +--- +drivers/clk/qcom/Kconfig | 4 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-krait.h | 49 +++++++++++++ + 4 files changed, 220 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 +@@ -114,3 +114,7 @@ + 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 +@@ -8,6 +8,7 @@ + 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,166 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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); ++} ++ ++static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw) ++{ ++ int i; ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ int num_parents = clk_hw_get_num_parents(hw->clk); ++ ++ i = mux->safe_sel; ++ for (i = 0; i < num_parents; i++) ++ if (mux->safe_sel == mux->parent_map[i]) ++ break; ++ ++ return __clk_get_hw(clk_hw_get_parent_by_index(hw->clk, i)); ++} ++ ++static int krait_mux_enable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->en_mask); ++ ++ return 0; ++} ++ ++static void krait_mux_disable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->safe_sel); ++} ++ ++const struct clk_ops krait_mux_clk_ops = { ++ .enable = krait_mux_enable, ++ .disable = krait_mux_disable, ++ .set_parent = krait_mux_set_parent, ++ .get_parent = krait_mux_get_parent, ++ .determine_rate = __clk_mux_determine_rate_closest, ++ .get_safe_parent = krait_mux_get_safe_parent, ++}; ++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->clk), 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,49 @@ ++/* ++ * 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 ++ ++struct krait_mux_clk { ++ unsigned int *parent_map; ++ bool has_safe_parent; ++ u8 safe_sel; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ bool lpl; ++ ++ struct clk_hw hw; ++}; ++ ++#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 diff --git a/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch new file mode 100644 index 0000000000..b2ddbc8a68 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -0,0 +1,207 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver +From: Stephen Boyd +X-Patchwork-Id: 6063201 +Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:29 -0700 + +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: +Signed-off-by: Stephen Boyd + +--- +.../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 | 95 ++++++++++++++++++++++ + 5 files changed, 139 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 @@ + 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: ++ 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: ++ Definition: should be one of: ++ "qcom,kpss-gcc" ++ ++- reg: ++ Usage: required ++ Value type: ++ Definition: base address and size of the register region ++ ++- clock-output-names: ++ Usage: required ++ Value type: ++ 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 +@@ -115,6 +115,14 @@ + 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 +@@ -9,6 +9,7 @@ + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o + clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o ++obj-$(CONFIG_KPSS_XCC) += kpss-xcc.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/kpss-xcc.c +@@ -0,0 +1,95 @@ ++/* 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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"); diff --git a/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch new file mode 100644 index 0000000000..f0e946781b --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -0,0 +1,435 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver +From: Stephen Boyd +X-Patchwork-Id: 6063121 +Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:30 -0700 + +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: +Signed-off-by: Stephen Boyd + +--- +.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++ + 4 files changed, 383 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: ++ Definition: must be one of: ++ "qcom,krait-cc-v1" ++ "qcom,krait-cc-v2" ++ ++- #clock-cells: ++ Usage: required ++ Value type: ++ 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 +@@ -123,6 +123,14 @@ + 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 +@@ -26,3 +26,4 @@ + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.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,352 @@ ++/* 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 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 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->has_safe_parent = true; ++ mux->safe_sel = 2; ++ 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 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->has_safe_parent = true; ++ mux->safe_sel = 0; ++ 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 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, CLK_IS_ROOT, 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"); diff --git a/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch new file mode 100644 index 0000000000..d4c43f412a --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch @@ -0,0 +1,304 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs +From: Stephen Boyd +X-Patchwork-Id: 6063191 +Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette , Stephen Boyd +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar , +Date: Fri, 20 Mar 2015 23:45:31 -0700 + +Register a cpufreq-generic device whenever we detect that a +"qcom,krait" compatible CPU is present in DT. + +Cc: +Signed-off-by: Stephen Boyd + +--- +.../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: ++ 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: ++ 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 +@@ -95,6 +95,15 @@ + 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 +@@ -61,6 +61,7 @@ + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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"); diff --git a/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch new file mode 100644 index 0000000000..aaf140126c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch @@ -0,0 +1,100 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -26,6 +26,11 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc0>; + qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>; ++ clock-names = "cpu"; ++ clock-latency = <100000>; ++ core-supply = <&smb208_s2a>; ++ voltage-tolerance = <5>; + }; + + cpu@1 { +@@ -36,11 +41,24 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc1>; + qcom,saw = <&saw1>; ++ clocks = <&kraitcc 1>; ++ clock-names = "cpu"; ++ clock-latency = <100000>; ++ core-supply = <&smb208_s2b>; + }; + + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; ++ clocks = <&kraitcc 4>; ++ clock-names = "cache"; ++ cache-points-kHz = < ++ /* kHz uV CPU kHz */ ++ 1200000 1150000 1200000 ++ 1000000 1100000 600000 ++ 384000 1100000 384000 ++ >; ++ vdd_dig-supply = <&smb208_s1a>; + }; + }; + +@@ -73,6 +91,46 @@ + }; + }; + ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; ++ ++ qcom,pvs { ++ qcom,pvs-format-a; ++ qcom,speed0-pvs0-bin-v0 = ++ < 1400000000 1250000 >, ++ < 1200000000 1200000 >, ++ < 1000000000 1150000 >, ++ < 800000000 1100000 >, ++ < 600000000 1050000 >, ++ < 384000000 1000000 >; ++ ++ qcom,speed0-pvs1-bin-v0 = ++ < 1400000000 1175000 >, ++ < 1200000000 1125000 >, ++ < 1000000000 1075000 >, ++ < 800000000 1025000 >, ++ < 600000000 975000 >, ++ < 384000000 925000 >; ++ ++ qcom,speed0-pvs2-bin-v0 = ++ < 1400000000 1125000 >, ++ < 1200000000 1075000 >, ++ < 1000000000 1025000 >, ++ < 800000000 995000 >, ++ < 600000000 925000 >, ++ < 384000000 875000 >; ++ ++ qcom,speed0-pvs3-bin-v0 = ++ < 1400000000 1050000 >, ++ < 1200000000 1000000 >, ++ < 1000000000 950000 >, ++ < 800000000 900000 >, ++ < 600000000 850000 >, ++ < 384000000 800000 >; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -215,11 +273,13 @@ + acc0: clock-controller@2088000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu0_aux"; + }; + + acc1: clock-controller@2098000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu1_aux"; + }; + + l2cc: clock-controller@2011000 { diff --git a/target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch new file mode 100644 index 0000000000..f33c9e0d8c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch @@ -0,0 +1,461 @@ +From dd77db4143290689d3a5e1ec61627233d0711b66 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Fri, 30 May 2014 16:36:11 -0700 +Subject: [PATCH] FROMLIST: cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 + +Krait processors have individual clocks for each CPU that can +scale independently from one another. cpufreq-cpu0 is fairly +close to this, but assumes that there is only one clock for all +CPUs. Add a driver to support the Krait configuration. + +TODO: Merge into cpufreq-cpu0? Or make generic? + +Signed-off-by: Stephen Boyd + +--- + drivers/cpufreq/Kconfig | 13 +++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 204 insertions(+) + create mode 100644 drivers/cpufreq/cpufreq-krait.c + +--- a/drivers/cpufreq/Kconfig ++++ b/drivers/cpufreq/Kconfig +@@ -198,6 +198,19 @@ + + If in doubt, say N. + ++config GENERIC_CPUFREQ_KRAIT ++ tristate "Krait cpufreq driver" ++ depends on HAVE_CLK && OF ++ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: ++ depends on !CPU_THERMAL || THERMAL ++ select PM_OPP ++ help ++ This adds a generic cpufreq driver for CPU0 frequency management. ++ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) ++ systems which share clock and voltage across all CPUs. ++ ++ If in doubt, say N. ++ + if X86 + source "drivers/cpufreq/Kconfig.x86" + endif +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -13,6 +13,7 @@ + obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o + + obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o ++obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o + + ################################################################################## + # x86 drivers. +--- /dev/null ++++ b/drivers/cpufreq/cpufreq-krait.c +@@ -0,0 +1,390 @@ ++/* ++ * Copyright (C) 2012 Freescale Semiconductor, Inc. ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * The OPP code in function krait_set_target() is reused from ++ * drivers/cpufreq/omap-cpufreq.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned int transition_latency; ++static unsigned int voltage_tolerance; /* in percentage */ ++ ++static struct device *cpu_dev; ++static DEFINE_PER_CPU(struct clk *, krait_cpu_clks); ++static DEFINE_PER_CPU(struct regulator *, krait_supply_core); ++static struct cpufreq_frequency_table *freq_table; ++static struct thermal_cooling_device *cdev; ++ ++struct cache_points { ++ unsigned long cache_freq; ++ unsigned int cache_volt; ++ unsigned long cpu_freq; ++}; ++ ++static struct regulator *krait_l2_reg; ++static struct clk *krait_l2_clk; ++static struct cache_points *krait_l2_points; ++static int nr_krait_l2_points; ++ ++static int krait_parse_cache_points(struct device *dev, ++ struct device_node *of_node) ++{ ++ const struct property *prop; ++ const __be32 *val; ++ int nr, i; ++ ++ prop = of_find_property(of_node, "cache-points-kHz", NULL); ++ if (!prop) ++ return -ENODEV; ++ if (!prop->value) ++ return -ENODATA; ++ ++ /* ++ * Each OPP is a set of tuples consisting of frequency and ++ * cpu-frequency like . ++ */ ++ nr = prop->length / sizeof(u32); ++ if (nr % 3) { ++ dev_err(dev, "%s: Invalid cache points\n", __func__); ++ return -EINVAL; ++ } ++ nr /= 3; ++ ++ krait_l2_points = devm_kcalloc(dev, nr, sizeof(*krait_l2_points), ++ GFP_KERNEL); ++ if (!krait_l2_points) ++ return -ENOMEM; ++ nr_krait_l2_points = nr; ++ ++ for (i = 0, val = prop->value; i < nr; i++) { ++ unsigned long cache_freq = be32_to_cpup(val++) * 1000; ++ unsigned int cache_volt = be32_to_cpup(val++); ++ unsigned long cpu_freq = be32_to_cpup(val++) * 1000; ++ ++ krait_l2_points[i].cache_freq = cache_freq; ++ krait_l2_points[i].cache_volt = cache_volt; ++ krait_l2_points[i].cpu_freq = cpu_freq; ++ } ++ ++ return 0; ++} ++ ++static int krait_set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ struct dev_pm_opp *opp; ++ unsigned long volt = 0, volt_old = 0, tol = 0; ++ unsigned long freq, max_cpu_freq = 0; ++ unsigned int old_freq, new_freq; ++ long freq_Hz, freq_exact; ++ int ret, i; ++ struct clk *cpu_clk; ++ struct regulator *core; ++ unsigned int cpu; ++ ++ cpu_clk = per_cpu(krait_cpu_clks, policy->cpu); ++ ++ freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); ++ if (freq_Hz <= 0) ++ freq_Hz = freq_table[index].frequency * 1000; ++ ++ freq_exact = freq_Hz; ++ new_freq = freq_Hz / 1000; ++ old_freq = clk_get_rate(cpu_clk) / 1000; ++ ++ core = per_cpu(krait_supply_core, policy->cpu); ++ ++ rcu_read_lock(); ++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); ++ if (IS_ERR(opp)) { ++ rcu_read_unlock(); ++ pr_err("failed to find OPP for %ld\n", freq_Hz); ++ return PTR_ERR(opp); ++ } ++ volt = dev_pm_opp_get_voltage(opp); ++ rcu_read_unlock(); ++ tol = volt * voltage_tolerance / 100; ++ volt_old = regulator_get_voltage(core); ++ ++ pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", ++ old_freq / 1000, volt_old ? volt_old / 1000 : -1, ++ new_freq / 1000, volt ? volt / 1000 : -1); ++ ++ /* scaling up? scale voltage before frequency */ ++ if (new_freq > old_freq) { ++ ret = regulator_set_voltage_tol(core, volt, tol); ++ if (ret) { ++ pr_err("failed to scale voltage up: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ ret = clk_set_rate(cpu_clk, freq_exact); ++ if (ret) { ++ pr_err("failed to set clock rate: %d\n", ret); ++ return ret; ++ } ++ ++ /* scaling down? scale voltage after frequency */ ++ if (new_freq < old_freq) { ++ ret = regulator_set_voltage_tol(core, volt, tol); ++ if (ret) { ++ pr_err("failed to scale voltage down: %d\n", ret); ++ clk_set_rate(cpu_clk, old_freq * 1000); ++ } ++ } ++ ++ for_each_possible_cpu(cpu) { ++ freq = clk_get_rate(per_cpu(krait_cpu_clks, cpu)); ++ max_cpu_freq = max(max_cpu_freq, freq); ++ } ++ ++ for (i = 0; i < nr_krait_l2_points; i++) { ++ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { ++ if (krait_l2_reg) { ++ ret = regulator_set_voltage_tol(krait_l2_reg, ++ krait_l2_points[i].cache_volt, ++ tol); ++ if (ret) { ++ pr_err("failed to scale l2 voltage: %d\n", ++ ret); ++ } ++ } ++ ret = clk_set_rate(krait_l2_clk, ++ krait_l2_points[i].cache_freq); ++ if (ret) ++ pr_err("failed to scale l2 clk: %d\n", ret); ++ break; ++ } ++ ++ } ++ ++ return ret; ++} ++ ++static int krait_cpufreq_init(struct cpufreq_policy *policy) ++{ ++ int ret; ++ ++ policy->clk = per_cpu(krait_cpu_clks, policy->cpu); ++ ++ ret = cpufreq_table_validate_and_show(policy, freq_table); ++ if (ret) { ++ pr_err("%s: invalid frequency table: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ policy->cpuinfo.transition_latency = transition_latency; ++ ++ return 0; ++} ++ ++static struct cpufreq_driver krait_cpufreq_driver = { ++ .flags = CPUFREQ_STICKY, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = krait_set_target, ++ .get = cpufreq_generic_get, ++ .init = krait_cpufreq_init, ++ .name = "generic_krait", ++ .attr = cpufreq_generic_attr, ++}; ++ ++static int krait_cpufreq_probe(struct platform_device *pdev) ++{ ++ struct device_node *np, *cache; ++ int ret, i; ++ unsigned int cpu; ++ struct device *dev; ++ struct clk *clk; ++ struct regulator *core; ++ unsigned long freq_Hz, freq, max_cpu_freq = 0; ++ struct dev_pm_opp *opp; ++ unsigned long volt, tol; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get krait device\n"); ++ return -ENODEV; ++ } ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) { ++ pr_err("failed to find krait node\n"); ++ return -ENOENT; ++ } ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ++ if (ret) { ++ pr_err("failed to init cpufreq table: %d\n", ret); ++ goto out_put_node; ++ } ++ ++ of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance); ++ ++ if (of_property_read_u32(np, "clock-latency", &transition_latency)) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ cache = of_find_next_cache_node(np); ++ if (cache) { ++ struct device_node *vdd; ++ ++ vdd = of_parse_phandle(cache, "vdd_dig-supply", 0); ++ if (vdd) { ++ krait_l2_reg = regulator_get(NULL, vdd->name); ++ if (IS_ERR(krait_l2_reg)) { ++ pr_warn("failed to get l2 vdd_dig supply\n"); ++ krait_l2_reg = NULL; ++ } ++ of_node_put(vdd); ++ } ++ ++ krait_l2_clk = of_clk_get(cache, 0); ++ if (!IS_ERR(krait_l2_clk)) { ++ ret = krait_parse_cache_points(&pdev->dev, cache); ++ if (ret) ++ clk_put(krait_l2_clk); ++ } ++ if (IS_ERR(krait_l2_clk) || ret) ++ krait_l2_clk = NULL; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ dev = get_cpu_device(cpu); ++ if (!dev) { ++ pr_err("failed to get krait device\n"); ++ ret = -ENOENT; ++ goto out_free_table; ++ } ++ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); ++ goto out_free_table; ++ } ++ core = devm_regulator_get(dev, "core"); ++ if (IS_ERR(core)) { ++ pr_debug("failed to get core regulator\n"); ++ ret = PTR_ERR(core); ++ goto out_free_table; ++ } ++ per_cpu(krait_supply_core, cpu) = core; ++ ++ freq = freq_Hz = clk_get_rate(clk); ++ ++ rcu_read_lock(); ++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); ++ if (IS_ERR(opp)) { ++ rcu_read_unlock(); ++ pr_err("failed to find OPP for %ld\n", freq_Hz); ++ ret = PTR_ERR(opp); ++ goto out_free_table; ++ } ++ volt = dev_pm_opp_get_voltage(opp); ++ rcu_read_unlock(); ++ ++ tol = volt * voltage_tolerance / 100; ++ ret = regulator_set_voltage_tol(core, volt, tol); ++ if (ret) { ++ pr_err("failed to scale voltage up: %d\n", ret); ++ goto out_free_table; ++ } ++ ret = regulator_enable(core); ++ if (ret) { ++ pr_err("failed to enable regulator: %d\n", ret); ++ goto out_free_table; ++ } ++ max_cpu_freq = max(max_cpu_freq, freq); ++ } ++ ++ for (i = 0; i < nr_krait_l2_points; i++) { ++ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { ++ if (krait_l2_reg) { ++ ret = regulator_set_voltage_tol(krait_l2_reg, ++ krait_l2_points[i].cache_volt, ++ tol); ++ if (ret) ++ pr_err("failed to scale l2 voltage: %d\n", ++ ret); ++ ret = regulator_enable(krait_l2_reg); ++ if (ret) ++ pr_err("failed to enable l2 voltage: %d\n", ++ ret); ++ } ++ break; ++ } ++ ++ } ++ ++ ret = cpufreq_register_driver(&krait_cpufreq_driver); ++ if (ret) { ++ pr_err("failed register driver: %d\n", ret); ++ goto out_free_table; ++ } ++ of_node_put(np); ++ ++ /* ++ * For now, just loading the cooling device; ++ * thermal DT code takes care of matching them. ++ */ ++ for_each_possible_cpu(cpu) { ++ dev = get_cpu_device(cpu); ++ np = of_node_get(dev->of_node); ++ if (of_find_property(np, "#cooling-cells", NULL)) { ++ cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu)); ++ if (IS_ERR(cdev)) ++ pr_err("running cpufreq without cooling device: %ld\n", ++ PTR_ERR(cdev)); ++ } ++ of_node_put(np); ++ } ++ ++ return 0; ++ ++out_free_table: ++ regulator_put(krait_l2_reg); ++ clk_put(krait_l2_clk); ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++out_put_node: ++ of_node_put(np); ++ return ret; ++} ++ ++static int krait_cpufreq_remove(struct platform_device *pdev) ++{ ++ cpufreq_cooling_unregister(cdev); ++ cpufreq_unregister_driver(&krait_cpufreq_driver); ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++ clk_put(krait_l2_clk); ++ regulator_put(krait_l2_reg); ++ ++ return 0; ++} ++ ++static struct platform_driver krait_cpufreq_platdrv = { ++ .driver = { ++ .name = "cpufreq-krait", ++ .owner = THIS_MODULE, ++ }, ++ .probe = krait_cpufreq_probe, ++ .remove = krait_cpufreq_remove, ++}; ++module_platform_driver(krait_cpufreq_platdrv); ++ ++MODULE_DESCRIPTION("Krait CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/cpufreq/qcom-cpufreq.c ++++ b/drivers/cpufreq/qcom-cpufreq.c +@@ -168,11 +168,8 @@ + + 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), ++ .name = "cpufreq-krait", + }; + struct device *cpu_dev; + struct device_node *np; diff --git a/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch new file mode 100644 index 0000000000..4f5c0efb7b --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/155-dt-bindings-qcom_adm-Fix-channel-specifiers.patch @@ -0,0 +1,76 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v6,1/2] dt/bindings: qcom_adm: Fix channel specifiers +From: Andy Gross +X-Patchwork-Id: 6027361 +Message-Id: <1426571172-9711-2-git-send-email-agross@codeaurora.org> +To: Vinod Koul +Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, Kumar Gala , + Bjorn Andersson , + Andy Gross +Date: Tue, 17 Mar 2015 00:46:11 -0500 + +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 + +--- +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: + +@@ -56,7 +52,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"; + }; diff --git a/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch new file mode 100644 index 0000000000..805f28f323 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/156-dmaengine-Add-ADM-driver.patch @@ -0,0 +1,964 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v6,2/2] dmaengine: Add ADM driver +From: Andy Gross +X-Patchwork-Id: 6027351 +Message-Id: <1426571172-9711-3-git-send-email-agross@codeaurora.org> +To: Vinod Koul +Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org, + linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, Kumar Gala , + Bjorn Andersson , + Andy Gross +Date: Tue, 17 Mar 2015 00:46:12 -0500 + +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 +Reviewed-by: sricharan + +--- +drivers/dma/Kconfig | 10 + + drivers/dma/Makefile | 1 + + drivers/dma/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 911 insertions(+) + create mode 100644 drivers/dma/qcom_adm.c + +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -558,4 +558,14 @@ + config DMA_ENGINE_RAID + bool + ++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. ++ + endif +--- /dev/null ++++ b/drivers/dma/qcom_adm.c +@@ -0,0 +1,900 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; ++ 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_NOWAIT); ++ 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 = dma_alloc_writecombine(adev->dev, async_desc->dma_len, ++ &async_desc->dma_addr, GFP_NOWAIT); ++ ++ if (!async_desc->cpl) { ++ kfree(async_desc); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ 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); ++ ++ /* init cmd list */ ++ *cple = ADM_CPLE_LP; ++ *cple |= (desc - async_desc->cpl + async_desc->dma_addr) >> 3; ++ ++ 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); ++ } ++ ++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); ++} ++ ++/** ++ * 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_free_writecombine(async_desc->adev->dev, async_desc->dma_len, ++ async_desc->cpl, async_desc->dma_addr); ++ 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 "); ++MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -65,5 +65,6 @@ + obj-$(CONFIG_TI_EDMA) += edma.o + obj-$(CONFIG_XGENE_DMA) += xgene-dma.o + obj-$(CONFIG_ZX_DMA) += zx296702_dma.o ++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o + + obj-y += xilinx/ diff --git a/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch new file mode 100644 index 0000000000..b8abd0a4b2 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/157-ARM-DT-ipq8064-Add-ADM-device-node.patch @@ -0,0 +1,42 @@ +From 1fb18acab2d71e7e4efd9c10492edb1baf84dcc0 Mon Sep 17 00:00:00 2001 +From: Andy Gross +Date: Wed, 20 May 2015 15:41:07 +0530 +Subject: [PATCH] ARM: DT: ipq8064: Add ADM device node + +This patch adds support for the ADM DMA on the IPQ8064 SOC + +Signed-off-by: Andy Gross +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 4 ++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++ + 2 files changed, 25 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -727,6 +727,26 @@ + + status = "disabled"; + }; ++ ++ adm_dma: dma@18300000 { ++ compatible = "qcom,adm"; ++ reg = <0x18300000 0x100000>; ++ interrupts = <0 170 0>; ++ #dma-cells = <1>; ++ ++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; ++ clock-names = "core", "iface"; ++ ++ resets = <&gcc ADM0_RESET>, ++ <&gcc ADM0_PBUS_RESET>, ++ <&gcc ADM0_C0_RESET>, ++ <&gcc ADM0_C1_RESET>, ++ <&gcc ADM0_C2_RESET>; ++ reset-names = "clk", "pbus", "c0", "c1", "c2"; ++ qcom,ee = <0>; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch new file mode 100644 index 0000000000..21fe405b90 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch @@ -0,0 +1,83 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3, + 1/5] mtd: nand: Create a BBT flag to access bad block markers in raw + mode +From: Archit Taneja +X-Patchwork-Id: 6927081 +Message-Id: <1438578498-32254-2-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja +Date: Mon, 3 Aug 2015 10:38:14 +0530 + +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 +Signed-off-by: Archit Taneja + +--- +drivers/mtd/nand/nand_base.c | 6 +++++- + drivers/mtd/nand/nand_bbt.c | 6 +++++- + include/linux/mtd/bbm.h | 7 +++++++ + 3 files changed, 17 insertions(+), 2 deletions(-) + +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -394,7 +394,11 @@ + } 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 @@ + 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 @@ + #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. diff --git a/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch new file mode 100644 index 0000000000..19e5f91812 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/162-mtd-nand-Qualcomm-NAND-controller-driver.patch @@ -0,0 +1,2024 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver +From: Archit Taneja +X-Patchwork-Id: 6927101 +Message-Id: <1438578498-32254-3-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja +Date: Mon, 3 Aug 2015 10:38:15 +0530 + +The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx, +MDM9x15 series. + +It exists as a sub block inside the IPs EBI2 (External Bus Interface 2) +and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a +broader interface for external slow peripheral devices such as LCD and +NAND/NOR flash memory or SRAM like interfaces. + +We add support for the NAND controller found within EBI2. For the SoCs +of our interest, we only use the NAND controller within EBI2. Therefore, +it's safe for us to assume that the NAND controller is a standalone block +within the SoC. + +The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND +flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and +16 bit correction/step) and RS ECC(4 bit correction/step) that covers main +and spare data. The controller contains an internal 512 byte page buffer +to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA +for register read/write and data transfers. The controller performs page +reads and writes at a codeword/step level of 512 bytes. It can support up +to 2 external chips of different configurations. + +The driver prepares register read and write configuration descriptors for +each codeword, followed by data descriptors to read or write data from the +controller's internal buffer. It uses a single ADM DMA channel that we get +via dmaengine API. The controller requires 2 ADM CRCIs for command and +data flow control. These are passed via DT. + +The ecc layout used by the controller is syndrome like, but we can't use +the standard syndrome ecc ops because of several reasons. First, the amount +of data bytes covered by ecc isn't same in each step. Second, writing to +free oob space requires us writing to the entire step in which the oob +lies. This forces us to create our own ecc ops. + +One more difference is how the controller accesses the bad block marker. +The controller ignores reading the marker when ECC is enabled. ECC needs +to be explicity disabled to read or write to the bad block marker. For +this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to +read the factory provided bad block markers. + +v3: +- Refactor dma functions for maximum reuse +- Use dma_slave_confing on stack +- optimize and clean upempty_page_fixup using memchr_inv +- ensure portability with dma register reads using le32_* funcs +- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves +- fix handling of return values of dmaengine funcs +- constify wherever possible +- Remove dependency on ADM DMA in Kconfig +- Misc fixes and clean ups + +v2: +- Use new BBT flag that allows us to read BBM in raw mode +- reduce memcpy-s in the driver +- some refactor and clean ups because of above changes + +Reviewed-by: Andy Gross +Signed-off-by: Archit Taneja + +--- +drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1921 insertions(+) + create mode 100644 drivers/mtd/nand/qcom_nandc.c + +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -546,4 +546,11 @@ + help + Enables support for NAND controller on Hisilicon SoC Hip04. + ++config MTD_NAND_QCOM ++ tristate "Support for NAND on QCOM SoCs" ++ depends on ARCH_QCOM ++ help ++ Enables support for NAND flash chips on SoCs containing the EBI2 NAND ++ controller. This controller is found on IPQ806x SoC. ++ + endif # MTD_NAND +--- /dev/null ++++ b/drivers/mtd/nand/qcom_nandc.c +@@ -0,0 +1,1918 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* NANDc reg offsets */ ++#define NAND_FLASH_CMD 0x00 ++#define NAND_ADDR0 0x04 ++#define NAND_ADDR1 0x08 ++#define NAND_FLASH_CHIP_SELECT 0x0c ++#define NAND_EXEC_CMD 0x10 ++#define NAND_FLASH_STATUS 0x14 ++#define NAND_BUFFER_STATUS 0x18 ++#define NAND_DEV0_CFG0 0x20 ++#define NAND_DEV0_CFG1 0x24 ++#define NAND_DEV0_ECC_CFG 0x28 ++#define NAND_DEV1_ECC_CFG 0x2c ++#define NAND_DEV1_CFG0 0x30 ++#define NAND_DEV1_CFG1 0x34 ++#define NAND_READ_ID 0x40 ++#define NAND_READ_STATUS 0x44 ++#define NAND_DEV_CMD0 0xa0 ++#define NAND_DEV_CMD1 0xa4 ++#define NAND_DEV_CMD2 0xa8 ++#define NAND_DEV_CMD_VLD 0xac ++#define SFLASHC_BURST_CFG 0xe0 ++#define NAND_ERASED_CW_DETECT_CFG 0xe8 ++#define NAND_ERASED_CW_DETECT_STATUS 0xec ++#define NAND_EBI2_ECC_BUF_CFG 0xf0 ++#define FLASH_BUF_ACC 0x100 ++ ++#define NAND_CTRL 0xf00 ++#define NAND_VERSION 0xf08 ++#define NAND_READ_LOCATION_0 0xf20 ++#define NAND_READ_LOCATION_1 0xf24 ++ ++/* dummy register offsets, used by write_reg_dma */ ++#define NAND_DEV_CMD1_RESTORE 0xdead ++#define NAND_DEV_CMD_VLD_RESTORE 0xbeef ++ ++/* NAND_FLASH_CMD bits */ ++#define PAGE_ACC BIT(4) ++#define LAST_PAGE BIT(5) ++ ++/* NAND_FLASH_CHIP_SELECT bits */ ++#define NAND_DEV_SEL 0 ++#define DM_EN BIT(2) ++ ++/* NAND_FLASH_STATUS bits */ ++#define FS_OP_ERR BIT(4) ++#define FS_READY_BSY_N BIT(5) ++#define FS_MPU_ERR BIT(8) ++#define FS_DEVICE_STS_ERR BIT(16) ++#define FS_DEVICE_WP BIT(23) ++ ++/* NAND_BUFFER_STATUS bits */ ++#define BS_UNCORRECTABLE_BIT BIT(8) ++#define BS_CORRECTABLE_ERR_MSK 0x1f ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DISABLE_STATUS_AFTER_WRITE 4 ++#define CW_PER_PAGE 6 ++#define UD_SIZE_BYTES 9 ++#define ECC_PARITY_SIZE_BYTES_RS 19 ++#define SPARE_SIZE_BYTES 23 ++#define NUM_ADDR_CYCLES 27 ++#define STATUS_BFR_READ 30 ++#define SET_RD_MODE_AFTER_STATUS 31 ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DEV0_CFG1_ECC_DISABLE 0 ++#define WIDE_FLASH 1 ++#define NAND_RECOVERY_CYCLES 2 ++#define CS_ACTIVE_BSY 5 ++#define BAD_BLOCK_BYTE_NUM 6 ++#define BAD_BLOCK_IN_SPARE_AREA 16 ++#define WR_RD_BSY_GAP 17 ++#define ENABLE_BCH_ECC 27 ++ ++/* NAND_DEV0_ECC_CFG bits */ ++#define ECC_CFG_ECC_DISABLE 0 ++#define ECC_SW_RESET 1 ++#define ECC_MODE 4 ++#define ECC_PARITY_SIZE_BYTES_BCH 8 ++#define ECC_NUM_DATA_BYTES 16 ++#define ECC_FORCE_CLK_OPEN 30 ++ ++/* NAND_DEV_CMD1 bits */ ++#define READ_ADDR 0 ++ ++/* NAND_DEV_CMD_VLD bits */ ++#define READ_START_VLD 0 ++ ++/* NAND_EBI2_ECC_BUF_CFG bits */ ++#define NUM_STEPS 0 ++ ++/* NAND_ERASED_CW_DETECT_CFG bits */ ++#define ERASED_CW_ECC_MASK 1 ++#define AUTO_DETECT_RES 0 ++#define MASK_ECC (1 << ERASED_CW_ECC_MASK) ++#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) ++#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) ++#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) ++#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) ++ ++/* NAND_ERASED_CW_DETECT_STATUS bits */ ++#define PAGE_ALL_ERASED BIT(7) ++#define CODEWORD_ALL_ERASED BIT(6) ++#define PAGE_ERASED BIT(5) ++#define CODEWORD_ERASED BIT(4) ++#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) ++#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) ++ ++/* Version Mask */ ++#define NAND_VERSION_MAJOR_MASK 0xf0000000 ++#define NAND_VERSION_MAJOR_SHIFT 28 ++#define NAND_VERSION_MINOR_MASK 0x0fff0000 ++#define NAND_VERSION_MINOR_SHIFT 16 ++ ++/* NAND OP_CMDs */ ++#define PAGE_READ 0x2 ++#define PAGE_READ_WITH_ECC 0x3 ++#define PAGE_READ_WITH_ECC_SPARE 0x4 ++#define PROGRAM_PAGE 0x6 ++#define PAGE_PROGRAM_WITH_ECC 0x7 ++#define PROGRAM_PAGE_SPARE 0x9 ++#define BLOCK_ERASE 0xa ++#define FETCH_ID 0xb ++#define RESET_DEVICE 0xd ++ ++/* ++ * the NAND controller performs reads/writes with ECC in 516 byte chunks. ++ * the driver calls the chunks 'step' or 'codeword' interchangeably ++ */ ++#define NANDC_STEP_SIZE 512 ++ ++/* ++ * the largest page size we support is 8K, this will have 16 steps/codewords ++ * of 512 bytes each ++ */ ++#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) ++ ++/* we read at most 3 registers per codeword scan */ ++#define MAX_REG_RD (3 * MAX_NUM_STEPS) ++ ++/* ECC modes */ ++#define ECC_NONE BIT(0) ++#define ECC_RS_4BIT BIT(1) ++#define ECC_BCH_4BIT BIT(2) ++#define ECC_BCH_8BIT BIT(3) ++ ++struct desc_info { ++ struct list_head list; ++ ++ enum dma_transfer_direction dir; ++ struct scatterlist sgl; ++ struct dma_async_tx_descriptor *dma_desc; ++}; ++ ++/* ++ * holds the current register values that we want to write. acts as a contiguous ++ * chunk of memory which we use to write the controller registers through DMA. ++ */ ++struct nandc_regs { ++ u32 cmd; ++ u32 addr0; ++ u32 addr1; ++ u32 chip_sel; ++ u32 exec; ++ ++ u32 cfg0; ++ u32 cfg1; ++ u32 ecc_bch_cfg; ++ ++ u32 clrflashstatus; ++ u32 clrreadstatus; ++ ++ u32 cmd1; ++ u32 vld; ++ ++ u32 orig_cmd1; ++ u32 orig_vld; ++ ++ u32 ecc_buf_cfg; ++}; ++ ++/* ++ * @cmd_crci: ADM DMA CRCI for command flow control ++ * @data_crci: ADM DMA CRCI for data flow control ++ * @list: DMA descriptor list (list of desc_infos) ++ * @dma_done: completion param to denote end of last ++ * descriptor in the list ++ * @data_buffer: our local DMA buffer for page read/writes, ++ * used when we can't use the buffer provided ++ * by upper layers directly ++ * @buf_size/count/start: markers for chip->read_buf/write_buf functions ++ * @reg_read_buf: buffer for reading register data via DMA ++ * @reg_read_pos: marker for data read in reg_read_buf ++ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for ++ * ecc/non-ecc mode for the current nand flash ++ * device ++ * @regs: a contiguous chunk of memory for DMA register ++ * writes ++ * @ecc_strength: 4 bit or 8 bit ecc, received via DT ++ * @bus_width: 8 bit or 16 bit NAND bus width, received via DT ++ * @ecc_modes: supported ECC modes by the current controller, ++ * initialized via DT match data ++ * @cw_size: the number of bytes in a single step/codeword ++ * of a page, consisting of all data, ecc, spare ++ * and reserved bytes ++ * @cw_data: the number of bytes within a codeword protected ++ * by ECC ++ * @bch_enabled: flag to tell whether BCH or RS ECC mode is used ++ * @status: value to be returned if NAND_CMD_STATUS command ++ * is executed ++ */ ++struct qcom_nandc_data { ++ struct platform_device *pdev; ++ struct device *dev; ++ ++ void __iomem *base; ++ struct resource *res; ++ ++ struct clk *core_clk; ++ struct clk *aon_clk; ++ ++ /* DMA stuff */ ++ struct dma_chan *chan; ++ struct dma_slave_config slave_conf; ++ unsigned int cmd_crci; ++ unsigned int data_crci; ++ struct list_head list; ++ struct completion dma_done; ++ ++ /* MTD stuff */ ++ struct nand_chip chip; ++ struct mtd_info mtd; ++ ++ /* local data buffer and markers */ ++ u8 *data_buffer; ++ int buf_size; ++ int buf_count; ++ int buf_start; ++ ++ /* local buffer to read back registers */ ++ u32 *reg_read_buf; ++ int reg_read_pos; ++ ++ /* required configs */ ++ u32 cfg0, cfg1; ++ u32 cfg0_raw, cfg1_raw; ++ u32 ecc_buf_cfg; ++ u32 ecc_bch_cfg; ++ u32 clrflashstatus; ++ u32 clrreadstatus; ++ u32 sflashc_burst_cfg; ++ u32 cmd1, vld; ++ ++ /* register state */ ++ struct nandc_regs *regs; ++ ++ /* things we get from DT */ ++ int ecc_strength; ++ int bus_width; ++ ++ u32 ecc_modes; ++ ++ /* misc params */ ++ int cw_size; ++ int cw_data; ++ bool use_ecc; ++ bool bch_enabled; ++ u8 status; ++ int last_command; ++}; ++ ++static inline u32 nandc_read(struct qcom_nandc_data *this, int offset) ++{ ++ return ioread32(this->base + offset); ++} ++ ++static inline void nandc_write(struct qcom_nandc_data *this, int offset, ++ u32 val) ++{ ++ iowrite32(val, this->base + offset); ++} ++ ++/* helper to configure address register values */ ++static void set_address(struct qcom_nandc_data *this, u16 column, int page) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nandc_regs *regs = this->regs; ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ ++ regs->addr0 = page << 16 | column; ++ regs->addr1 = page >> 16 & 0xff; ++} ++ ++/* ++ * update_rw_regs: set up read/write register values, these will be ++ * written to the NAND controller registers via DMA ++ * ++ * @num_cw: number of steps for the read/write operation ++ * @read: read or write operation ++ */ ++static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ if (read) { ++ if (this->use_ecc) ++ regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; ++ else ++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; ++ } else { ++ regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; ++ } ++ ++ if (this->use_ecc) { ++ regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ ++ regs->cfg1 = this->cfg1; ++ regs->ecc_bch_cfg = this->ecc_bch_cfg; ++ } else { ++ regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ ++ regs->cfg1 = this->cfg1_raw; ++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ } ++ ++ regs->ecc_buf_cfg = this->ecc_buf_cfg; ++ regs->clrflashstatus = this->clrflashstatus; ++ regs->clrreadstatus = this->clrreadstatus; ++ regs->exec = 1; ++} ++ ++static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off, ++ const void *vaddr, int size, bool flow_control) ++{ ++ struct desc_info *desc; ++ struct dma_async_tx_descriptor *dma_desc; ++ struct scatterlist *sgl; ++ struct dma_slave_config slave_conf; ++ int r; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ list_add_tail(&desc->list, &this->list); ++ ++ sgl = &desc->sgl; ++ ++ sg_init_one(sgl, vaddr, size); ++ ++ desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; ++ ++ r = dma_map_sg(this->dev, sgl, 1, desc->dir); ++ if (r == 0) { ++ r = -ENOMEM; ++ goto err; ++ } ++ ++ memset(&slave_conf, 0x00, sizeof(slave_conf)); ++ ++ slave_conf.device_fc = flow_control; ++ if (read) { ++ slave_conf.src_maxburst = 16; ++ slave_conf.src_addr = this->res->start + reg_off; ++ slave_conf.slave_id = this->data_crci; ++ } else { ++ slave_conf.dst_maxburst = 16; ++ slave_conf.dst_addr = this->res->start + reg_off; ++ slave_conf.slave_id = this->cmd_crci; ++ } ++ ++ r = dmaengine_slave_config(this->chan, &slave_conf); ++ if (r) { ++ dev_err(this->dev, "failed to configure dma channel\n"); ++ goto err; ++ } ++ ++ dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0); ++ if (!dma_desc) { ++ dev_err(this->dev, "failed to prepare desc\n"); ++ r = -EINVAL; ++ goto err; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ return 0; ++err: ++ kfree(desc); ++ ++ return r; ++} ++ ++/* ++ * read_reg_dma: prepares a descriptor to read a given number of ++ * contiguous registers to the reg_read_buf pointer ++ * ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to read ++ */ ++static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) ++{ ++ bool flow_control = false; ++ void *vaddr; ++ int size; ++ ++ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) ++ flow_control = true; ++ ++ size = num_regs * sizeof(u32); ++ vaddr = this->reg_read_buf + this->reg_read_pos; ++ this->reg_read_pos += num_regs; ++ ++ return prep_dma_desc(this, true, first, vaddr, size, flow_control); ++} ++ ++/* ++ * write_reg_dma: prepares a descriptor to write a given number of ++ * contiguous registers ++ * ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to write ++ */ ++static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs) ++{ ++ bool flow_control = false; ++ struct nandc_regs *regs = this->regs; ++ void *vaddr; ++ int size; ++ ++ switch (first) { ++ case NAND_FLASH_CMD: ++ vaddr = ®s->cmd; ++ flow_control = true; ++ break; ++ case NAND_EXEC_CMD: ++ vaddr = ®s->exec; ++ break; ++ case NAND_FLASH_STATUS: ++ vaddr = ®s->clrflashstatus; ++ break; ++ case NAND_DEV0_CFG0: ++ vaddr = ®s->cfg0; ++ break; ++ case NAND_READ_STATUS: ++ vaddr = ®s->clrreadstatus; ++ break; ++ case NAND_DEV_CMD1: ++ vaddr = ®s->cmd1; ++ break; ++ case NAND_DEV_CMD1_RESTORE: ++ first = NAND_DEV_CMD1; ++ vaddr = ®s->orig_cmd1; ++ break; ++ case NAND_DEV_CMD_VLD: ++ vaddr = ®s->vld; ++ break; ++ case NAND_DEV_CMD_VLD_RESTORE: ++ first = NAND_DEV_CMD_VLD; ++ vaddr = ®s->orig_vld; ++ break; ++ case NAND_EBI2_ECC_BUF_CFG: ++ vaddr = ®s->ecc_buf_cfg; ++ break; ++ default: ++ dev_err(this->dev, "invalid starting register\n"); ++ return -EINVAL; ++ } ++ ++ size = num_regs * sizeof(u32); ++ ++ return prep_dma_desc(this, false, first, vaddr, size, flow_control); ++} ++ ++/* ++ * read_data_dma: prepares a DMA descriptor to transfer data from the ++ * controller's internal buffer to the buffer 'vaddr' ++ * ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ */ ++static int read_data_dma(struct qcom_nandc_data *this, int reg_off, ++ const u8 *vaddr, int size) ++{ ++ return prep_dma_desc(this, true, reg_off, vaddr, size, false); ++} ++ ++/* ++ * write_data_dma: prepares a DMA descriptor to transfer data from ++ * 'vaddr' to the controller's internal buffer ++ * ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to read from ++ * @size: DMA transaction size in bytes ++ */ ++static int write_data_dma(struct qcom_nandc_data *this, int reg_off, ++ const u8 *vaddr, int size) ++{ ++ return prep_dma_desc(this, false, reg_off, vaddr, size, false); ++} ++ ++/* ++ * helper to prepare dma descriptors to configure registers needed for reading a ++ * codeword/step in a page ++ */ ++static void config_cw_read(struct qcom_nandc_data *this) ++{ ++ write_reg_dma(this, NAND_FLASH_CMD, 3); ++ write_reg_dma(this, NAND_DEV0_CFG0, 3); ++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); ++ ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 2); ++ read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1); ++} ++ ++/* ++ * helpers to prepare dma descriptors used to configure registers needed for ++ * writing a codeword/step in a page ++ */ ++static void config_cw_write_pre(struct qcom_nandc_data *this) ++{ ++ write_reg_dma(this, NAND_FLASH_CMD, 3); ++ write_reg_dma(this, NAND_DEV0_CFG0, 3); ++ write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1); ++} ++ ++static void config_cw_write_post(struct qcom_nandc_data *this) ++{ ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 1); ++ ++ write_reg_dma(this, NAND_FLASH_STATUS, 1); ++ write_reg_dma(this, NAND_READ_STATUS, 1); ++} ++ ++/* ++ * the following functions are used within chip->cmdfunc() to perform different ++ * NAND_CMD_* commands ++ */ ++ ++/* sets up descriptors for NAND_CMD_PARAM */ ++static int nandc_param(struct qcom_nandc_data *this) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ /* ++ * NAND_CMD_PARAM is called before we know much about the FLASH chip ++ * in use. we configure the controller to perform a raw read of 512 ++ * bytes to read onfi params ++ */ ++ regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; ++ regs->addr0 = 0; ++ regs->addr1 = 0; ++ regs->cfg0 = 0 << CW_PER_PAGE ++ | 512 << UD_SIZE_BYTES ++ | 5 << NUM_ADDR_CYCLES ++ | 0 << SPARE_SIZE_BYTES; ++ ++ regs->cfg1 = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | 17 << BAD_BLOCK_BYTE_NUM ++ | 1 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | 0 << WIDE_FLASH ++ | 1 << DEV0_CFG1_ECC_DISABLE; ++ ++ regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ ++ /* configure CMD1 and VLD for ONFI param probing */ ++ regs->vld = (this->vld & ~(1 << READ_START_VLD)) ++ | 0 << READ_START_VLD; ++ ++ regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR)) ++ | NAND_CMD_PARAM << READ_ADDR; ++ ++ regs->exec = 1; ++ ++ regs->orig_cmd1 = this->cmd1; ++ regs->orig_vld = this->vld; ++ ++ write_reg_dma(this, NAND_DEV_CMD_VLD, 1); ++ write_reg_dma(this, NAND_DEV_CMD1, 1); ++ ++ this->buf_count = 512; ++ memset(this->data_buffer, 0xff, this->buf_count); ++ ++ config_cw_read(this); ++ ++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count); ++ ++ /* restore CMD1 and VLD regs */ ++ write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1); ++ write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_ERASE1 */ ++static int erase_block(struct qcom_nandc_data *this, int page_addr) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE; ++ regs->addr0 = page_addr; ++ regs->addr1 = 0; ++ regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE); ++ regs->cfg1 = this->cfg1_raw; ++ regs->exec = 1; ++ regs->clrflashstatus = this->clrflashstatus; ++ regs->clrreadstatus = this->clrreadstatus; ++ ++ write_reg_dma(this, NAND_FLASH_CMD, 3); ++ write_reg_dma(this, NAND_DEV0_CFG0, 2); ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 1); ++ ++ write_reg_dma(this, NAND_FLASH_STATUS, 1); ++ write_reg_dma(this, NAND_READ_STATUS, 1); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_READID */ ++static int read_id(struct qcom_nandc_data *this, int column) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ if (column == -1) ++ return 0; ++ ++ regs->cmd = FETCH_ID; ++ regs->addr0 = column; ++ regs->addr1 = 0; ++ regs->chip_sel = DM_EN; ++ regs->exec = 1; ++ ++ write_reg_dma(this, NAND_FLASH_CMD, 4); ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_READ_ID, 1); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_RESET */ ++static int reset(struct qcom_nandc_data *this) ++{ ++ struct nandc_regs *regs = this->regs; ++ ++ regs->cmd = RESET_DEVICE; ++ regs->exec = 1; ++ ++ write_reg_dma(this, NAND_FLASH_CMD, 1); ++ write_reg_dma(this, NAND_EXEC_CMD, 1); ++ ++ read_reg_dma(this, NAND_FLASH_STATUS, 1); ++ ++ return 0; ++} ++ ++/* helpers to submit/free our list of dma descriptors */ ++static void dma_callback(void *param) ++{ ++ struct qcom_nandc_data *this = param; ++ struct completion *c = &this->dma_done; ++ ++ complete(c); ++} ++ ++static int submit_descs(struct qcom_nandc_data *this) ++{ ++ struct completion *c = &this->dma_done; ++ struct desc_info *desc; ++ int r; ++ ++ init_completion(c); ++ ++ list_for_each_entry(desc, &this->list, list) { ++ /* ++ * we add a callback to the last descriptor in our list to ++ * notify completion of command ++ */ ++ if (list_is_last(&desc->list, &this->list)) { ++ desc->dma_desc->callback = dma_callback; ++ desc->dma_desc->callback_param = this; ++ } ++ ++ dmaengine_submit(desc->dma_desc); ++ } ++ ++ dma_async_issue_pending(this->chan); ++ ++ r = wait_for_completion_timeout(c, msecs_to_jiffies(500)); ++ if (!r) ++ return -ETIMEDOUT; ++ ++ return 0; ++} ++ ++static void free_descs(struct qcom_nandc_data *this) ++{ ++ struct desc_info *desc, *n; ++ ++ list_for_each_entry_safe(desc, n, &this->list, list) { ++ list_del(&desc->list); ++ dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir); ++ kfree(desc); ++ } ++} ++ ++/* reset the register read buffer for next NAND operation */ ++static void clear_read_regs(struct qcom_nandc_data *this) ++{ ++ this->reg_read_pos = 0; ++ memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf)); ++} ++ ++static void pre_command(struct qcom_nandc_data *this, int command) ++{ ++ this->buf_count = 0; ++ this->buf_start = 0; ++ this->use_ecc = false; ++ this->last_command = command; ++ ++ clear_read_regs(this); ++} ++ ++/* ++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our ++ * privately maintained status byte, this status byte can be read after ++ * NAND_CMD_STATUS is called ++ */ ++static void parse_erase_write_errors(struct qcom_nandc_data *this, int command) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int num_cw; ++ int i; ++ ++ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; ++ ++ for (i = 0; i < num_cw; i++) { ++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[i]); ++ ++ if (flash_status & FS_MPU_ERR) ++ this->status &= ~NAND_STATUS_WP; ++ ++ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && ++ (flash_status & FS_DEVICE_STS_ERR))) ++ this->status |= NAND_STATUS_FAIL; ++ } ++} ++ ++static void post_command(struct qcom_nandc_data *this, int command) ++{ ++ switch (command) { ++ case NAND_CMD_READID: ++ memcpy(this->data_buffer, this->reg_read_buf, this->buf_count); ++ break; ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ parse_erase_write_errors(this, command); ++ break; ++ default: ++ break; ++ } ++} ++ ++/* ++ * Implements chip->cmdfunc. It's only used for a limited set of commands. ++ * The rest of the commands wouldn't be called by upper layers. For example, ++ * NAND_CMD_READOOB would never be called because we have our own versions ++ * of read_oob ops for nand_ecc_ctrl. ++ */ ++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ struct qcom_nandc_data *this = chip->priv; ++ bool wait = false; ++ int r = 0; ++ ++ pre_command(this, command); ++ ++ switch (command) { ++ case NAND_CMD_RESET: ++ r = reset(this); ++ wait = true; ++ break; ++ ++ case NAND_CMD_READID: ++ this->buf_count = 4; ++ r = read_id(this, column); ++ wait = true; ++ break; ++ ++ case NAND_CMD_PARAM: ++ r = nandc_param(this); ++ wait = true; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ r = erase_block(this, page_addr); ++ wait = true; ++ break; ++ ++ case NAND_CMD_READ0: ++ /* we read the entire page for now */ ++ WARN_ON(column != 0); ++ ++ this->use_ecc = true; ++ set_address(this, 0, page_addr); ++ update_rw_regs(this, ecc->steps, true); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ WARN_ON(column != 0); ++ set_address(this, 0, page_addr); ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_NONE: ++ default: ++ break; ++ } ++ ++ if (r) { ++ dev_err(this->dev, "failure executing command %d\n", ++ command); ++ free_descs(this); ++ return; ++ } ++ ++ if (wait) { ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, ++ "failure submitting descs for command %d\n", ++ command); ++ } ++ ++ free_descs(this); ++ ++ post_command(this, command); ++} ++ ++/* ++ * when using RS ECC, the NAND controller flags an error when reading an ++ * erased page. however, there are special characters at certain offsets when ++ * we read the erased page. we check here if the page is really empty. if so, ++ * we replace the magic characters with 0xffs ++ */ ++static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage = ecc->steps; ++ u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS]; ++ int i, j; ++ ++ /* if BCH is enabled, HW will take care of detecting erased pages */ ++ if (this->bch_enabled || !this->use_ecc) ++ return false; ++ ++ for (i = 0; i < cwperpage; i++) { ++ u8 *empty1, *empty2; ++ __le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]); ++ ++ /* ++ * an erased page flags an error in NAND_FLASH_STATUS, check if ++ * the page is erased by looking for 0x54s at offsets 3 and 175 ++ * from the beginning of each codeword ++ */ ++ if (!(flash_status & FS_OP_ERR)) ++ break; ++ ++ empty1 = &data_buf[3 + i * this->cw_data]; ++ empty2 = &data_buf[175 + i * this->cw_data]; ++ ++ /* ++ * if the error wasn't because of an erased page, bail out and ++ * and let someone else do the error checking ++ */ ++ if ((*empty1 == 0x54 && *empty2 == 0xff) || ++ (*empty1 == 0xff && *empty2 == 0x54)) { ++ orig1[i] = *empty1; ++ orig2[i] = *empty2; ++ ++ *empty1 = 0xff; ++ *empty2 = 0xff; ++ } else { ++ break; ++ } ++ } ++ ++ if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize)) ++ goto not_empty; ++ ++ /* ++ * tell the caller that the page was empty and is fixed up, so that ++ * parse_read_errors() doesn't think it's an error ++ */ ++ return true; ++ ++not_empty: ++ /* restore original values if not empty*/ ++ for (j = 0; j < i; j++) { ++ data_buf[3 + j * this->cw_data] = orig1[j]; ++ data_buf[175 + j * this->cw_data] = orig2[j]; ++ } ++ ++ return false; ++} ++ ++struct read_stats { ++ __le32 flash; ++ __le32 buffer; ++ __le32 erased_cw; ++}; ++ ++/* ++ * reads back status registers set by the controller to notify page read ++ * errors. this is equivalent to what 'ecc->correct()' would do. ++ */ ++static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage = ecc->steps; ++ unsigned int max_bitflips = 0; ++ int i; ++ ++ for (i = 0; i < cwperpage; i++) { ++ int stat; ++ struct read_stats *buf; ++ ++ buf = (struct read_stats *) (this->reg_read_buf + 3 * i); ++ ++ buf->flash = le32_to_cpu(buf->flash); ++ buf->buffer = le32_to_cpu(buf->buffer); ++ buf->erased_cw = le32_to_cpu(buf->erased_cw); ++ ++ if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) { ++ ++ /* ignore erased codeword errors */ ++ if (this->bch_enabled) { ++ if ((buf->erased_cw & ERASED_CW) == ERASED_CW) ++ continue; ++ } else if (erased_page) { ++ continue; ++ } ++ ++ if (buf->buffer & BS_UNCORRECTABLE_BIT) { ++ mtd->ecc_stats.failed++; ++ continue; ++ } ++ } ++ ++ stat = buf->buffer & BS_CORRECTABLE_ERR_MSK; ++ mtd->ecc_stats.corrected += stat; ++ ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ ++ return max_bitflips; ++} ++ ++/* ++ * helper to perform the actual page read operation, used by ecc->read_page() ++ * and ecc->read_oob() ++ */ ++static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf, ++ u8 *oob_buf) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int i, r; ++ ++ /* queue cmd descs for each codeword */ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size, oob_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + ecc->bytes; ++ } else { ++ data_size = this->cw_data; ++ oob_size = ecc->bytes; ++ } ++ ++ config_cw_read(this); ++ ++ if (data_buf) ++ read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); ++ ++ if (oob_buf) ++ read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf, ++ oob_size); ++ ++ if (data_buf) ++ data_buf += data_size; ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, "failure to read page/oob\n"); ++ ++ free_descs(this); ++ ++ return r; ++} ++ ++/* ++ * a helper that copies the last step/codeword of a page (containing free oob) ++ * into our local buffer ++ */ ++static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page) ++{ ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int size; ++ int r; ++ ++ clear_read_regs(this); ++ ++ size = use_ecc ? this->cw_data : this->cw_size; ++ ++ /* prepare a clean read buffer */ ++ memset(this->data_buffer, 0xff, size); ++ ++ this->use_ecc = use_ecc; ++ set_address(this, this->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(this, 1, true); ++ ++ config_cw_read(this); ++ ++ read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size); ++ ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, "failed to copy last codeword\n"); ++ ++ free_descs(this); ++ ++ return r; ++} ++ ++/* implements ecc->read_page() */ ++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ u8 *data_buf, *oob_buf = NULL; ++ bool erased_page; ++ int r; ++ ++ data_buf = buf; ++ oob_buf = oob_required ? chip->oob_poi : NULL; ++ ++ r = read_page_low(this, data_buf, oob_buf); ++ if (r) { ++ dev_err(this->dev, "failure to read page\n"); ++ return r; ++ } ++ ++ erased_page = empty_page_fixup(this, data_buf); ++ ++ return parse_read_errors(this, erased_page); ++} ++ ++/* implements ecc->read_oob() */ ++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int r; ++ ++ clear_read_regs(this); ++ ++ this->use_ecc = true; ++ set_address(this, 0, page); ++ update_rw_regs(this, ecc->steps, true); ++ ++ r = read_page_low(this, NULL, chip->oob_poi); ++ if (r) ++ dev_err(this->dev, "failure to read oob\n"); ++ ++ return r; ++} ++ ++/* implements ecc->read_oob_raw(), used to read the bad block marker flag */ ++static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ uint8_t *oob = chip->oob_poi; ++ int start, length; ++ int r; ++ ++ /* ++ * configure registers for a raw page read, the address is set to the ++ * beginning of the last codeword, we don't care about reading ecc ++ * portion of oob, just the free stuff ++ */ ++ r = copy_last_cw(this, false, page); ++ if (r) ++ return r; ++ ++ /* ++ * reading raw oob has 2 parts, first the bad block byte, then the ++ * actual free oob region. perform a memcpy in two steps ++ */ ++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); ++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; ++ ++ memcpy(oob, this->data_buffer + start, length); ++ ++ oob += length; ++ ++ start = this->cw_data - (ecc->steps << 2) + 1; ++ length = ecc->steps << 2; ++ ++ memcpy(oob, this->data_buffer + start, length); ++ ++ return 0; ++} ++ ++/* implements ecc->write_page() */ ++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ u8 *data_buf, *oob_buf; ++ int i, r = 0; ++ ++ clear_read_regs(this); ++ ++ data_buf = (u8 *) buf; ++ oob_buf = chip->oob_poi; ++ ++ this->use_ecc = true; ++ update_rw_regs(this, ecc->steps, false); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size, oob_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + ecc->bytes; ++ } else { ++ data_size = this->cw_data; ++ oob_size = ecc->bytes; ++ } ++ ++ config_cw_write_pre(this); ++ write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size); ++ ++ /* ++ * we don't really need to write anything to oob for the ++ * first n - 1 codewords since these oob regions just ++ * contain ecc that's written by the controller itself ++ */ ++ if (i == (ecc->steps - 1)) ++ write_data_dma(this, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size); ++ config_cw_write_post(this); ++ ++ data_buf += data_size; ++ oob_buf += oob_size; ++ } ++ ++ r = submit_descs(this); ++ if (r) ++ dev_err(this->dev, "failure to write page\n"); ++ ++ free_descs(this); ++ ++ return r; ++} ++ ++/* ++ * implements ecc->write_oob() ++ * ++ * the NAND controller cannot write only data or only oob within a codeword, ++ * since ecc is calculated for the combined codeword. we first copy the ++ * entire contents for the last codeword(data + oob), replace the old oob ++ * with the new one in chip->oob_poi, and then write the entire codeword. ++ * this read-copy-write operation results in a slight perormance loss. ++ */ ++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ uint8_t *oob = chip->oob_poi; ++ int free_boff; ++ int data_size, oob_size; ++ int r, status = 0; ++ ++ r = copy_last_cw(this, true, page); ++ if (r) ++ return r; ++ ++ clear_read_regs(this); ++ ++ /* calculate the data and oob size for the last codeword/step */ ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + ecc->bytes; ++ ++ /* ++ * the location of spare data in the oob buffer, we could also use ++ * ecc->layout.oobfree here ++ */ ++ free_boff = ecc->bytes * (ecc->steps - 1); ++ ++ /* override new oob content to last codeword */ ++ memcpy(this->data_buffer + data_size, oob + free_boff, oob_size); ++ ++ this->use_ecc = true; ++ set_address(this, this->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(this, 1, false); ++ ++ config_cw_write_pre(this); ++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, ++ data_size + oob_size); ++ config_cw_write_post(this); ++ ++ r = submit_descs(this); ++ ++ free_descs(this); ++ ++ if (r) { ++ dev_err(this->dev, "failure to write oob\n"); ++ return -EIO; ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++/* implements ecc->write_oob_raw(), used to write bad block marker flag */ ++static int qcom_nandc_write_oob_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ struct qcom_nandc_data *this = chip->priv; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ uint8_t *oob = chip->oob_poi; ++ int start, length; ++ int r, status = 0; ++ ++ r = copy_last_cw(this, false, page); ++ if (r) ++ return r; ++ ++ clear_read_regs(this); ++ ++ /* ++ * writing raw oob has 2 parts, first the bad block region, then the ++ * actual free region ++ */ ++ start = mtd->writesize - (this->cw_size * (ecc->steps - 1)); ++ length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1; ++ ++ memcpy(this->data_buffer + start, oob, length); ++ ++ oob += length; ++ ++ start = this->cw_data - (ecc->steps << 2) + 1; ++ length = ecc->steps << 2; ++ ++ memcpy(this->data_buffer + start, oob, length); ++ ++ /* prepare write */ ++ this->use_ecc = false; ++ set_address(this, this->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(this, 1, false); ++ ++ config_cw_write_pre(this); ++ write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size); ++ config_cw_write_post(this); ++ ++ r = submit_descs(this); ++ ++ free_descs(this); ++ ++ if (r) { ++ dev_err(this->dev, "failure to write updated oob\n"); ++ return -EIO; ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++/* ++ * the three functions below implement chip->read_byte(), chip->read_buf() ++ * and chip->write_buf() respectively. these aren't used for ++ * reading/writing page data, they are used for smaller data like reading ++ * id, status etc ++ */ ++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ uint8_t *buf = this->data_buffer; ++ uint8_t ret = 0x0; ++ ++ if (this->last_command == NAND_CMD_STATUS) { ++ ret = this->status; ++ ++ this->status = NAND_STATUS_READY | NAND_STATUS_WP; ++ ++ return ret; ++ } ++ ++ if (this->buf_start < this->buf_count) ++ ret = buf[this->buf_start++]; ++ ++ return ret; ++} ++ ++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); ++ ++ memcpy(buf, this->data_buffer + this->buf_start, real_len); ++ this->buf_start += real_len; ++} ++ ++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ int real_len = min_t(size_t, len, this->buf_count - this->buf_start); ++ ++ memcpy(this->data_buffer + this->buf_start, buf, real_len); ++ ++ this->buf_start += real_len; ++} ++ ++/* we support only one external chip for now */ ++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct qcom_nandc_data *this = chip->priv; ++ ++ if (chipnr <= 0) ++ return; ++ ++ dev_warn(this->dev, "invalid chip select\n"); ++} ++ ++/* ++ * NAND controller page layout info ++ * ++ * |-----------------------| |---------------------------------| ++ * | xx.......xx| | *********xx.......xx| ++ * | DATA xx..ECC..xx| | DATA **SPARE**xx..ECC..xx| ++ * | (516) xx.......xx| | (516-n*4) **(n*4)**xx.......xx| ++ * | xx.......xx| | *********xx.......xx| ++ * |-----------------------| |---------------------------------| ++ * codeword 1,2..n-1 codeword n ++ * <---(528/532 Bytes)----> <-------(528/532 Bytes)----------> ++ * ++ * n = number of codewords in the page ++ * . = ECC bytes ++ * * = spare bytes ++ * x = unused/reserved bytes ++ * ++ * 2K page: n = 4, spare = 16 bytes ++ * 4K page: n = 8, spare = 32 bytes ++ * 8K page: n = 16, spare = 64 bytes ++ * ++ * the qcom nand controller operates at a sub page/codeword level. each ++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. ++ * the number of ECC bytes vary based on the ECC strength and the bus width. ++ * ++ * the first n - 1 codewords contains 516 bytes of user data, the remaining ++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains ++ * both user data and spare(oobavail) bytes that sum up to 516 bytes. ++ * ++ * the layout described above is used by the controller when the ECC block is ++ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are ++ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout ++ * layouts defined below doesn't consider the positions occupied by the reserved ++ * bytes ++ * ++ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width) ++ * in the last codeword is the position of bad block marker. the bad block ++ * marker cannot be accessed when ECC is enabled. ++ * ++ */ ++ ++/* ++ * Layouts for different page sizes and ecc modes. We skip the eccpos field ++ * since it isn't needed for this driver ++ */ ++ ++/* 2K page, 4 bit ECC */ ++static struct nand_ecclayout layout_oob_64 = { ++ .eccbytes = 40, ++ .oobfree = { ++ { 30, 16 }, ++ }, ++}; ++ ++/* 4K page, 4 bit ECC, 8/16 bit bus width */ ++static struct nand_ecclayout layout_oob_128 = { ++ .eccbytes = 80, ++ .oobfree = { ++ { 70, 32 }, ++ }, ++}; ++ ++/* 4K page, 8 bit ECC, 8 bit bus width */ ++static struct nand_ecclayout layout_oob_224_x8 = { ++ .eccbytes = 104, ++ .oobfree = { ++ { 91, 32 }, ++ }, ++}; ++ ++/* 4K page, 8 bit ECC, 16 bit bus width */ ++static struct nand_ecclayout layout_oob_224_x16 = { ++ .eccbytes = 112, ++ .oobfree = { ++ { 98, 32 }, ++ }, ++}; ++ ++/* 8K page, 4 bit ECC, 8/16 bit bus width */ ++static struct nand_ecclayout layout_oob_256 = { ++ .eccbytes = 160, ++ .oobfree = { ++ { 151, 64 }, ++ }, ++}; ++ ++/* ++ * this is called before scan_ident, we do some minimal configurations so ++ * that reading ID and ONFI params work ++ */ ++static void qcom_nandc_pre_init(struct qcom_nandc_data *this) ++{ ++ /* kill onenand */ ++ nandc_write(this, SFLASHC_BURST_CFG, 0); ++ ++ /* enable ADM DMA */ ++ nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN); ++ ++ /* save the original values of these registers */ ++ this->cmd1 = nandc_read(this, NAND_DEV_CMD1); ++ this->vld = nandc_read(this, NAND_DEV_CMD_VLD); ++ ++ /* initial status value */ ++ this->status = NAND_STATUS_READY | NAND_STATUS_WP; ++} ++ ++static int qcom_nandc_ecc_init(struct qcom_nandc_data *this) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage; ++ bool wide_bus; ++ ++ /* the nand controller fetches codewords/chunks of 512 bytes */ ++ cwperpage = mtd->writesize >> 9; ++ ++ ecc->strength = this->ecc_strength; ++ ++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; ++ ++ if (ecc->strength >= 8) { ++ /* 8 bit ECC defaults to BCH ECC on all platforms */ ++ ecc->bytes = wide_bus ? 14 : 13; ++ } else { ++ /* ++ * if the controller supports BCH for 4 bit ECC, the controller ++ * uses lesser bytes for ECC. If RS is used, the ECC bytes is ++ * always 10 bytes ++ */ ++ if (this->ecc_modes & ECC_BCH_4BIT) ++ ecc->bytes = wide_bus ? 8 : 7; ++ else ++ ecc->bytes = 10; ++ } ++ ++ /* each step consists of 512 bytes of data */ ++ ecc->size = NANDC_STEP_SIZE; ++ ++ ecc->read_page = qcom_nandc_read_page; ++ ecc->read_oob = qcom_nandc_read_oob; ++ ecc->write_page = qcom_nandc_write_page; ++ ecc->write_oob = qcom_nandc_write_oob; ++ ++ /* ++ * the bad block marker is readable only when we read the page with ECC ++ * disabled. all the ops above run with ECC enabled. We need raw read ++ * and write function for oob in order to access bad block marker. ++ */ ++ ecc->read_oob_raw = qcom_nandc_read_oob_raw; ++ ecc->write_oob_raw = qcom_nandc_write_oob_raw; ++ ++ switch (mtd->oobsize) { ++ case 64: ++ ecc->layout = &layout_oob_64; ++ break; ++ case 128: ++ ecc->layout = &layout_oob_128; ++ break; ++ case 224: ++ if (wide_bus) ++ ecc->layout = &layout_oob_224_x16; ++ else ++ ecc->layout = &layout_oob_224_x8; ++ break; ++ case 256: ++ ecc->layout = &layout_oob_256; ++ break; ++ default: ++ dev_err(this->dev, "unsupported NAND device, oobsize %d\n", ++ mtd->oobsize); ++ return -ENODEV; ++ } ++ ++ ecc->mode = NAND_ECC_HW; ++ ++ /* enable ecc by default */ ++ this->use_ecc = true; ++ ++ return 0; ++} ++ ++static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int cwperpage = mtd->writesize / ecc->size; ++ int spare_bytes, bad_block_byte; ++ bool wide_bus; ++ int ecc_mode = 0; ++ ++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; ++ ++ if (ecc->strength >= 8) { ++ this->cw_size = 532; ++ ++ spare_bytes = wide_bus ? 0 : 2; ++ ++ this->bch_enabled = true; ++ ecc_mode = 1; ++ } else { ++ this->cw_size = 528; ++ ++ if (this->ecc_modes & ECC_BCH_4BIT) { ++ spare_bytes = wide_bus ? 2 : 4; ++ ++ this->bch_enabled = true; ++ ecc_mode = 0; ++ } else { ++ spare_bytes = wide_bus ? 0 : 1; ++ } ++ } ++ ++ /* ++ * DATA_UD_BYTES varies based on whether the read/write command protects ++ * spare data with ECC too. We protect spare data by default, so we set ++ * it to main + spare data, which are 512 and 4 bytes respectively. ++ */ ++ this->cw_data = 516; ++ ++ bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1; ++ ++ this->cfg0 = (cwperpage - 1) << CW_PER_PAGE ++ | this->cw_data << UD_SIZE_BYTES ++ | 0 << DISABLE_STATUS_AFTER_WRITE ++ | 5 << NUM_ADDR_CYCLES ++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_RS ++ | 0 << STATUS_BFR_READ ++ | 1 << SET_RD_MODE_AFTER_STATUS ++ | spare_bytes << SPARE_SIZE_BYTES; ++ ++ this->cfg1 = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | bad_block_byte << BAD_BLOCK_BYTE_NUM ++ | 0 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | wide_bus << WIDE_FLASH ++ | this->bch_enabled << ENABLE_BCH_ECC; ++ ++ this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE ++ | this->cw_size << UD_SIZE_BYTES ++ | 5 << NUM_ADDR_CYCLES ++ | 0 << SPARE_SIZE_BYTES; ++ ++ this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | 17 << BAD_BLOCK_BYTE_NUM ++ | 1 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | wide_bus << WIDE_FLASH ++ | 1 << DEV0_CFG1_ECC_DISABLE; ++ ++ this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE ++ | 0 << ECC_SW_RESET ++ | this->cw_data << ECC_NUM_DATA_BYTES ++ | 1 << ECC_FORCE_CLK_OPEN ++ | ecc_mode << ECC_MODE ++ | ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH; ++ ++ this->ecc_buf_cfg = 0x203 << NUM_STEPS; ++ ++ this->clrflashstatus = FS_READY_BSY_N; ++ this->clrreadstatus = 0xc0; ++ ++ dev_dbg(this->dev, ++ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", ++ this->cfg0, this->cfg1, this->ecc_buf_cfg, ++ this->ecc_bch_cfg, this->cw_size, this->cw_data, ++ ecc->strength, ecc->bytes, cwperpage); ++} ++ ++static int qcom_nandc_alloc(struct qcom_nandc_data *this) ++{ ++ int r; ++ ++ r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32)); ++ if (r) { ++ dev_err(this->dev, "failed to set DMA mask\n"); ++ return r; ++ } ++ ++ /* ++ * we use the internal buffer for reading ONFI params, reading small ++ * data like ID and status, and preforming read-copy-write operations ++ * when writing to a codeword partially. 532 is the maximum possible ++ * size of a codeword for our nand controller ++ */ ++ this->buf_size = 532; ++ ++ this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL); ++ if (!this->data_buffer) ++ return -ENOMEM; ++ ++ this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL); ++ if (!this->regs) ++ return -ENOMEM; ++ ++ this->reg_read_buf = devm_kzalloc(this->dev, ++ MAX_REG_RD * sizeof(*this->reg_read_buf), ++ GFP_KERNEL); ++ if (!this->reg_read_buf) ++ return -ENOMEM; ++ ++ INIT_LIST_HEAD(&this->list); ++ ++ this->chan = dma_request_slave_channel(this->dev, "rxtx"); ++ if (!this->chan) { ++ dev_err(this->dev, "failed to request slave channel\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void qcom_nandc_unalloc(struct qcom_nandc_data *this) ++{ ++ dma_release_channel(this->chan); ++} ++ ++static int qcom_nandc_init(struct qcom_nandc_data *this) ++{ ++ struct mtd_info *mtd = &this->mtd; ++ struct nand_chip *chip = &this->chip; ++ struct device_node *np = this->dev->of_node; ++ struct mtd_part_parser_data ppdata = { .of_node = np }; ++ int r; ++ ++ mtd->priv = chip; ++ mtd->name = "qcom-nandc"; ++ mtd->owner = THIS_MODULE; ++ ++ chip->priv = this; ++ ++ chip->cmdfunc = qcom_nandc_command; ++ chip->select_chip = qcom_nandc_select_chip; ++ chip->read_byte = qcom_nandc_read_byte; ++ chip->read_buf = qcom_nandc_read_buf; ++ chip->write_buf = qcom_nandc_write_buf; ++ ++ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; ++ if (this->bus_width == 16) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW; ++ if (of_get_nand_on_flash_bbt(np)) ++ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; ++ ++ qcom_nandc_pre_init(this); ++ ++ r = nand_scan_ident(mtd, 1, NULL); ++ if (r) ++ return r; ++ ++ r = qcom_nandc_ecc_init(this); ++ if (r) ++ return r; ++ ++ qcom_nandc_hw_post_init(this); ++ ++ r = nand_scan_tail(mtd); ++ if (r) ++ return r; ++ ++ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); ++} ++ ++static int qcom_nandc_parse_dt(struct platform_device *pdev) ++{ ++ struct qcom_nandc_data *this = platform_get_drvdata(pdev); ++ struct device_node *np = this->dev->of_node; ++ int r; ++ ++ this->ecc_strength = of_get_nand_ecc_strength(np); ++ if (this->ecc_strength < 0) { ++ dev_warn(this->dev, ++ "incorrect ecc strength, setting to 4 bits/step\n"); ++ this->ecc_strength = 4; ++ } ++ ++ this->bus_width = of_get_nand_bus_width(np); ++ if (this->bus_width < 0) { ++ dev_warn(this->dev, "incorrect bus width, setting to 8\n"); ++ this->bus_width = 8; ++ } ++ ++ r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci); ++ if (r) { ++ dev_err(this->dev, "command CRCI unspecified\n"); ++ return r; ++ } ++ ++ r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci); ++ if (r) { ++ dev_err(this->dev, "data CRCI unspecified\n"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++static int qcom_nandc_probe(struct platform_device *pdev) ++{ ++ struct qcom_nandc_data *this; ++ const struct of_device_id *match; ++ int r; ++ ++ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); ++ if (!this) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, this); ++ ++ this->pdev = pdev; ++ this->dev = &pdev->dev; ++ ++ match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); ++ if (!match) { ++ dev_err(&pdev->dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ ++ if (!match->data) { ++ dev_err(&pdev->dev, "failed to get device data\n"); ++ return -ENODEV; ++ } ++ ++ this->ecc_modes = (u32) match->data; ++ ++ this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ this->base = devm_ioremap_resource(&pdev->dev, this->res); ++ if (IS_ERR(this->base)) ++ return PTR_ERR(this->base); ++ ++ this->core_clk = devm_clk_get(&pdev->dev, "core"); ++ if (IS_ERR(this->core_clk)) ++ return PTR_ERR(this->core_clk); ++ ++ this->aon_clk = devm_clk_get(&pdev->dev, "aon"); ++ if (IS_ERR(this->aon_clk)) ++ return PTR_ERR(this->aon_clk); ++ ++ r = qcom_nandc_parse_dt(pdev); ++ if (r) ++ return r; ++ ++ r = qcom_nandc_alloc(this); ++ if (r) ++ return r; ++ ++ r = clk_prepare_enable(this->core_clk); ++ if (r) ++ goto err_core_clk; ++ ++ r = clk_prepare_enable(this->aon_clk); ++ if (r) ++ goto err_aon_clk; ++ ++ r = qcom_nandc_init(this); ++ if (r) ++ goto err_init; ++ ++ return 0; ++ ++err_init: ++ clk_disable_unprepare(this->aon_clk); ++err_aon_clk: ++ clk_disable_unprepare(this->core_clk); ++err_core_clk: ++ qcom_nandc_unalloc(this); ++ ++ return r; ++} ++ ++static int qcom_nandc_remove(struct platform_device *pdev) ++{ ++ struct qcom_nandc_data *this = platform_get_drvdata(pdev); ++ ++ qcom_nandc_unalloc(this); ++ ++ clk_disable_unprepare(this->aon_clk); ++ clk_disable_unprepare(this->core_clk); ++ ++ return 0; ++} ++ ++#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT) ++ ++/* ++ * data will hold a struct pointer containing more differences once we support ++ * more IPs ++ */ ++static const struct of_device_id qcom_nandc_of_match[] = { ++ { .compatible = "qcom,ebi2-nandc", ++ .data = (void *) EBI2_NANDC_ECC_MODES, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); ++ ++static struct platform_driver qcom_nandc_driver = { ++ .driver = { ++ .name = "qcom-nandc", ++ .of_match_table = qcom_nandc_of_match, ++ }, ++ .probe = qcom_nandc_probe, ++ .remove = qcom_nandc_remove, ++}; ++module_platform_driver(qcom_nandc_driver); ++ ++MODULE_AUTHOR("Archit Taneja "); ++MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -55,5 +55,6 @@ + obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o + obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o + obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ ++obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o + + nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch new file mode 100644 index 0000000000..6530eb1c86 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch @@ -0,0 +1,82 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings +From: Archit Taneja +X-Patchwork-Id: 6927141 +Message-Id: <1438578498-32254-4-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja , devicetree@vger.kernel.org +Date: Mon, 3 Aug 2015 10:38:16 +0530 + +Add DT bindings document for the Qualcomm NAND controller driver. + +Cc: devicetree@vger.kernel.org + +v3: +- Don't use '0x' when specifying nand controller address space +- Add optional property for on-flash bbt usage + +Acked-by: Andy Gross +Signed-off-by: Archit Taneja + +--- +.../devicetree/bindings/mtd/qcom_nandc.txt | 49 ++++++++++++++++++++++ + 1 file changed, 49 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt +@@ -0,0 +1,49 @@ ++* Qualcomm NAND controller ++ ++Required properties: ++- compatible: should be "qcom,ebi2-nand" for IPQ806x ++- reg: MMIO address range ++- clocks: must contain core clock and always on clock ++- clock-names: must contain "core" for the core clock and "aon" for the ++ always on clock ++- dmas: DMA specifier, consisting of a phandle to the ADM DMA ++ controller node and the channel number to be used for ++ NAND. Refer to dma.txt and qcom_adm.txt for more details ++- dma-names: must be "rxtx" ++- qcom,cmd-crci: must contain the ADM command type CRCI block instance ++ number specified for the NAND controller on the given ++ platform ++- qcom,data-crci: must contain the ADM data type CRCI block instance ++ number specified for the NAND controller on the given ++ platform ++ ++Optional properties: ++- nand-bus-width: bus width. Must be 8 or 16. If not present, 8 is chosen ++ as default ++ ++- nand-ecc-strength: number of bits to correct per ECC step. Must be 4 or 8 ++ bits. If not present, 4 is chosen as default ++- nand-on-flash-bbt: Create/use on-flash bad block table ++ ++The device tree may optionally contain sub-nodes describing partitions of the ++address space. See partition.txt for more detail. ++ ++Example: ++ ++nand@1ac00000 { ++ compatible = "qcom,ebi2-nandc"; ++ reg = <0x1ac00000 0x800>; ++ ++ clocks = <&gcc EBI2_CLK>, ++ <&gcc EBI2_AON_CLK>; ++ clock-names = "core", "aon"; ++ ++ dmas = <&adm_dma 3>; ++ dma-names = "rxtx"; ++ qcom,cmd-crci = <15>; ++ qcom,data-crci = <3>; ++ ++ partition@0 { ++ ... ++ }; ++}; diff --git a/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch new file mode 100644 index 0000000000..4fc8a8ce90 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch @@ -0,0 +1,51 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x +From: Archit Taneja +X-Patchwork-Id: 6927121 +Message-Id: <1438578498-32254-5-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja , devicetree@vger.kernel.org +Date: Mon, 3 Aug 2015 10:38:17 +0530 + +The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding +compatible string. + +Cc: devicetree@vger.kernel.org + +Reviewed-by: Andy Gross +Signed-off-by: Archit Taneja + +--- +arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -747,6 +747,22 @@ + + status = "disabled"; + }; ++ ++ nand@1ac00000 { ++ compatible = "qcom,ebi2-nandc"; ++ reg = <0x1ac00000 0x800>; ++ ++ clocks = <&gcc EBI2_CLK>, ++ <&gcc EBI2_AON_CLK>; ++ clock-names = "core", "aon"; ++ ++ dmas = <&adm_dma 3>; ++ dma-names = "rxtx"; ++ qcom,cmd-crci = <15>; ++ qcom,data-crci = <3>; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch new file mode 100644 index 0000000000..9de3d8fe81 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch @@ -0,0 +1,76 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform +From: Archit Taneja +X-Patchwork-Id: 6927091 +Message-Id: <1438578498-32254-6-git-send-email-architt@codeaurora.org> +To: linux-mtd@lists.infradead.org, dehrenberg@google.com, + cernekee@gmail.com, computersforpeace@gmail.com +Cc: linux-arm-msm@vger.kernel.org, agross@codeaurora.org, + sboyd@codeaurora.org, linux-kernel@vger.kernel.org, + Archit Taneja , devicetree@vger.kernel.org +Date: Mon, 3 Aug 2015 10:38:18 +0530 + +Enable the NAND controller node on the AP148 platform. Provide pinmux +information. + +Cc: devicetree@vger.kernel.org + +Signed-off-by: Archit Taneja + +--- +arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -43,6 +43,28 @@ + 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; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -126,5 +148,19 @@ + status = "ok"; + phy-tx0-term-offset = <7>; + }; ++ ++ nand@1ac00000 { ++ status = "ok"; ++ ++ pinctrl-0 = <&nand_pins>; ++ pinctrl-names = "default"; ++ ++ nand-ecc-strength = <4>; ++ nand-bus-width = <8>; ++ }; + }; + }; ++ ++&adm_dma { ++ status = "ok"; ++}; diff --git a/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch new file mode 100644 index 0000000000..706028259d --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/166-arch-qcom-dts-enable-qcom-smem-on-AP148-NAND.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -157,6 +157,8 @@ + + nand-ecc-strength = <4>; + nand-bus-width = <8>; ++ ++ linux,part-probe = "qcom-smem"; + }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch new file mode 100644 index 0000000000..c0281ff070 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch @@ -0,0 +1,62 @@ +From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Wed, 29 Apr 2015 15:21:46 -0700 +Subject: [PATCH] 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 +--- + 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 +@@ -323,7 +323,7 @@ + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_HAS_SG_CHAIN + select ARM_PATCH_PHYS_VIRT +- select AUTO_ZRELADDR ++ select AUTO_ZRELADDR if !ARCH_QCOM + select CLKSRC_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -256,9 +256,11 @@ + 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 diff --git a/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch new file mode 100644 index 0000000000..8890a91731 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/302-mtd-qcom-smem-rename-rootfs-ubi.patch @@ -0,0 +1,13 @@ +--- 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 diff --git a/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch new file mode 100644 index 0000000000..625ff0a275 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch @@ -0,0 +1,146 @@ +From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Mon, 11 May 2015 16:32:09 -0700 +Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149 + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++- + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++ + 2 files changed, 85 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -19,8 +19,9 @@ + }; + }; + +- alias { ++ aliases { + serial0 = &uart4; ++ mdio-gpio0 = &mdio0; + }; + + chosen { +@@ -65,6 +66,15 @@ + bias-bus-hold; + }; + }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -160,6 +170,34 @@ + + linux,part-probe = "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 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>; ++ }; ++ }; + }; + }; + +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -16,6 +16,7 @@ + + alias { + serial0 = &uart2; ++ mdio-gpio0 = &mdio0; + }; + + chosen { +@@ -38,6 +39,15 @@ + bias-none; + }; + }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi2: gsbi@12480000 { +@@ -140,5 +150,44 @@ + 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 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>; ++ }; ++ ++ phy6: ethernet-phy@6 { ++ device_type = "ethernet-phy"; ++ reg = <6>; ++ }; ++ ++ phy7: ethernet-phy@7 { ++ device_type = "ethernet-phy"; ++ reg = <7>; ++ }; ++ }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch new file mode 100644 index 0000000000..691ebb66ba --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch @@ -0,0 +1,216 @@ +From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Mon, 11 May 2015 12:29:18 -0700 +Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms + +Signed-off-by: Mathieu Olivari +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++ + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++ + 3 files changed, 160 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -75,6 +75,16 @@ + 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 { +@@ -198,6 +208,31 @@ + 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; ++ }; ++ }; + }; + }; + +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -48,6 +48,14 @@ + bias-disable; + }; + }; ++ ++ rgmii0_pins: rgmii0_pins { ++ mux { ++ pins = "gpio2", "gpio66"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi2: gsbi@12480000 { +@@ -189,5 +197,40 @@ + 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>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -763,6 +763,92 @@ + + status = "disabled"; + }; ++ ++ nss_common: syscon@03000000 { ++ compatible = "syscon"; ++ reg = <0x03000000 0x0000FFFF>; ++ }; ++ ++ qsgmii_csr: syscon@1bb00000 { ++ compatible = "syscon"; ++ reg = <0x1bb00000 0x000001FF>; ++ }; ++ ++ gmac0: ethernet@37000000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37000000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE1_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE1_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac1: ethernet@37200000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37200000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE2_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE2_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37400000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE3_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE3_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac3: ethernet@37600000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37600000 0x200000>; ++ interrupts = ; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE4_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE4_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; + }; + + sfpb_mutex: sfpb-mutex { diff --git a/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch new file mode 100644 index 0000000000..8c4718e2a6 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/709-spi-qup-Fix-fifo-and-dma-support-for-IPQ806x.patch @@ -0,0 +1,134 @@ +From 16d2871830ff3fe12a6bff582549a9264adff278 Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir +Date: Tue, 10 May 2016 20:19:31 +0530 +Subject: [PATCH] spi: qup: Fix fifo and dma support for IPQ806x + +Signed-off-by: Ram Chandra Jangir +--- + drivers/spi/spi-qup.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 52 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c +index 810a7fa..0808017 100644 +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #define QUP_CONFIG 0x0000 + #define QUP_STATE 0x0004 +@@ -152,6 +153,7 @@ struct spi_qup { + int use_dma; + struct dma_slave_config rx_conf; + struct dma_slave_config tx_conf; ++ int mode; + }; + + +@@ -370,7 +372,8 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer) + return ret; + } + +- spi_qup_fifo_write(qup, xfer); ++ if (qup->mode == QUP_IO_M_MODE_FIFO) ++ spi_qup_fifo_write(qup, xfer); + + return 0; + } +@@ -448,6 +451,7 @@ spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer) + { + struct spi_qup *qup = spi_master_get_devdata(master); + u32 mode; ++ size_t dma_align = dma_get_cache_alignment(); + + qup->w_size = 4; + +@@ -458,6 +462,14 @@ spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer) + + qup->n_words = xfer->len / qup->w_size; + ++ if (!IS_ERR_OR_NULL(master->dma_rx) && ++ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) && ++ IS_ALIGNED((size_t)xfer->rx_buf, dma_align) && ++ !is_vmalloc_addr(xfer->tx_buf) && ++ !is_vmalloc_addr(xfer->rx_buf) && ++ (xfer->len > 3*qup->in_blk_sz)) ++ qup->use_dma = 1; ++ + if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32))) + mode = QUP_IO_M_MODE_FIFO; + else +@@ -491,7 +503,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + return -EIO; + } + +- mode = spi_qup_get_mode(spi->master, xfer); ++ controller->mode = mode = spi_qup_get_mode(spi->master, xfer); + n_words = controller->n_words; + + if (mode == QUP_IO_M_MODE_FIFO) { +@@ -500,6 +512,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); ++ controller->use_dma = 0; + } else if (!controller->use_dma) { + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); +@@ -750,6 +763,38 @@ err_tx: + return ret; + } + ++static void spi_qup_set_cs(struct spi_device *spi, bool val) ++{ ++ struct spi_qup *controller; ++ u32 spi_ioc; ++ u32 spi_ioc_orig; ++ ++ controller = spi_master_get_devdata(spi->master); ++ spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL); ++ spi_ioc_orig = spi_ioc; ++ if (!val) ++ spi_ioc |= SPI_IO_C_FORCE_CS; ++ else ++ spi_ioc &= ~SPI_IO_C_FORCE_CS; ++ ++ if (spi_ioc != spi_ioc_orig) ++ writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL); ++} ++ ++static int spi_qup_setup(struct spi_device *spi) ++{ ++ if (spi->cs_gpio >= 0) { ++ if (spi->mode & SPI_CS_HIGH) ++ gpio_set_value(spi->cs_gpio, 0); ++ else ++ gpio_set_value(spi->cs_gpio, 1); ++ ++ udelay(10); ++ } ++ ++ return 0; ++} ++ + static int spi_qup_probe(struct platform_device *pdev) + { + struct spi_master *master; +@@ -846,6 +891,11 @@ static int spi_qup_probe(struct platform_device *pdev) + if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1")) + controller->qup_v1 = 1; + ++ if (!controller->qup_v1) ++ master->set_cs = spi_qup_set_cs; ++ else ++ master->setup = spi_qup_setup; ++ + spin_lock_init(&controller->lock); + init_completion(&controller->done); + +-- +2.7.2 + diff --git a/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch b/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch new file mode 100644 index 0000000000..7573c963a7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch @@ -0,0 +1,43 @@ +From abc9f55079169806bcc31f29ec27f7df11c6184c Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir +Date: Thu, 4 Feb 2016 12:41:56 +0530 +Subject: [PATCH 2/2] watchdog: qcom: set WDT_BARK_TIME register offset to one + second less of bite time + +Currently WDT_BARK_TIME register offset is not configured with bark +timeout during wdt_start,and it is taking bark timeout's default value. +For some versions of TZ (secure mode) will consider a BARK the same +as BITE and reset the board. + +So instead let's just configure the BARK time to be less than a second +of the bite timeout so the board does not reset in this scenario + +Change-Id: Ie09850ad7e0470ed721e6924911ca2a81fd9ff8a +Signed-off-by: Ram Chandra Jangir +--- + drivers/watchdog/qcom-wdt.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c +index 773dcfa..002274a 100644 +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -22,6 +22,7 @@ + + #define WDT_RST 0x38 + #define WDT_EN 0x40 ++#define WDT_BARK_TIME 0x4C + #define WDT_BITE_TIME 0x5C + + struct qcom_wdt { +@@ -44,6 +45,7 @@ static int qcom_wdt_start(struct watchdog_device *wdd) + + writel(0, wdt->base + WDT_EN); + writel(1, wdt->base + WDT_RST); ++ writel((wdd->timeout - 1) * wdt->rate, wdt->base + WDT_BARK_TIME); + writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); + writel(1, wdt->base + WDT_EN); + return 0; +-- +2.7.2 + diff --git a/target/linux/ipq806x/patches-4.4/800-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch b/target/linux/ipq806x/patches-4.4/800-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch new file mode 100644 index 0000000000..1f5eba8377 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/800-ARM-qcom-add-Netgear-Nighthawk-X4-R7500-device-tree.patch @@ -0,0 +1,367 @@ +From 7e77aa188a7a7c4391856a9e5ef5ef58f769e679 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Sun, 9 Aug 2015 13:02:38 +0200 +Subject: [PATCH] ARM: qcom: add Netgear Nighthawk X4 R7500 device tree + +Signed-off-by: Jonas Gorski +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-ipq8064-r7500.dts | 370 +++++++++++++++++++++++++++++++ + 2 files changed, 371 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-r7500.dts + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -452,6 +452,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8084-mtp.dtb \ + qcom-ipq8064-ap148.dtb \ + qcom-ipq8064-db149.dtb \ ++ qcom-ipq8064-r7500.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-sony-xperia-honami.dtb +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-r7500.dts +@@ -0,0 +1,342 @@ ++#include "qcom-ipq8064-v1.0.dtsi" ++ ++#include ++ ++/ { ++ 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 = &uart4; ++ mdio-gpio0 = &mdio0; ++ }; ++ ++ chosen { ++ bootargs = "rootfstype=squashfs noinitrd"; ++ linux,stdout-path = "serial0:115200n8"; ++ }; ++ ++ soc { ++ pinmux@800000 { ++ i2c4_pins: i2c4_pinmux { ++ pins = "gpio12", "gpio13"; ++ function = "gsbi4"; ++ 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; ++ }; ++ }; ++ ++ 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 = ; ++ 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"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ }; ++ ++ nand@1ac00000 { ++ status = "ok"; ++ ++ pinctrl-0 = <&nand_pins>; ++ pinctrl-names = "default"; ++ ++ nand-ecc-strength = <4>; ++ nand-bus-width = <8>; ++ ++ #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 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 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; ++ }; ++ }; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ ++ wifi { ++ label = "wifi"; ++ gpios = <&qcom_pinmux 6 1>; ++ linux,code = ; ++ }; ++ ++ reset { ++ label = "reset"; ++ gpios = <&qcom_pinmux 54 1>; ++ linux,code = ; ++ }; ++ ++ wps { ++ label = "wps"; ++ gpios = <&qcom_pinmux 65 1>; ++ linux,code = ; ++ }; ++ }; ++ ++ gpio-leds { ++ compatible = "gpio-leds"; ++ ++ usb1 { ++ label = "r7500:amber:usb1"; ++ gpios = <&qcom_pinmux 7 0>; ++ }; ++ ++ usb3 { ++ label = "r7500:amber:usb3"; ++ gpios = <&qcom_pinmux 8 0>; ++ }; ++ ++ status { ++ label = "r7500:amber:status"; ++ gpios = <&qcom_pinmux 9 0>; ++ }; ++ ++ internet { ++ label = "r7500:white:internet"; ++ gpios = <&qcom_pinmux 22 0>; ++ }; ++ ++ wan { ++ label = "r7500:white:wan"; ++ gpios = <&qcom_pinmux 23 0>; ++ }; ++ ++ wps { ++ label = "r7500:white:wps"; ++ gpios = <&qcom_pinmux 24 0>; ++ }; ++ ++ esata { ++ label = "r7500:white:esata"; ++ gpios = <&qcom_pinmux 26 0>; ++ }; ++ ++ power { ++ label = "r7500:white:power"; ++ gpios = <&qcom_pinmux 53 0>; ++ default-state = "on"; ++ }; ++ ++ rfkill { ++ label = "r7500:white:rfkill"; ++ gpios = <&qcom_pinmux 64 0>; ++ }; ++ ++ wifi5g { ++ label = "r7500:white:wifi5g"; ++ gpios = <&qcom_pinmux 67 0>; ++ }; ++ }; ++}; ++ ++&adm_dma { ++ status = "ok"; ++}; diff --git a/target/linux/ipq806x/patches-4.4/801-ARM-qcom-add-Netgear-Nighthawk-X4-D7800-device-tree.patch b/target/linux/ipq806x/patches-4.4/801-ARM-qcom-add-Netgear-Nighthawk-X4-D7800-device-tree.patch new file mode 100644 index 0000000000..eaf037f57e --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/801-ARM-qcom-add-Netgear-Nighthawk-X4-D7800-device-tree.patch @@ -0,0 +1,381 @@ +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -362,6 +362,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-ipq8064-ap148.dtb \ + qcom-ipq8064-db149.dtb \ + qcom-ipq8064-r7500.dtb \ ++ qcom-ipq8064-d7800.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-sony-xperia-honami.dtb +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-d7800.dts +@@ -0,0 +1,368 @@ ++#include "qcom-ipq8064-v1.0.dtsi" ++ ++#include ++ ++/ { ++ 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 = &uart4; ++ mdio-gpio0 = &mdio0; ++ }; ++ ++ chosen { ++ bootargs = "rootfstype=squashfs noinitrd"; ++ linux,stdout-path = "serial0:115200n8"; ++ }; ++ ++ soc { ++ pinmux@800000 { ++ i2c4_pins: i2c4_pinmux { ++ pins = "gpio12", "gpio13"; ++ function = "gsbi4"; ++ bias-disable; ++ }; ++ ++ pcie0_pins: pcie0_pinmux { ++ mux { ++ pins = "gpio3"; ++ function = "pcie1_rst"; ++ drive-strength = <12>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie1_pins: pcie1_pinmux { ++ mux { ++ pins = "gpio48"; ++ function = "pcie2_rst"; ++ drive-strength = <12>; ++ 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; ++ }; ++ }; ++ ++ 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 = ; ++ 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"; ++ reset-gpio = <&qcom_pinmux 3 0>; ++ pinctrl-0 = <&pcie0_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 48 0>; ++ pinctrl-0 = <&pcie1_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ nand@1ac00000 { ++ status = "ok"; ++ ++ pinctrl-0 = <&nand_pins>; ++ pinctrl-names = "default"; ++ ++ nand-ecc-strength = <4>; ++ nand-bus-width = <8>; ++ ++ #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 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 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"; ++ 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; ++ }; ++ }; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ ++ wifi { ++ label = "wifi"; ++ gpios = <&qcom_pinmux 6 1>; ++ linux,code = ; ++ }; ++ ++ reset { ++ label = "reset"; ++ gpios = <&qcom_pinmux 54 1>; ++ linux,code = ; ++ }; ++ ++ wps { ++ label = "wps"; ++ gpios = <&qcom_pinmux 65 1>; ++ linux,code = ; ++ }; ++ }; ++ ++ gpio-leds { ++ compatible = "gpio-leds"; ++ ++ usb1 { ++ label = "d7800:amber:usb1"; ++ gpios = <&qcom_pinmux 7 0>; ++ }; ++ ++ usb3 { ++ label = "d7800:amber:usb3"; ++ gpios = <&qcom_pinmux 8 0>; ++ }; ++ ++ status { ++ label = "d7800:amber:status"; ++ gpios = <&qcom_pinmux 9 0>; ++ }; ++ ++ internet { ++ label = "d7800:white:internet"; ++ gpios = <&qcom_pinmux 22 0>; ++ }; ++ ++ wan { ++ label = "d7800:white:wan"; ++ gpios = <&qcom_pinmux 23 0>; ++ }; ++ ++ wps { ++ label = "d7800:white:wps"; ++ gpios = <&qcom_pinmux 24 0>; ++ }; ++ ++ esata { ++ label = "d7800:white:esata"; ++ gpios = <&qcom_pinmux 26 0>; ++ }; ++ ++ power { ++ label = "d7800:white:power"; ++ gpios = <&qcom_pinmux 53 0>; ++ default-state = "on"; ++ }; ++ ++ rfkill { ++ label = "d7800:white:rfkill"; ++ gpios = <&qcom_pinmux 64 0>; ++ }; ++ ++ wifi5g { ++ label = "d7800:white:wifi5g"; ++ gpios = <&qcom_pinmux 67 0>; ++ }; ++ }; ++}; ++ ++&adm_dma { ++ status = "ok"; ++}; diff --git a/target/linux/ipq806x/profiles/default.mk b/target/linux/ipq806x/profiles/default.mk deleted file mode 100644 index 6469bcb5f1..0000000000 --- a/target/linux/ipq806x/profiles/default.mk +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright (c) 2014 The Linux Foundation. All rights reserved. -# Copyright (C) 2009 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -define Profile/Default - NAME:=Default Profile (minimum package set) - PACKAGES:= \ - kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev \ - kmod-usb3 kmod-usb-dwc3-qcom kmod-usb-phy-qcom-dwc3 -endef - -define Profile/Default/Description - Default package set compatible with most boards. -endef -$(eval $(call Profile,Default)) diff --git a/target/linux/ipq806x/profiles/netgear.mk b/target/linux/ipq806x/profiles/netgear.mk deleted file mode 100644 index 25258cdca3..0000000000 --- a/target/linux/ipq806x/profiles/netgear.mk +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2014 The Linux Foundation. All rights reserved. -# Copyright (C) 2009 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -define Profile/D7800 - NAME:=Netgear Nighthawk X4 D7800 - PACKAGES:= \ - kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev \ - kmod-usb3 kmod-usb-dwc3-qcom kmod-usb-phy-qcom-dwc3 \ - kmod-ath10k wpad-mini ath10k-firmware-qca99x0 -endef - -define Profile/D7800/Description - Package set for the Netgear Nighthawk X4 D7800. -endef - -define Profile/R7500 - NAME:=Netgear Nighthawk X4 R7500 - PACKAGES:= \ - kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev \ - kmod-usb3 kmod-usb-dwc3-qcom kmod-usb-phy-qcom-dwc3 \ - kmod-ath10k wpad-mini -endef - -define Profile/R7500/Description - Package set for the Netgear Nighthawk X4 R7500. -endef -$(eval $(call Profile,D7800)) -$(eval $(call Profile,R7500))