ipq806x: add v4.9 support
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
parent
04c4b6f7fd
commit
bb255f7429
50 changed files with 11894 additions and 0 deletions
457
target/linux/ipq806x/config-4.9
Normal file
457
target/linux/ipq806x/config-4.9
Normal file
|
@ -0,0 +1,457 @@
|
|||
CONFIG_ALIGNMENT_TRAP=y
|
||||
# CONFIG_AMBA_PL08X is not set
|
||||
CONFIG_APQ_GCC_8084=y
|
||||
CONFIG_APQ_MMCC_8084=y
|
||||
CONFIG_AR8216_PHY=y
|
||||
CONFIG_ARCH_CLOCKSOURCE_DATA=y
|
||||
CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
|
||||
CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
|
||||
CONFIG_ARCH_HAS_SG_CHAIN=y
|
||||
CONFIG_ARCH_HAS_TICK_BROADCAST=y
|
||||
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
|
||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||
# CONFIG_ARCH_MDM9615 is not set
|
||||
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
|
||||
CONFIG_ARCH_MSM8960=y
|
||||
CONFIG_ARCH_MSM8974=y
|
||||
CONFIG_ARCH_MSM8X60=y
|
||||
CONFIG_ARCH_MULTIPLATFORM=y
|
||||
# CONFIG_ARCH_MULTI_CPU_AUTO is not set
|
||||
CONFIG_ARCH_MULTI_V6_V7=y
|
||||
CONFIG_ARCH_MULTI_V7=y
|
||||
CONFIG_ARCH_NR_GPIO=0
|
||||
CONFIG_ARCH_QCOM=y
|
||||
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
|
||||
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
|
||||
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
|
||||
CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y
|
||||
CONFIG_ARCH_SUPPORTS_UPROBES=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
|
||||
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
|
||||
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
|
||||
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
|
||||
CONFIG_ARM=y
|
||||
CONFIG_ARM_AMBA=y
|
||||
CONFIG_ARM_APPENDED_DTB=y
|
||||
CONFIG_ARM_ARCH_TIMER=y
|
||||
CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
|
||||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||
# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
|
||||
CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y
|
||||
# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE is not set
|
||||
CONFIG_ARM_CPU_SUSPEND=y
|
||||
# CONFIG_ARM_CPU_TOPOLOGY is not set
|
||||
CONFIG_ARM_GIC=y
|
||||
CONFIG_ARM_HAS_SG_CHAIN=y
|
||||
CONFIG_ARM_L1_CACHE_SHIFT=6
|
||||
CONFIG_ARM_L1_CACHE_SHIFT_6=y
|
||||
# CONFIG_ARM_LPAE is not set
|
||||
CONFIG_ARM_PATCH_IDIV=y
|
||||
CONFIG_ARM_PATCH_PHYS_VIRT=y
|
||||
# CONFIG_ARM_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_AUTO_ZRELADDR=y
|
||||
# CONFIG_B53 is not set
|
||||
# CONFIG_BINFMT_FLAT is not set
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_MQ_PCI=y
|
||||
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
|
||||
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
|
||||
CONFIG_BOUNCE=y
|
||||
# CONFIG_CACHE_L2X0 is not set
|
||||
CONFIG_CLKDEV_LOOKUP=y
|
||||
CONFIG_CLKSRC_OF=y
|
||||
CONFIG_CLKSRC_PROBE=y
|
||||
CONFIG_CLKSRC_QCOM=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_COMMON_CLK_QCOM=y
|
||||
CONFIG_CPUFREQ_DT=y
|
||||
CONFIG_CPUFREQ_DT_PLATDEV=y
|
||||
CONFIG_CPU_32v6K=y
|
||||
CONFIG_CPU_32v7=y
|
||||
CONFIG_CPU_ABRT_EV7=y
|
||||
# CONFIG_CPU_BIG_ENDIAN is not set
|
||||
# CONFIG_CPU_BPREDICT_DISABLE is not set
|
||||
CONFIG_CPU_CACHE_V7=y
|
||||
CONFIG_CPU_CACHE_VIPT=y
|
||||
CONFIG_CPU_COPY_V6=y
|
||||
CONFIG_CPU_CP15=y
|
||||
CONFIG_CPU_CP15_MMU=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
|
||||
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
|
||||
CONFIG_CPU_FREQ_GOV_COMMON=y
|
||||
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
|
||||
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
|
||||
# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_HAS_ASID=y
|
||||
# CONFIG_CPU_ICACHE_DISABLE is not set
|
||||
CONFIG_CPU_PABRT_V7=y
|
||||
CONFIG_CPU_RMAP=y
|
||||
CONFIG_CPU_THERMAL=y
|
||||
CONFIG_CPU_TLB_V7=y
|
||||
CONFIG_CPU_V7=y
|
||||
CONFIG_CRC16=y
|
||||
# CONFIG_CRC32_SARWATE is not set
|
||||
CONFIG_CRC32_SLICEBY8=y
|
||||
CONFIG_CRYPTO_AEAD=y
|
||||
CONFIG_CRYPTO_AEAD2=y
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_HASH2=y
|
||||
CONFIG_CRYPTO_LZO=y
|
||||
CONFIG_CRYPTO_MANAGER=y
|
||||
CONFIG_CRYPTO_MANAGER2=y
|
||||
CONFIG_CRYPTO_NULL2=y
|
||||
CONFIG_CRYPTO_RNG2=y
|
||||
CONFIG_CRYPTO_WORKQUEUE=y
|
||||
CONFIG_DCACHE_WORD_ACCESS=y
|
||||
CONFIG_DEBUG_GPIO=y
|
||||
CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
|
||||
# CONFIG_DEBUG_UART_8250 is not set
|
||||
# CONFIG_DEBUG_USER is not set
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_DMA_ENGINE=y
|
||||
CONFIG_DMA_OF=y
|
||||
CONFIG_DMA_VIRTUAL_CHANNELS=y
|
||||
CONFIG_DTC=y
|
||||
# CONFIG_DWMAC_GENERIC is not set
|
||||
CONFIG_DWMAC_IPQ806X=y
|
||||
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_GENERIC_ALLOCATOR=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
|
||||
CONFIG_GENERIC_EARLY_IOREMAP=y
|
||||
CONFIG_GENERIC_IDLE_POLL_SETUP=y
|
||||
CONFIG_GENERIC_IO=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
|
||||
CONFIG_GENERIC_MSI_IRQ=y
|
||||
CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_PHY=y
|
||||
CONFIG_GENERIC_PINCONF=y
|
||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GENERIC_STRNCPY_FROM_USER=y
|
||||
CONFIG_GENERIC_STRNLEN_USER=y
|
||||
CONFIG_GENERIC_TIME_VSYSCALL=y
|
||||
CONFIG_GPIOLIB=y
|
||||
CONFIG_GPIOLIB_IRQCHIP=y
|
||||
CONFIG_GPIO_SYSFS=y
|
||||
CONFIG_HANDLE_DOMAIN_IRQ=y
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
|
||||
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
|
||||
CONFIG_HAVE_ARCH_BITREVERSE=y
|
||||
CONFIG_HAVE_ARCH_JUMP_LABEL=y
|
||||
CONFIG_HAVE_ARCH_KGDB=y
|
||||
CONFIG_HAVE_ARCH_PFN_VALID=y
|
||||
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
|
||||
CONFIG_HAVE_ARCH_TRACEHOOK=y
|
||||
CONFIG_HAVE_ARM_ARCH_TIMER=y
|
||||
CONFIG_HAVE_ARM_SMCCC=y
|
||||
# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
|
||||
CONFIG_HAVE_CBPF_JIT=y
|
||||
CONFIG_HAVE_CC_STACKPROTECTOR=y
|
||||
CONFIG_HAVE_CLK=y
|
||||
CONFIG_HAVE_CLK_PREPARE=y
|
||||
CONFIG_HAVE_CONTEXT_TRACKING=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_DEBUG_KMEMLEAK=y
|
||||
CONFIG_HAVE_DMA_API_DEBUG=y
|
||||
CONFIG_HAVE_DMA_CONTIGUOUS=y
|
||||
CONFIG_HAVE_DYNAMIC_FTRACE=y
|
||||
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
|
||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
||||
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
||||
CONFIG_HAVE_IDE=y
|
||||
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
|
||||
CONFIG_HAVE_MEMBLOCK=y
|
||||
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
|
||||
CONFIG_HAVE_NET_DSA=y
|
||||
CONFIG_HAVE_OPROFILE=y
|
||||
CONFIG_HAVE_OPTPROBES=y
|
||||
CONFIG_HAVE_PERF_EVENTS=y
|
||||
CONFIG_HAVE_PERF_REGS=y
|
||||
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
|
||||
CONFIG_HAVE_PROC_CPU=y
|
||||
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
|
||||
CONFIG_HAVE_SMP=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_UID16=y
|
||||
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
|
||||
CONFIG_HIGHMEM=y
|
||||
# CONFIG_HIGHPTE is not set
|
||||
CONFIG_HWMON=y
|
||||
CONFIG_HWSPINLOCK=y
|
||||
CONFIG_HWSPINLOCK_QCOM=y
|
||||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HW_RANDOM_MSM=y
|
||||
CONFIG_HZ_FIXED=0
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_BOARDINFO=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_HELPER_AUTO=y
|
||||
CONFIG_I2C_QUP=y
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_IOMMU_HELPER=y
|
||||
# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set
|
||||
# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set
|
||||
CONFIG_IOMMU_SUPPORT=y
|
||||
CONFIG_IPQ_GCC_4019=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_LIBFDT=y
|
||||
CONFIG_LOCKUP_DETECTOR=y
|
||||
CONFIG_LOCK_SPIN_ON_OWNER=y
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
CONFIG_MDIO_BITBANG=y
|
||||
CONFIG_MDIO_BOARDINFO=y
|
||||
CONFIG_MDIO_GPIO=y
|
||||
# CONFIG_MDM_GCC_9615 is not set
|
||||
# CONFIG_MDM_LCC_9615 is not set
|
||||
# CONFIG_MFD_MAX77620 is not set
|
||||
CONFIG_MFD_QCOM_RPM=y
|
||||
# CONFIG_MFD_SPMI_PMIC is not set
|
||||
CONFIG_MFD_SYSCON=y
|
||||
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
|
||||
CONFIG_MIGHT_HAVE_PCI=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_ARMMMCI=y
|
||||
CONFIG_MMC_BLOCK=y
|
||||
CONFIG_MMC_BLOCK_MINORS=16
|
||||
CONFIG_MMC_QCOM_DML=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_MSM=y
|
||||
# CONFIG_MMC_SDHCI_PCI is not set
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
# CONFIG_MMC_TIFM_SD is not set
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
CONFIG_MSM_GCC_8660=y
|
||||
# CONFIG_MSM_GCC_8916 is not set
|
||||
CONFIG_MSM_GCC_8960=y
|
||||
CONFIG_MSM_GCC_8974=y
|
||||
# CONFIG_MSM_GCC_8996 is not set
|
||||
# CONFIG_MSM_IOMMU is not set
|
||||
# CONFIG_MSM_LCC_8960 is not set
|
||||
CONFIG_MSM_MMCC_8960=y
|
||||
CONFIG_MSM_MMCC_8974=y
|
||||
# CONFIG_MSM_MMCC_8996 is not set
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_M25P80=y
|
||||
CONFIG_MTD_NAND=y
|
||||
CONFIG_MTD_NAND_ECC=y
|
||||
CONFIG_MTD_NAND_QCOM=y
|
||||
# CONFIG_MTD_PHYSMAP_OF_VERSATILE is not set
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_FIRMWARE=y
|
||||
CONFIG_MTD_SPLIT_FIT_FW=y
|
||||
CONFIG_MTD_UBI=y
|
||||
CONFIG_MTD_UBI_BEB_LIMIT=20
|
||||
CONFIG_MTD_UBI_BLOCK=y
|
||||
# CONFIG_MTD_UBI_FASTMAP is not set
|
||||
# CONFIG_MTD_UBI_GLUEBI is not set
|
||||
CONFIG_MTD_UBI_WL_THRESHOLD=4096
|
||||
CONFIG_MULTI_IRQ_HANDLER=y
|
||||
CONFIG_MUTEX_SPIN_ON_OWNER=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NEON=y
|
||||
CONFIG_NET_DSA=y
|
||||
CONFIG_NET_DSA_HWMON=y
|
||||
CONFIG_NET_DSA_QCA8K=y
|
||||
CONFIG_NET_DSA_TAG_QCA=y
|
||||
CONFIG_NET_FLOW_LIMIT=y
|
||||
CONFIG_NET_PTP_CLASSIFY=y
|
||||
CONFIG_NET_SWITCHDEV=y
|
||||
CONFIG_NLS=y
|
||||
CONFIG_NO_BOOTMEM=y
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_NO_HZ_COMMON=y
|
||||
CONFIG_NO_HZ_IDLE=y
|
||||
CONFIG_NR_CPUS=4
|
||||
CONFIG_NVMEM=y
|
||||
CONFIG_OF=y
|
||||
CONFIG_OF_ADDRESS=y
|
||||
CONFIG_OF_ADDRESS_PCI=y
|
||||
CONFIG_OF_EARLY_FLATTREE=y
|
||||
CONFIG_OF_FLATTREE=y
|
||||
CONFIG_OF_GPIO=y
|
||||
CONFIG_OF_IRQ=y
|
||||
CONFIG_OF_MDIO=y
|
||||
CONFIG_OF_NET=y
|
||||
CONFIG_OF_PCI=y
|
||||
CONFIG_OF_PCI_IRQ=y
|
||||
CONFIG_OF_RESERVED_MEM=y
|
||||
CONFIG_OLD_SIGACTION=y
|
||||
CONFIG_OLD_SIGSUSPEND3=y
|
||||
CONFIG_PADATA=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
CONFIG_PCI=y
|
||||
CONFIG_PCIEAER=y
|
||||
CONFIG_PCIEPORTBUS=y
|
||||
CONFIG_PCIE_DW=y
|
||||
CONFIG_PCIE_QCOM=y
|
||||
CONFIG_PCI_DEBUG=y
|
||||
CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
|
||||
CONFIG_PCI_DOMAINS=y
|
||||
CONFIG_PCI_DOMAINS_GENERIC=y
|
||||
CONFIG_PCI_MSI=y
|
||||
CONFIG_PCI_MSI_IRQ_DOMAIN=y
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PGTABLE_LEVELS=2
|
||||
CONFIG_PHYLIB=y
|
||||
# CONFIG_PHY_QCOM_APQ8064_SATA is not set
|
||||
CONFIG_PHY_QCOM_IPQ806X_SATA=y
|
||||
# CONFIG_PHY_QCOM_UFS is not set
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_PINCTRL_APQ8064=y
|
||||
# CONFIG_PINCTRL_APQ8084 is not set
|
||||
CONFIG_PINCTRL_IPQ4019=y
|
||||
CONFIG_PINCTRL_IPQ8064=y
|
||||
# CONFIG_PINCTRL_MDM9615 is not set
|
||||
CONFIG_PINCTRL_MSM=y
|
||||
# CONFIG_PINCTRL_MSM8660 is not set
|
||||
# CONFIG_PINCTRL_MSM8916 is not set
|
||||
# CONFIG_PINCTRL_MSM8960 is not set
|
||||
# CONFIG_PINCTRL_MSM8996 is not set
|
||||
CONFIG_PINCTRL_MSM8X74=y
|
||||
# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set
|
||||
# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set
|
||||
# CONFIG_PL330_DMA is not set
|
||||
CONFIG_PM_OPP=y
|
||||
CONFIG_POWER_RESET=y
|
||||
# CONFIG_POWER_RESET_BRCMKONA is not set
|
||||
CONFIG_POWER_RESET_MSM=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_PPS=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_PTP_1588_CLOCK=y
|
||||
CONFIG_QCOM_ADM=y
|
||||
CONFIG_QCOM_BAM_DMA=y
|
||||
CONFIG_QCOM_CLK_RPM=y
|
||||
# CONFIG_QCOM_EBI2 is not set
|
||||
CONFIG_QCOM_GDSC=y
|
||||
CONFIG_QCOM_GSBI=y
|
||||
CONFIG_QCOM_PM=y
|
||||
# CONFIG_QCOM_Q6V5_PIL is not set
|
||||
CONFIG_QCOM_QFPROM=y
|
||||
CONFIG_QCOM_RPMCC=y
|
||||
CONFIG_QCOM_SCM=y
|
||||
CONFIG_QCOM_SCM_32=y
|
||||
# CONFIG_QCOM_SMD is not set
|
||||
CONFIG_QCOM_SMEM=y
|
||||
# CONFIG_QCOM_SMP2P is not set
|
||||
# CONFIG_QCOM_SMSM is not set
|
||||
CONFIG_QCOM_TSENS=y
|
||||
# CONFIG_QCOM_WCNSS_PIL is not set
|
||||
CONFIG_QCOM_WDT=y
|
||||
# CONFIG_QRTR is not set
|
||||
CONFIG_RAS=y
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_RCU_CPU_STALL_TIMEOUT=21
|
||||
CONFIG_RCU_STALL_COMMON=y
|
||||
CONFIG_REGMAP=y
|
||||
CONFIG_REGMAP_I2C=y
|
||||
CONFIG_REGMAP_MMIO=y
|
||||
CONFIG_REGMAP_SPI=y
|
||||
CONFIG_REGULATOR=y
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_REGULATOR_QCOM_RPM=y
|
||||
# CONFIG_REGULATOR_QCOM_SPMI is not set
|
||||
CONFIG_RESET_CONTROLLER=y
|
||||
CONFIG_RFS_ACCEL=y
|
||||
# CONFIG_RPMSG_QCOM_SMD is not set
|
||||
CONFIG_RPS=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
# CONFIG_RTC_DRV_CMOS is not set
|
||||
CONFIG_RTC_I2C_AND_SPI=y
|
||||
CONFIG_RWSEM_SPIN_ON_OWNER=y
|
||||
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
|
||||
CONFIG_SCHED_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_SG_POOL is not set
|
||||
CONFIG_SMP=y
|
||||
CONFIG_SMP_ON_UP=y
|
||||
CONFIG_SPARSE_IRQ=y
|
||||
CONFIG_SPI=y
|
||||
# CONFIG_SPI_CADENCE_QUADSPI is not set
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_QUP=y
|
||||
CONFIG_SPMI=y
|
||||
CONFIG_SPMI_MSM_PMIC_ARB=y
|
||||
CONFIG_SRCU=y
|
||||
CONFIG_STMMAC_ETH=y
|
||||
CONFIG_STMMAC_PLATFORM=y
|
||||
CONFIG_SWCONFIG=y
|
||||
CONFIG_SWCONFIG_LEDS=y
|
||||
CONFIG_SWIOTLB=y
|
||||
CONFIG_SWPHY=y
|
||||
CONFIG_SWP_EMULATE=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
CONFIG_THERMAL=y
|
||||
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
|
||||
CONFIG_THERMAL_GOV_STEP_WISE=y
|
||||
CONFIG_THERMAL_HWMON=y
|
||||
CONFIG_THERMAL_OF=y
|
||||
# CONFIG_THUMB2_KERNEL is not set
|
||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
CONFIG_TREE_RCU=y
|
||||
CONFIG_UBIFS_FS=y
|
||||
CONFIG_UBIFS_FS_ADVANCED_COMPR=y
|
||||
CONFIG_UBIFS_FS_LZO=y
|
||||
CONFIG_UBIFS_FS_ZLIB=y
|
||||
CONFIG_UEVENT_HELPER_PATH=""
|
||||
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_COMMON=y
|
||||
# CONFIG_USB_EHCI_HCD is not set
|
||||
# CONFIG_USB_IPQ4019_PHY is not set
|
||||
CONFIG_USB_SUPPORT=y
|
||||
# CONFIG_USB_UHCI_HCD is not set
|
||||
CONFIG_USE_OF=y
|
||||
CONFIG_VDSO=y
|
||||
CONFIG_VECTORS_BASE=0xffff0000
|
||||
CONFIG_VFP=y
|
||||
CONFIG_VFPv3=y
|
||||
CONFIG_WATCHDOG_CORE=y
|
||||
CONFIG_XPS=y
|
||||
CONFIG_XZ_DEC_ARM=y
|
||||
CONFIG_XZ_DEC_BCJ=y
|
||||
CONFIG_ZBOOT_ROM_BSS=0
|
||||
CONFIG_ZBOOT_ROM_TEXT=0
|
||||
CONFIG_ZLIB_DEFLATE=y
|
||||
CONFIG_ZLIB_INFLATE=y
|
|
@ -0,0 +1,71 @@
|
|||
From f275ae09b82aa423d2ea5a2be3ea315c8fcf6143 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Mon, 16 May 2016 17:58:50 -0700
|
||||
Subject: [PATCH 01/37] dtbindings: qcom_adm: Fix channel specifiers
|
||||
|
||||
Original patch from Andy Gross.
|
||||
|
||||
This patch removes the crci information from the dma
|
||||
channel property. At least one client device requires
|
||||
using more than one CRCI value for a channel. This does
|
||||
not match the current binding and the crci information
|
||||
needs to be removed.
|
||||
|
||||
Instead, the client device will provide this information
|
||||
via other means.
|
||||
|
||||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
---
|
||||
Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++----------
|
||||
1 file changed, 6 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt
|
||||
+++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt
|
||||
@@ -4,8 +4,7 @@ Required properties:
|
||||
- compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960
|
||||
- reg: Address range for DMA registers
|
||||
- interrupts: Should contain one interrupt shared by all channels
|
||||
-- #dma-cells: must be <2>. First cell denotes the channel number. Second cell
|
||||
- denotes CRCI (client rate control interface) flow control assignment.
|
||||
+- #dma-cells: must be <1>. First cell denotes the channel number.
|
||||
- clocks: Should contain the core clock and interface clock.
|
||||
- clock-names: Must contain "core" for the core clock and "iface" for the
|
||||
interface clock.
|
||||
@@ -22,7 +21,7 @@ Example:
|
||||
compatible = "qcom,adm";
|
||||
reg = <0x18300000 0x100000>;
|
||||
interrupts = <0 170 0>;
|
||||
- #dma-cells = <2>;
|
||||
+ #dma-cells = <1>;
|
||||
|
||||
clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
@@ -35,15 +34,12 @@ Example:
|
||||
qcom,ee = <0>;
|
||||
};
|
||||
|
||||
-DMA clients must use the format descripted in the dma.txt file, using a three
|
||||
+DMA clients must use the format descripted in the dma.txt file, using a two
|
||||
cell specifier for each channel.
|
||||
|
||||
-Each dmas request consists of 3 cells:
|
||||
+Each dmas request consists of two cells:
|
||||
1. phandle pointing to the DMA controller
|
||||
2. channel number
|
||||
- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0.
|
||||
- The CRCI is used for flow control. It identifies the peripheral device that
|
||||
- is the source/destination for the transferred data.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -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";
|
||||
};
|
|
@ -0,0 +1,952 @@
|
|||
From 1d32bf93c8e83db0aca04d2961badef7e86d663b Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Mon, 16 May 2016 17:58:51 -0700
|
||||
Subject: [PATCH 02/37] dmaengine: Add ADM driver
|
||||
|
||||
Original patch by Andy Gross.
|
||||
|
||||
Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA
|
||||
controller found in the MSM8x60 and IPQ/APQ8064 platforms.
|
||||
|
||||
The ADM supports both memory to memory transactions and memory
|
||||
to/from peripheral device transactions. The controller also provides flow
|
||||
control capabilities for transactions to/from peripheral devices.
|
||||
|
||||
The initial release of this driver supports slave transfers to/from peripherals
|
||||
and also incorporates CRCI (client rate control interface) flow control.
|
||||
|
||||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
---
|
||||
drivers/dma/qcom/Kconfig | 10 +
|
||||
drivers/dma/qcom/Makefile | 1 +
|
||||
drivers/dma/qcom/qcom_adm.c | 900 +++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 911 insertions(+)
|
||||
create mode 100644 drivers/dma/qcom/qcom_adm.c
|
||||
|
||||
--- a/drivers/dma/qcom/Kconfig
|
||||
+++ b/drivers/dma/qcom/Kconfig
|
||||
@@ -27,3 +27,13 @@ config QCOM_HIDMA
|
||||
(user to kernel, kernel to kernel, etc.). It only supports
|
||||
memcpy interface. The core is not intended for general
|
||||
purpose slave DMA.
|
||||
+
|
||||
+config QCOM_ADM
|
||||
+ tristate "Qualcomm ADM support"
|
||||
+ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
|
||||
+ select DMA_ENGINE
|
||||
+ select DMA_VIRTUAL_CHANNELS
|
||||
+ ---help---
|
||||
+ Enable support for the Qualcomm ADM DMA controller. This controller
|
||||
+ provides DMA capabilities for both general purpose and on-chip
|
||||
+ peripheral devices.
|
||||
--- a/drivers/dma/qcom/Makefile
|
||||
+++ b/drivers/dma/qcom/Makefile
|
||||
@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg
|
||||
hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o
|
||||
obj-$(CONFIG_QCOM_HIDMA) += hdma.o
|
||||
hdma-objs := hidma_ll.o hidma.o hidma_dbg.o
|
||||
+obj-$(CONFIG_QCOM_ADM) += qcom_adm.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/dma/qcom/qcom_adm.c
|
||||
@@ -0,0 +1,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 <linux/kernel.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
+#include <linux/scatterlist.h>
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/of_dma.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/dmaengine.h>
|
||||
+
|
||||
+#include "../dmaengine.h"
|
||||
+#include "../virt-dma.h"
|
||||
+
|
||||
+/* ADM registers - calculated from channel number and security domain */
|
||||
+#define ADM_CHAN_MULTI 0x4
|
||||
+#define ADM_CI_MULTI 0x4
|
||||
+#define ADM_CRCI_MULTI 0x4
|
||||
+#define ADM_EE_MULTI 0x800
|
||||
+#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
|
||||
+#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee)
|
||||
+#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee))
|
||||
+#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan)
|
||||
+#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci))
|
||||
+#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee))
|
||||
+#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee))
|
||||
+#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee))
|
||||
+#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee))
|
||||
+#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan))
|
||||
+#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee))
|
||||
+#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee))
|
||||
+#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI)
|
||||
+#define ADM_GP_CTL 0x3d8
|
||||
+#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \
|
||||
+ ADM_EE_OFFS(ee))
|
||||
+
|
||||
+/* channel status */
|
||||
+#define ADM_CH_STATUS_VALID BIT(1)
|
||||
+
|
||||
+/* channel result */
|
||||
+#define ADM_CH_RSLT_VALID BIT(31)
|
||||
+#define ADM_CH_RSLT_ERR BIT(3)
|
||||
+#define ADM_CH_RSLT_FLUSH BIT(2)
|
||||
+#define ADM_CH_RSLT_TPD BIT(1)
|
||||
+
|
||||
+/* channel conf */
|
||||
+#define ADM_CH_CONF_SHADOW_EN BIT(12)
|
||||
+#define ADM_CH_CONF_MPU_DISABLE BIT(11)
|
||||
+#define ADM_CH_CONF_PERM_MPU_CONF BIT(9)
|
||||
+#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7)
|
||||
+#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11))
|
||||
+
|
||||
+/* channel result conf */
|
||||
+#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1)
|
||||
+#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0)
|
||||
+
|
||||
+/* CRCI CTL */
|
||||
+#define ADM_CRCI_CTL_MUX_SEL BIT(18)
|
||||
+#define ADM_CRCI_CTL_RST BIT(17)
|
||||
+
|
||||
+/* CI configuration */
|
||||
+#define ADM_CI_RANGE_END(x) (x << 24)
|
||||
+#define ADM_CI_RANGE_START(x) (x << 16)
|
||||
+#define ADM_CI_BURST_4_WORDS BIT(2)
|
||||
+#define ADM_CI_BURST_8_WORDS BIT(3)
|
||||
+
|
||||
+/* GP CTL */
|
||||
+#define ADM_GP_CTL_LP_EN BIT(12)
|
||||
+#define ADM_GP_CTL_LP_CNT(x) (x << 8)
|
||||
+
|
||||
+/* Command pointer list entry */
|
||||
+#define ADM_CPLE_LP BIT(31)
|
||||
+#define ADM_CPLE_CMD_PTR_LIST BIT(29)
|
||||
+
|
||||
+/* Command list entry */
|
||||
+#define ADM_CMD_LC BIT(31)
|
||||
+#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7)
|
||||
+#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3)
|
||||
+
|
||||
+#define ADM_CMD_TYPE_SINGLE 0x0
|
||||
+#define ADM_CMD_TYPE_BOX 0x3
|
||||
+
|
||||
+#define ADM_CRCI_MUX_SEL BIT(4)
|
||||
+#define ADM_DESC_ALIGN 8
|
||||
+#define ADM_MAX_XFER (SZ_64K-1)
|
||||
+#define ADM_MAX_ROWS (SZ_64K-1)
|
||||
+#define ADM_MAX_CHANNELS 16
|
||||
+
|
||||
+struct adm_desc_hw_box {
|
||||
+ u32 cmd;
|
||||
+ u32 src_addr;
|
||||
+ u32 dst_addr;
|
||||
+ u32 row_len;
|
||||
+ u32 num_rows;
|
||||
+ u32 row_offset;
|
||||
+};
|
||||
+
|
||||
+struct adm_desc_hw_single {
|
||||
+ u32 cmd;
|
||||
+ u32 src_addr;
|
||||
+ u32 dst_addr;
|
||||
+ u32 len;
|
||||
+};
|
||||
+
|
||||
+struct adm_async_desc {
|
||||
+ struct virt_dma_desc vd;
|
||||
+ struct adm_device *adev;
|
||||
+
|
||||
+ size_t length;
|
||||
+ enum dma_transfer_direction dir;
|
||||
+ dma_addr_t dma_addr;
|
||||
+ size_t dma_len;
|
||||
+
|
||||
+ void *cpl;
|
||||
+ dma_addr_t cp_addr;
|
||||
+ u32 crci;
|
||||
+ u32 mux;
|
||||
+ u32 blk_size;
|
||||
+};
|
||||
+
|
||||
+struct adm_chan {
|
||||
+ struct virt_dma_chan vc;
|
||||
+ struct adm_device *adev;
|
||||
+
|
||||
+ /* parsed from DT */
|
||||
+ u32 id; /* channel id */
|
||||
+
|
||||
+ struct adm_async_desc *curr_txd;
|
||||
+ struct dma_slave_config slave;
|
||||
+ struct list_head node;
|
||||
+
|
||||
+ int error;
|
||||
+ int initialized;
|
||||
+};
|
||||
+
|
||||
+static inline struct adm_chan *to_adm_chan(struct dma_chan *common)
|
||||
+{
|
||||
+ return container_of(common, struct adm_chan, vc.chan);
|
||||
+}
|
||||
+
|
||||
+struct adm_device {
|
||||
+ void __iomem *regs;
|
||||
+ struct device *dev;
|
||||
+ struct dma_device common;
|
||||
+ struct device_dma_parameters dma_parms;
|
||||
+ struct adm_chan *channels;
|
||||
+
|
||||
+ u32 ee;
|
||||
+
|
||||
+ struct clk *core_clk;
|
||||
+ struct clk *iface_clk;
|
||||
+
|
||||
+ struct reset_control *clk_reset;
|
||||
+ struct reset_control *c0_reset;
|
||||
+ struct reset_control *c1_reset;
|
||||
+ struct reset_control *c2_reset;
|
||||
+ int irq;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * adm_free_chan - Frees dma resources associated with the specific channel
|
||||
+ *
|
||||
+ * Free all allocated descriptors associated with this channel
|
||||
+ *
|
||||
+ */
|
||||
+static void adm_free_chan(struct dma_chan *chan)
|
||||
+{
|
||||
+ /* free all queued descriptors */
|
||||
+ vchan_free_chan_resources(to_virt_chan(chan));
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * adm_get_blksize - Get block size from burst value
|
||||
+ *
|
||||
+ */
|
||||
+static int adm_get_blksize(unsigned int burst)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ switch (burst) {
|
||||
+ case 16:
|
||||
+ case 32:
|
||||
+ case 64:
|
||||
+ case 128:
|
||||
+ ret = ffs(burst>>4) - 1;
|
||||
+ break;
|
||||
+ case 192:
|
||||
+ ret = 4;
|
||||
+ break;
|
||||
+ case 256:
|
||||
+ ret = 5;
|
||||
+ break;
|
||||
+ default:
|
||||
+ ret = -EINVAL;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers
|
||||
+ *
|
||||
+ * @achan: ADM channel
|
||||
+ * @desc: Descriptor memory pointer
|
||||
+ * @sg: Scatterlist entry
|
||||
+ * @crci: CRCI value
|
||||
+ * @burst: Burst size of transaction
|
||||
+ * @direction: DMA transfer direction
|
||||
+ */
|
||||
+static void *adm_process_fc_descriptors(struct adm_chan *achan,
|
||||
+ void *desc, struct scatterlist *sg, u32 crci, u32 burst,
|
||||
+ enum dma_transfer_direction direction)
|
||||
+{
|
||||
+ struct adm_desc_hw_box *box_desc = NULL;
|
||||
+ struct adm_desc_hw_single *single_desc;
|
||||
+ u32 remainder = sg_dma_len(sg);
|
||||
+ u32 rows, row_offset, crci_cmd;
|
||||
+ u32 mem_addr = sg_dma_address(sg);
|
||||
+ u32 *incr_addr = &mem_addr;
|
||||
+ u32 *src, *dst;
|
||||
+
|
||||
+ if (direction == DMA_DEV_TO_MEM) {
|
||||
+ crci_cmd = ADM_CMD_SRC_CRCI(crci);
|
||||
+ row_offset = burst;
|
||||
+ src = &achan->slave.src_addr;
|
||||
+ dst = &mem_addr;
|
||||
+ } else {
|
||||
+ crci_cmd = ADM_CMD_DST_CRCI(crci);
|
||||
+ row_offset = burst << 16;
|
||||
+ src = &mem_addr;
|
||||
+ dst = &achan->slave.dst_addr;
|
||||
+ }
|
||||
+
|
||||
+ while (remainder >= burst) {
|
||||
+ box_desc = desc;
|
||||
+ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd;
|
||||
+ box_desc->row_offset = row_offset;
|
||||
+ box_desc->src_addr = *src;
|
||||
+ box_desc->dst_addr = *dst;
|
||||
+
|
||||
+ rows = remainder / burst;
|
||||
+ rows = min_t(u32, rows, ADM_MAX_ROWS);
|
||||
+ box_desc->num_rows = rows << 16 | rows;
|
||||
+ box_desc->row_len = burst << 16 | burst;
|
||||
+
|
||||
+ *incr_addr += burst * rows;
|
||||
+ remainder -= burst * rows;
|
||||
+ desc += sizeof(*box_desc);
|
||||
+ }
|
||||
+
|
||||
+ /* if leftover bytes, do one single descriptor */
|
||||
+ if (remainder) {
|
||||
+ single_desc = desc;
|
||||
+ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd;
|
||||
+ single_desc->len = remainder;
|
||||
+ single_desc->src_addr = *src;
|
||||
+ single_desc->dst_addr = *dst;
|
||||
+ desc += sizeof(*single_desc);
|
||||
+
|
||||
+ if (sg_is_last(sg))
|
||||
+ single_desc->cmd |= ADM_CMD_LC;
|
||||
+ } else {
|
||||
+ if (box_desc && sg_is_last(sg))
|
||||
+ box_desc->cmd |= ADM_CMD_LC;
|
||||
+ }
|
||||
+
|
||||
+ return desc;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers
|
||||
+ *
|
||||
+ * @achan: ADM channel
|
||||
+ * @desc: Descriptor memory pointer
|
||||
+ * @sg: Scatterlist entry
|
||||
+ * @direction: DMA transfer direction
|
||||
+ */
|
||||
+static void *adm_process_non_fc_descriptors(struct adm_chan *achan,
|
||||
+ void *desc, struct scatterlist *sg,
|
||||
+ enum dma_transfer_direction direction)
|
||||
+{
|
||||
+ struct adm_desc_hw_single *single_desc;
|
||||
+ u32 remainder = sg_dma_len(sg);
|
||||
+ u32 mem_addr = sg_dma_address(sg);
|
||||
+ u32 *incr_addr = &mem_addr;
|
||||
+ u32 *src, *dst;
|
||||
+
|
||||
+ if (direction == DMA_DEV_TO_MEM) {
|
||||
+ src = &achan->slave.src_addr;
|
||||
+ dst = &mem_addr;
|
||||
+ } else {
|
||||
+ src = &mem_addr;
|
||||
+ dst = &achan->slave.dst_addr;
|
||||
+ }
|
||||
+
|
||||
+ do {
|
||||
+ single_desc = desc;
|
||||
+ single_desc->cmd = ADM_CMD_TYPE_SINGLE;
|
||||
+ single_desc->src_addr = *src;
|
||||
+ single_desc->dst_addr = *dst;
|
||||
+ single_desc->len = (remainder > ADM_MAX_XFER) ?
|
||||
+ ADM_MAX_XFER : remainder;
|
||||
+
|
||||
+ remainder -= single_desc->len;
|
||||
+ *incr_addr += single_desc->len;
|
||||
+ desc += sizeof(*single_desc);
|
||||
+ } while (remainder);
|
||||
+
|
||||
+ /* set last command if this is the end of the whole transaction */
|
||||
+ if (sg_is_last(sg))
|
||||
+ single_desc->cmd |= ADM_CMD_LC;
|
||||
+
|
||||
+ return desc;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * adm_prep_slave_sg - Prep slave sg transaction
|
||||
+ *
|
||||
+ * @chan: dma channel
|
||||
+ * @sgl: scatter gather list
|
||||
+ * @sg_len: length of sg
|
||||
+ * @direction: DMA transfer direction
|
||||
+ * @flags: DMA flags
|
||||
+ * @context: transfer context (unused)
|
||||
+ */
|
||||
+static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
|
||||
+ struct scatterlist *sgl, unsigned int sg_len,
|
||||
+ enum dma_transfer_direction direction, unsigned long flags,
|
||||
+ void *context)
|
||||
+{
|
||||
+ struct adm_chan *achan = to_adm_chan(chan);
|
||||
+ struct adm_device *adev = achan->adev;
|
||||
+ struct adm_async_desc *async_desc;
|
||||
+ struct scatterlist *sg;
|
||||
+ 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 <agross@codeaurora.org>");
|
||||
+MODULE_DESCRIPTION("QCOM ADM DMA engine driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,52 @@
|
|||
From eb694e964310ba402d6ff99f08e0ef78345e7397 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Mon, 16 May 2016 17:58:52 -0700
|
||||
Subject: [PATCH 03/37] arm: qcom: dts: ipq8064: Add ADM device node
|
||||
|
||||
Original patch by Andy Gross.
|
||||
|
||||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq8064.dtsi | 21 +++++++++++++++++++++
|
||||
1 file changed, 21 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
@@ -1,9 +1,11 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include "skeleton.dtsi"
|
||||
+#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
|
||||
#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
|
||||
#include <dt-bindings/clock/qcom,lcc-ipq806x.h>
|
||||
#include <dt-bindings/soc/qcom,gsbi.h>
|
||||
+#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
/ {
|
||||
model = "Qualcomm IPQ8064";
|
||||
@@ -342,5 +344,24 @@
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
+ adm_dma: dma@18300000 {
|
||||
+ compatible = "qcom,adm";
|
||||
+ reg = <0x18300000 0x100000>;
|
||||
+ interrupts = <GIC_SPI 170 IRQ_TYPE_NONE>;
|
||||
+ #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";
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
From c6d45af259eb4fb2a598c0396c6dd580b1658558 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Mon, 16 May 2016 17:58:53 -0700
|
||||
Subject: [PATCH 04/37] arm: qcom: dts: Add NAND controller node for ipq806x
|
||||
|
||||
Original patch by Archit Taneja.
|
||||
|
||||
Signed-off-by: Archit Taneja <architt@codeaurora.org>
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq8064.dtsi | 17 +++++++++++++++++
|
||||
1 file changed, 17 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
||||
@@ -363,5 +363,22 @@
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
+
|
||||
+ nand@1ac00000 {
|
||||
+ compatible = "qcom,ipq806x-nand";
|
||||
+ 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";
|
||||
+ };
|
||||
+
|
||||
};
|
||||
};
|
|
@ -0,0 +1,74 @@
|
|||
From f8d0939eca47f56449ec583810a6ff41a1caaa91 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Mon, 16 May 2016 17:58:54 -0700
|
||||
Subject: [PATCH 05/37] arm: qcom: dts: Enable NAND node on IPQ8064 AP148
|
||||
platform
|
||||
|
||||
Original patch by Archit Taneja.
|
||||
|
||||
Enable the NAND controller node on the AP148 platform. Provide pinmux
|
||||
information.
|
||||
|
||||
Signed-off-by: Archit Taneja <architt@codeaurora.org>
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 42 ++++++++++++++++++++++++++++++
|
||||
1 file changed, 42 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
||||
@@ -38,6 +38,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 {
|
||||
@@ -98,5 +120,25 @@
|
||||
ports-implemented = <0x1>;
|
||||
status = "ok";
|
||||
};
|
||||
+
|
||||
+ nand@1ac00000 {
|
||||
+ status = "ok";
|
||||
+
|
||||
+ pinctrl-0 = <&nand_pins>;
|
||||
+ pinctrl-names = "default";
|
||||
+
|
||||
+ nandcs@0 {
|
||||
+ compatible = "qcom,nandcs";
|
||||
+ reg = <0>;
|
||||
+
|
||||
+ nand-ecc-strength = <4>;
|
||||
+ nand-ecc-step-size = <512>;
|
||||
+ nand-bus-width = <8>;
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
||||
+
|
||||
+&adm_dma {
|
||||
+ status = "ok";
|
||||
+};
|
|
@ -0,0 +1,208 @@
|
|||
From 2852d6df4b60987f9248c3d36d5fe2462e6556b9 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Tue, 12 Apr 2016 09:11:47 -0500
|
||||
Subject: [PATCH 06/37] spi: qup: Make sure mode is only determined once
|
||||
|
||||
This patch calculates the mode once. All decisions on the current
|
||||
transaction
|
||||
is made using the mode instead of use_dma
|
||||
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 87 +++++++++++++++++++++----------------------------
|
||||
1 file changed, 37 insertions(+), 50 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -149,12 +149,20 @@ struct spi_qup {
|
||||
int rx_bytes;
|
||||
int qup_v1;
|
||||
|
||||
- int use_dma;
|
||||
+ int mode;
|
||||
struct dma_slave_config rx_conf;
|
||||
struct dma_slave_config tx_conf;
|
||||
};
|
||||
|
||||
|
||||
+static inline bool spi_qup_is_dma_xfer(int mode)
|
||||
+{
|
||||
+ if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
|
||||
+ return true;
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
|
||||
{
|
||||
u32 opstate = readl_relaxed(controller->base + QUP_STATE);
|
||||
@@ -424,7 +432,7 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
error = -EIO;
|
||||
}
|
||||
|
||||
- if (!controller->use_dma) {
|
||||
+ if (!spi_qup_is_dma_xfer(controller->mode)) {
|
||||
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
||||
spi_qup_fifo_read(controller, xfer);
|
||||
|
||||
@@ -443,34 +451,11 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
-static u32
|
||||
-spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
|
||||
-{
|
||||
- struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
- u32 mode;
|
||||
-
|
||||
- qup->w_size = 4;
|
||||
-
|
||||
- if (xfer->bits_per_word <= 8)
|
||||
- qup->w_size = 1;
|
||||
- else if (xfer->bits_per_word <= 16)
|
||||
- qup->w_size = 2;
|
||||
-
|
||||
- qup->n_words = xfer->len / qup->w_size;
|
||||
-
|
||||
- if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
|
||||
- mode = QUP_IO_M_MODE_FIFO;
|
||||
- else
|
||||
- mode = QUP_IO_M_MODE_BLOCK;
|
||||
-
|
||||
- return mode;
|
||||
-}
|
||||
-
|
||||
/* set clock freq ... bits per word */
|
||||
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, control;
|
||||
+ u32 config, iomode, control;
|
||||
int ret, n_words;
|
||||
|
||||
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
||||
@@ -491,23 +476,22 @@ static int spi_qup_io_config(struct spi_
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
- mode = spi_qup_get_mode(spi->master, xfer);
|
||||
+ controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
+ controller->n_words = xfer->len / controller->w_size;
|
||||
n_words = controller->n_words;
|
||||
|
||||
- if (mode == QUP_IO_M_MODE_FIFO) {
|
||||
+ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
|
||||
+ 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 */
|
||||
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
- } 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);
|
||||
- /* 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);
|
||||
- } else {
|
||||
- mode = QUP_IO_M_MODE_BAM;
|
||||
+
|
||||
+ } else if (spi->master->can_dma &&
|
||||
+ spi->master->can_dma(spi->master, spi, xfer) &&
|
||||
+ spi->master->cur_msg_mapped) {
|
||||
+ controller->mode = QUP_IO_M_MODE_BAM;
|
||||
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
||||
|
||||
@@ -528,19 +512,26 @@ static int spi_qup_io_config(struct spi_
|
||||
|
||||
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
}
|
||||
+ } else {
|
||||
+ 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 */
|
||||
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
- if (!controller->use_dma)
|
||||
+ if (!spi_qup_is_dma_xfer(controller->mode))
|
||||
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);
|
||||
+ 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);
|
||||
|
||||
@@ -581,7 +572,7 @@ static int spi_qup_io_config(struct spi_
|
||||
config |= xfer->bits_per_word - 1;
|
||||
config |= QUP_CONFIG_SPI_MODE;
|
||||
|
||||
- if (controller->use_dma) {
|
||||
+ if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
if (!xfer->tx_buf)
|
||||
config |= QUP_CONFIG_NO_OUTPUT;
|
||||
if (!xfer->rx_buf)
|
||||
@@ -599,7 +590,7 @@ static int spi_qup_io_config(struct spi_
|
||||
* status change in BAM mode
|
||||
*/
|
||||
|
||||
- if (mode == QUP_IO_M_MODE_BAM)
|
||||
+ if (spi_qup_is_dma_xfer(controller->mode))
|
||||
mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
|
||||
|
||||
writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
|
||||
@@ -633,7 +624,7 @@ static int spi_qup_transfer_one(struct s
|
||||
controller->tx_bytes = 0;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
- if (controller->use_dma)
|
||||
+ if (spi_qup_is_dma_xfer(controller->mode))
|
||||
ret = spi_qup_do_dma(master, xfer);
|
||||
else
|
||||
ret = spi_qup_do_pio(master, xfer);
|
||||
@@ -657,7 +648,7 @@ exit:
|
||||
ret = controller->error;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
- if (ret && controller->use_dma)
|
||||
+ if (ret && spi_qup_is_dma_xfer(controller->mode))
|
||||
spi_qup_dma_terminate(master, xfer);
|
||||
|
||||
return ret;
|
||||
@@ -668,9 +659,7 @@ static bool spi_qup_can_dma(struct spi_m
|
||||
{
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
size_t dma_align = dma_get_cache_alignment();
|
||||
- u32 mode;
|
||||
-
|
||||
- qup->use_dma = 0;
|
||||
+ int n_words;
|
||||
|
||||
if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
|
||||
IS_ERR_OR_NULL(master->dma_rx) ||
|
||||
@@ -682,12 +671,10 @@ static bool spi_qup_can_dma(struct spi_m
|
||||
!IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
|
||||
return false;
|
||||
|
||||
- mode = spi_qup_get_mode(master, xfer);
|
||||
- if (mode == QUP_IO_M_MODE_FIFO)
|
||||
+ n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
+ if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
|
||||
return false;
|
||||
|
||||
- qup->use_dma = 1;
|
||||
-
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
From 826290ee1fd1bcd26b6771f94f6680a4ff8951c4 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Fri, 29 Jan 2016 22:06:50 -0600
|
||||
Subject: [PATCH 07/37] spi: qup: Fix transaction done signaling
|
||||
|
||||
Wait to signal done until we get all of the interrupts we are expecting
|
||||
to get for a transaction. If we don't wait for the input done flag, we
|
||||
can be inbetween transactions when the done flag comes in and this can
|
||||
mess up the next transaction.
|
||||
|
||||
CC: Grant Grundler <grundler@chromium.org>
|
||||
CC: Sarthak Kukreti <skukreti@codeaurora.org>
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -445,7 +445,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;
|
|
@ -0,0 +1,219 @@
|
|||
From 715d008b67b21fb8bfefaeeefa5b8ddf23777872 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Tue, 2 Feb 2016 17:00:53 -0600
|
||||
Subject: [PATCH 08/37] spi: qup: Fix DMA mode to work correctly
|
||||
|
||||
This patch fixes a few issues with the DMA mode. The QUP needs to be
|
||||
placed in the run mode before the DMA transactions are executed. The
|
||||
conditions for being able to DMA vary between revisions of the QUP.
|
||||
This is due to v1.1.1 using ADM DMA and later revisions using BAM.
|
||||
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 94 ++++++++++++++++++++++++++++++++-----------------
|
||||
1 file changed, 62 insertions(+), 32 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -142,6 +142,7 @@ struct spi_qup {
|
||||
|
||||
struct spi_transfer *xfer;
|
||||
struct completion done;
|
||||
+ struct completion dma_tx_done;
|
||||
int error;
|
||||
int w_size; /* bytes per SPI word */
|
||||
int n_words;
|
||||
@@ -284,16 +285,16 @@ static void spi_qup_fifo_write(struct sp
|
||||
|
||||
static void spi_qup_dma_done(void *data)
|
||||
{
|
||||
- struct spi_qup *qup = data;
|
||||
+ struct completion *done = data;
|
||||
|
||||
- complete(&qup->done);
|
||||
+ complete(done);
|
||||
}
|
||||
|
||||
static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
|
||||
enum dma_transfer_direction dir,
|
||||
- dma_async_tx_callback callback)
|
||||
+ dma_async_tx_callback callback,
|
||||
+ void *data)
|
||||
{
|
||||
- struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct scatterlist *sgl;
|
||||
@@ -312,11 +313,11 @@ static int spi_qup_prep_sg(struct spi_ma
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
|
||||
- if (!desc)
|
||||
- return -EINVAL;
|
||||
+ if (IS_ERR_OR_NULL(desc))
|
||||
+ return desc ? PTR_ERR(desc) : -EINVAL;
|
||||
|
||||
desc->callback = callback;
|
||||
- desc->callback_param = qup;
|
||||
+ desc->callback_param = data;
|
||||
|
||||
cookie = dmaengine_submit(desc);
|
||||
|
||||
@@ -332,18 +333,29 @@ static void spi_qup_dma_terminate(struct
|
||||
dmaengine_terminate_all(master->dma_rx);
|
||||
}
|
||||
|
||||
-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
|
||||
+static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
|
||||
+unsigned long timeout)
|
||||
{
|
||||
+ struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
|
||||
int ret;
|
||||
|
||||
+ /* before issuing the descriptors, set the QUP to run */
|
||||
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
+ if (ret) {
|
||||
+ dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
if (xfer->rx_buf)
|
||||
rx_done = spi_qup_dma_done;
|
||||
- else if (xfer->tx_buf)
|
||||
+
|
||||
+ if (xfer->tx_buf)
|
||||
tx_done = spi_qup_dma_done;
|
||||
|
||||
if (xfer->rx_buf) {
|
||||
- ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done);
|
||||
+ ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
|
||||
+ &qup->done);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -351,17 +363,25 @@ static int spi_qup_do_dma(struct spi_mas
|
||||
}
|
||||
|
||||
if (xfer->tx_buf) {
|
||||
- ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done);
|
||||
+ ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done,
|
||||
+ &qup->dma_tx_done);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dma_async_issue_pending(master->dma_tx);
|
||||
}
|
||||
|
||||
- return 0;
|
||||
+ if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout))
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ if (xfer->tx_buf && !wait_for_completion_timeout(&qup->dma_tx_done, timeout))
|
||||
+ ret = -ETIMEDOUT;
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
|
||||
+static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
|
||||
+ unsigned long timeout)
|
||||
{
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
@@ -380,6 +400,15 @@ static int spi_qup_do_pio(struct spi_mas
|
||||
|
||||
spi_qup_fifo_write(qup, xfer);
|
||||
|
||||
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
+ if (ret) {
|
||||
+ dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if (!wait_for_completion_timeout(&qup->done, timeout))
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -428,7 +457,6 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
dev_warn(controller->dev, "CLK_OVER_RUN\n");
|
||||
if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
|
||||
dev_warn(controller->dev, "CLK_UNDER_RUN\n");
|
||||
-
|
||||
error = -EIO;
|
||||
}
|
||||
|
||||
@@ -617,6 +645,7 @@ static int spi_qup_transfer_one(struct s
|
||||
timeout = 100 * msecs_to_jiffies(timeout);
|
||||
|
||||
reinit_completion(&controller->done);
|
||||
+ reinit_completion(&controller->dma_tx_done);
|
||||
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
controller->xfer = xfer;
|
||||
@@ -626,21 +655,13 @@ static int spi_qup_transfer_one(struct s
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
if (spi_qup_is_dma_xfer(controller->mode))
|
||||
- ret = spi_qup_do_dma(master, xfer);
|
||||
+ ret = spi_qup_do_dma(master, xfer, timeout);
|
||||
else
|
||||
- ret = spi_qup_do_pio(master, xfer);
|
||||
+ ret = spi_qup_do_pio(master, xfer, timeout);
|
||||
|
||||
if (ret)
|
||||
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;
|
||||
-
|
||||
exit:
|
||||
spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
@@ -662,15 +683,23 @@ static bool spi_qup_can_dma(struct spi_m
|
||||
size_t dma_align = dma_get_cache_alignment();
|
||||
int n_words;
|
||||
|
||||
- if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
|
||||
- IS_ERR_OR_NULL(master->dma_rx) ||
|
||||
- !IS_ALIGNED((size_t)xfer->rx_buf, dma_align)))
|
||||
- return false;
|
||||
+ if (xfer->rx_buf) {
|
||||
+ if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) ||
|
||||
+ IS_ERR_OR_NULL(master->dma_rx))
|
||||
+ return false;
|
||||
|
||||
- if (xfer->tx_buf && (xfer->len % qup->out_blk_sz ||
|
||||
- IS_ERR_OR_NULL(master->dma_tx) ||
|
||||
- !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
|
||||
- return false;
|
||||
+ if (qup->qup_v1 && (xfer->len % qup->in_blk_sz))
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (xfer->tx_buf) {
|
||||
+ if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) ||
|
||||
+ IS_ERR_OR_NULL(master->dma_tx))
|
||||
+ return false;
|
||||
+
|
||||
+ if (qup->qup_v1 && (xfer->len % qup->out_blk_sz))
|
||||
+ return false;
|
||||
+ }
|
||||
|
||||
n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
|
||||
@@ -836,6 +865,7 @@ static int spi_qup_probe(struct platform
|
||||
|
||||
spin_lock_init(&controller->lock);
|
||||
init_completion(&controller->done);
|
||||
+ init_completion(&controller->dma_tx_done);
|
||||
|
||||
iomode = readl_relaxed(base + QUP_IO_M_MODES);
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
From 4dc7631bbf7c7ac7548026ce45d889235e4f5892 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Sun, 31 Jan 2016 21:28:13 -0600
|
||||
Subject: [PATCH 09/37] spi: qup: Fix block mode to work correctly
|
||||
|
||||
This patch corrects the behavior of the BLOCK transactions. During block
|
||||
transactions, the controller must be read/written to in block size transactions.
|
||||
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 182 ++++++++++++++++++++++++++++++++++++++-----------
|
||||
1 file changed, 142 insertions(+), 40 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)
|
||||
@@ -155,6 +157,12 @@ struct spi_qup {
|
||||
struct dma_slave_config tx_conf;
|
||||
};
|
||||
|
||||
+static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
|
||||
+{
|
||||
+ u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
+
|
||||
+ return opflag & flag;
|
||||
+}
|
||||
|
||||
static inline bool spi_qup_is_dma_xfer(int mode)
|
||||
{
|
||||
@@ -216,29 +224,26 @@ 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_read_from_fifo(struct spi_qup *controller,
|
||||
+ struct spi_transfer *xfer, u32 num_words)
|
||||
{
|
||||
u8 *rx_buf = xfer->rx_buf;
|
||||
- u32 word, state;
|
||||
- int idx, shift, w_size;
|
||||
-
|
||||
- w_size = controller->w_size;
|
||||
+ int i, shift, num_bytes;
|
||||
+ u32 word;
|
||||
|
||||
- while (controller->rx_bytes < xfer->len) {
|
||||
-
|
||||
- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
|
||||
- break;
|
||||
+ for (; num_words; num_words--) {
|
||||
|
||||
word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
||||
|
||||
+ num_bytes = min_t(int, xfer->len - controller->rx_bytes,
|
||||
+ controller->w_size);
|
||||
+
|
||||
if (!rx_buf) {
|
||||
- controller->rx_bytes += w_size;
|
||||
+ controller->rx_bytes += num_bytes;
|
||||
continue;
|
||||
}
|
||||
|
||||
- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
|
||||
+ for (i = 0; i < num_bytes; i++, controller->rx_bytes++) {
|
||||
/*
|
||||
* The data format depends on bytes per SPI word:
|
||||
* 4 bytes: 0x12345678
|
||||
@@ -246,38 +251,80 @@ static void spi_qup_fifo_read(struct spi
|
||||
* 1 byte : 0x00000012
|
||||
*/
|
||||
shift = BITS_PER_BYTE;
|
||||
- shift *= (w_size - idx - 1);
|
||||
+ shift *= (controller->w_size - i - 1);
|
||||
rx_buf[controller->rx_bytes] = word >> shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-static void spi_qup_fifo_write(struct spi_qup *controller,
|
||||
+static void spi_qup_read(struct spi_qup *controller,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
- const u8 *tx_buf = xfer->tx_buf;
|
||||
- u32 word, state, data;
|
||||
- int idx, w_size;
|
||||
+ u32 remainder, words_per_block, num_words;
|
||||
+ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
|
||||
+
|
||||
+ remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes,
|
||||
+ controller->w_size);
|
||||
+ words_per_block = controller->in_blk_sz >> 2;
|
||||
+
|
||||
+ do {
|
||||
+ /* ACK by clearing service flag */
|
||||
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
+ controller->base + QUP_OPERATIONAL);
|
||||
+
|
||||
+ if (is_block_mode) {
|
||||
+ num_words = (remainder > words_per_block) ?
|
||||
+ words_per_block : remainder;
|
||||
+ } else {
|
||||
+ if (!spi_qup_is_flag_set(controller,
|
||||
+ QUP_OP_IN_FIFO_NOT_EMPTY))
|
||||
+ break;
|
||||
|
||||
- w_size = controller->w_size;
|
||||
+ num_words = 1;
|
||||
+ }
|
||||
+
|
||||
+ /* read up to the maximum transfer size available */
|
||||
+ spi_qup_read_from_fifo(controller, xfer, num_words);
|
||||
|
||||
- while (controller->tx_bytes < xfer->len) {
|
||||
+ remainder -= num_words;
|
||||
|
||||
- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
- if (state & QUP_OP_OUT_FIFO_FULL)
|
||||
+ /* if block mode, check to see if next block is available */
|
||||
+ if (is_block_mode && !spi_qup_is_flag_set(controller,
|
||||
+ QUP_OP_IN_BLOCK_READ_REQ))
|
||||
break;
|
||||
|
||||
+ } while (remainder);
|
||||
+
|
||||
+ /*
|
||||
+ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
|
||||
+ * mode reads, it has to be cleared again at the very end
|
||||
+ */
|
||||
+ if (is_block_mode && spi_qup_is_flag_set(controller,
|
||||
+ QUP_OP_MAX_INPUT_DONE_FLAG))
|
||||
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
+ controller->base + QUP_OPERATIONAL);
|
||||
+
|
||||
+}
|
||||
+
|
||||
+static void spi_qup_write_to_fifo(struct spi_qup *controller,
|
||||
+ struct spi_transfer *xfer, u32 num_words)
|
||||
+{
|
||||
+ const u8 *tx_buf = xfer->tx_buf;
|
||||
+ int i, num_bytes;
|
||||
+ u32 word, data;
|
||||
+
|
||||
+ for (; num_words; num_words--) {
|
||||
word = 0;
|
||||
- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
|
||||
|
||||
- if (!tx_buf) {
|
||||
- controller->tx_bytes += w_size;
|
||||
- break;
|
||||
+ num_bytes = min_t(int, xfer->len - controller->tx_bytes,
|
||||
+ controller->w_size);
|
||||
+ if (tx_buf)
|
||||
+ for (i = 0; i < num_bytes; i++) {
|
||||
+ data = tx_buf[controller->tx_bytes + i];
|
||||
+ word |= data << (BITS_PER_BYTE * (3 - i));
|
||||
}
|
||||
|
||||
- data = tx_buf[controller->tx_bytes];
|
||||
- word |= data << (BITS_PER_BYTE * (3 - idx));
|
||||
- }
|
||||
+ controller->tx_bytes += num_bytes;
|
||||
|
||||
writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
|
||||
}
|
||||
@@ -290,6 +337,44 @@ static void spi_qup_dma_done(void *data)
|
||||
complete(done);
|
||||
}
|
||||
|
||||
+static void spi_qup_write(struct spi_qup *controller,
|
||||
+ struct spi_transfer *xfer)
|
||||
+{
|
||||
+ bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
|
||||
+ u32 remainder, words_per_block, num_words;
|
||||
+
|
||||
+ remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes,
|
||||
+ controller->w_size);
|
||||
+ words_per_block = controller->out_blk_sz >> 2;
|
||||
+
|
||||
+ do {
|
||||
+ /* ACK by clearing service flag */
|
||||
+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
|
||||
+ controller->base + QUP_OPERATIONAL);
|
||||
+
|
||||
+ if (is_block_mode) {
|
||||
+ num_words = (remainder > words_per_block) ?
|
||||
+ words_per_block : remainder;
|
||||
+ } else {
|
||||
+ if (spi_qup_is_flag_set(controller,
|
||||
+ QUP_OP_OUT_FIFO_FULL))
|
||||
+ break;
|
||||
+
|
||||
+ num_words = 1;
|
||||
+ }
|
||||
+
|
||||
+ spi_qup_write_to_fifo(controller, xfer, num_words);
|
||||
+
|
||||
+ remainder -= num_words;
|
||||
+
|
||||
+ /* if block mode, check to see if next block is available */
|
||||
+ if (is_block_mode && !spi_qup_is_flag_set(controller,
|
||||
+ QUP_OP_OUT_BLOCK_WRITE_REQ))
|
||||
+ break;
|
||||
+
|
||||
+ } while (remainder);
|
||||
+}
|
||||
+
|
||||
static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
|
||||
enum dma_transfer_direction dir,
|
||||
dma_async_tx_callback callback,
|
||||
@@ -347,11 +432,13 @@ unsigned long timeout)
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (xfer->rx_buf)
|
||||
- rx_done = spi_qup_dma_done;
|
||||
+ if (!qup->qup_v1) {
|
||||
+ if (xfer->rx_buf)
|
||||
+ rx_done = spi_qup_dma_done;
|
||||
|
||||
- if (xfer->tx_buf)
|
||||
- tx_done = spi_qup_dma_done;
|
||||
+ if (xfer->tx_buf)
|
||||
+ tx_done = spi_qup_dma_done;
|
||||
+ }
|
||||
|
||||
if (xfer->rx_buf) {
|
||||
ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
|
||||
@@ -398,7 +485,8 @@ static int spi_qup_do_pio(struct spi_mas
|
||||
return ret;
|
||||
}
|
||||
|
||||
- spi_qup_fifo_write(qup, xfer);
|
||||
+ if (qup->mode == QUP_IO_M_MODE_FIFO)
|
||||
+ spi_qup_write(qup, xfer);
|
||||
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
if (ret) {
|
||||
@@ -431,10 +519,11 @@ 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) {
|
||||
- dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
|
||||
+ 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;
|
||||
}
|
||||
@@ -460,12 +549,20 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
error = -EIO;
|
||||
}
|
||||
|
||||
- if (!spi_qup_is_dma_xfer(controller->mode)) {
|
||||
+ if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
||||
+ if (opflags & QUP_OP_IN_SERVICE_FLAG &&
|
||||
+ opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
|
||||
+ complete(&controller->done);
|
||||
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG &&
|
||||
+ opflags & QUP_OP_MAX_OUTPUT_DONE_FLAG)
|
||||
+ complete(&controller->dma_tx_done);
|
||||
+ } else {
|
||||
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
||||
- spi_qup_fifo_read(controller, xfer);
|
||||
+ spi_qup_read(controller, xfer);
|
||||
|
||||
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||
- spi_qup_fifo_write(controller, xfer);
|
||||
+ spi_qup_write(controller, xfer);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
@@ -473,6 +570,9 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
controller->xfer = xfer;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
+ /* re-read opflags as flags may have changed due to actions above */
|
||||
+ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
+
|
||||
if ((controller->rx_bytes == xfer->len &&
|
||||
(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
|
||||
complete(&controller->done);
|
||||
@@ -516,11 +616,13 @@ 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 if (spi->master->can_dma &&
|
||||
spi->master->can_dma(spi->master, spi, xfer) &&
|
||||
spi->master->cur_msg_mapped) {
|
||||
controller->mode = QUP_IO_M_MODE_BAM;
|
||||
+ 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);
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
From 543618f5388d487ba88e3d5304c161fc3ccf61d1 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 10 Mar 2016 16:44:55 -0600
|
||||
Subject: [PATCH 10/37] spi: qup: properly detect extra interrupts
|
||||
|
||||
It's possible for a SPI transaction to complete and get another
|
||||
interrupt and have it processed on the same spi_transfer before the
|
||||
transfer_one can set it to NULL.
|
||||
|
||||
This masks unexpected interrupts, so let's set the spi_transfer to
|
||||
NULL in the interrupt once the transaction is done. So we can
|
||||
properly detect these bad interrupts and print warning messages.
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 15 +++++++++------
|
||||
1 file changed, 9 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -507,6 +507,7 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
u32 opflags, qup_err, spi_err;
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
+ bool done = 0;
|
||||
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
xfer = controller->xfer;
|
||||
@@ -565,16 +566,19 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
spi_qup_write(controller, xfer);
|
||||
}
|
||||
|
||||
- spin_lock_irqsave(&controller->lock, flags);
|
||||
- controller->error = error;
|
||||
- controller->xfer = xfer;
|
||||
- spin_unlock_irqrestore(&controller->lock, flags);
|
||||
-
|
||||
/* re-read opflags as flags may have changed due to actions above */
|
||||
opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
|
||||
if ((controller->rx_bytes == xfer->len &&
|
||||
(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
|
||||
+ done = true;
|
||||
+
|
||||
+ spin_lock_irqsave(&controller->lock, flags);
|
||||
+ controller->error = error;
|
||||
+ controller->xfer = done ? NULL : xfer;
|
||||
+ spin_unlock_irqrestore(&controller->lock, flags);
|
||||
+
|
||||
+ if (done)
|
||||
complete(&controller->done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@@ -767,7 +771,6 @@ static int spi_qup_transfer_one(struct s
|
||||
exit:
|
||||
spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
- controller->xfer = NULL;
|
||||
if (!ret)
|
||||
ret = controller->error;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
|
@ -0,0 +1,26 @@
|
|||
From ba96e9449a63acd658d8ad0a5b3755b559410999 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 10 Mar 2016 16:48:27 -0600
|
||||
Subject: [PATCH 11/37] spi: qup: don't re-read opflags to see if transaction
|
||||
is done for reads
|
||||
|
||||
For reads, we will get another interrupt so we need to handle things
|
||||
then. For writes, we can finish up now.
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -567,7 +567,8 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
}
|
||||
|
||||
/* re-read opflags as flags may have changed due to actions above */
|
||||
- opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||
+ opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
|
||||
if ((controller->rx_bytes == xfer->len &&
|
||||
(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
|
|
@ -0,0 +1,202 @@
|
|||
From ef00ad56d728618203358d9eba7ca8e7eb48e701 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Tue, 26 Apr 2016 12:57:46 -0500
|
||||
Subject: [PATCH 12/37] spi: qup: refactor spi_qup_io_config in two functions
|
||||
|
||||
This is preparation for handling transactions larger than 64K-1 bytes in
|
||||
block mode which is currently unsupported quietly fails.
|
||||
|
||||
We need to break these into two functions 1) prep is called once per
|
||||
spi_message and 2) io_config is calle once per spi-qup bus transaction
|
||||
|
||||
This is just refactoring, there should be no functional change
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 141 ++++++++++++++++++++++++++++++-------------------
|
||||
1 file changed, 86 insertions(+), 55 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -585,12 +585,11 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
-/* set clock freq ... bits per word */
|
||||
-static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
+/* set clock freq ... bits per word, determine mode */
|
||||
+static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||
- u32 config, iomode, control;
|
||||
- int ret, n_words;
|
||||
+ int ret;
|
||||
|
||||
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
||||
dev_err(controller->dev, "too big size for loopback %d > %d\n",
|
||||
@@ -605,56 +604,94 @@ static int spi_qup_io_config(struct spi_
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
- if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
||||
- dev_err(controller->dev, "cannot set RESET state\n");
|
||||
- return -EIO;
|
||||
- }
|
||||
-
|
||||
controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
controller->n_words = xfer->len / controller->w_size;
|
||||
- n_words = controller->n_words;
|
||||
|
||||
- if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
|
||||
+ if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32)))
|
||||
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 */
|
||||
- writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||
- writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
- } else if (spi->master->can_dma &&
|
||||
- spi->master->can_dma(spi->master, spi, xfer) &&
|
||||
- spi->master->cur_msg_mapped) {
|
||||
+ else if (spi->master->can_dma &&
|
||||
+ spi->master->can_dma(spi->master, spi, xfer) &&
|
||||
+ spi->master->cur_msg_mapped)
|
||||
controller->mode = QUP_IO_M_MODE_BAM;
|
||||
- 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);
|
||||
-
|
||||
- if (!controller->qup_v1) {
|
||||
- void __iomem *input_cnt;
|
||||
-
|
||||
- input_cnt = controller->base + QUP_MX_INPUT_CNT;
|
||||
- /*
|
||||
- * for DMA transfers, both QUP_MX_INPUT_CNT and
|
||||
- * QUP_MX_OUTPUT_CNT must be zero to all cases but one.
|
||||
- * That case is a non-balanced transfer when there is
|
||||
- * only a rx_buf.
|
||||
- */
|
||||
- if (xfer->tx_buf)
|
||||
- writel_relaxed(0, input_cnt);
|
||||
- else
|
||||
- writel_relaxed(n_words, input_cnt);
|
||||
+ else
|
||||
+ controller->mode = QUP_IO_M_MODE_BLOCK;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
+/* prep qup for another spi transaction of specific type */
|
||||
+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, control;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ reinit_completion(&controller->done);
|
||||
+ reinit_completion(&controller->dma_tx_done);
|
||||
+
|
||||
+ spin_lock_irqsave(&controller->lock, flags);
|
||||
+ controller->xfer = xfer;
|
||||
+ controller->error = 0;
|
||||
+ controller->rx_bytes = 0;
|
||||
+ controller->tx_bytes = 0;
|
||||
+ spin_unlock_irqrestore(&controller->lock, flags);
|
||||
+
|
||||
+
|
||||
+ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
||||
+ dev_err(controller->dev, "cannot set RESET state\n");
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ switch (controller->mode) {
|
||||
+ case QUP_IO_M_MODE_FIFO:
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_READ_CNT);
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_WRITE_CNT);
|
||||
+ /* 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->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);
|
||||
+ break;
|
||||
+ case QUP_IO_M_MODE_BAM:
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_INPUT_CNT);
|
||||
+ writel_relaxed(controller->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);
|
||||
+ if (!controller->qup_v1) {
|
||||
+ void __iomem *input_cnt;
|
||||
+
|
||||
+ input_cnt = controller->base + QUP_MX_INPUT_CNT;
|
||||
+ /*
|
||||
+ * for DMA transfers, both QUP_MX_INPUT_CNT and
|
||||
+ * QUP_MX_OUTPUT_CNT must be zero to all cases
|
||||
+ * but one. That case is a non-balanced
|
||||
+ * transfer when there is only a rx_buf.
|
||||
+ */
|
||||
+ if (xfer->tx_buf)
|
||||
+ writel_relaxed(0, input_cnt);
|
||||
+ else
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ input_cnt);
|
||||
+
|
||||
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
+ }
|
||||
+ break;
|
||||
+ case QUP_IO_M_MODE_BLOCK:
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_INPUT_CNT);
|
||||
+ writel_relaxed(controller->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);
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(controller->dev, "unknown mode = %d\n",
|
||||
+ controller->mode);
|
||||
+ return -EIO;
|
||||
}
|
||||
|
||||
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
|
||||
@@ -743,6 +780,10 @@ static int spi_qup_transfer_one(struct s
|
||||
unsigned long timeout, flags;
|
||||
int ret = -EIO;
|
||||
|
||||
+ ret = spi_qup_io_prep(spi, xfer);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
ret = spi_qup_io_config(spi, xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -751,16 +792,6 @@ static int spi_qup_transfer_one(struct s
|
||||
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
|
||||
timeout = 100 * msecs_to_jiffies(timeout);
|
||||
|
||||
- reinit_completion(&controller->done);
|
||||
- reinit_completion(&controller->dma_tx_done);
|
||||
-
|
||||
- spin_lock_irqsave(&controller->lock, flags);
|
||||
- controller->xfer = xfer;
|
||||
- controller->error = 0;
|
||||
- controller->rx_bytes = 0;
|
||||
- controller->tx_bytes = 0;
|
||||
- spin_unlock_irqrestore(&controller->lock, flags);
|
||||
-
|
||||
if (spi_qup_is_dma_xfer(controller->mode))
|
||||
ret = spi_qup_do_dma(master, xfer, timeout);
|
||||
else
|
|
@ -0,0 +1,391 @@
|
|||
From 9263d98e255e1d51b41c752d53e39877728a9419 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Tue, 26 Apr 2016 13:14:45 -0500
|
||||
Subject: [PATCH 13/37] spi: qup: call io_config in mode specific function
|
||||
|
||||
DMA transactions should only only need to call io_config only once, but
|
||||
block mode might call it several times to setup several transactions so
|
||||
it can handle reads/writes larger than the max size per transaction, so
|
||||
we move the call to the do_ functions.
|
||||
|
||||
This is just refactoring, there should be no functional change
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 327 +++++++++++++++++++++++++------------------------
|
||||
1 file changed, 166 insertions(+), 161 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -418,13 +418,170 @@ static void spi_qup_dma_terminate(struct
|
||||
dmaengine_terminate_all(master->dma_rx);
|
||||
}
|
||||
|
||||
-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
|
||||
+/* prep qup for another spi transaction of specific type */
|
||||
+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, control;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ reinit_completion(&controller->done);
|
||||
+ reinit_completion(&controller->dma_tx_done);
|
||||
+
|
||||
+ spin_lock_irqsave(&controller->lock, flags);
|
||||
+ controller->xfer = xfer;
|
||||
+ controller->error = 0;
|
||||
+ controller->rx_bytes = 0;
|
||||
+ controller->tx_bytes = 0;
|
||||
+ spin_unlock_irqrestore(&controller->lock, flags);
|
||||
+
|
||||
+ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
||||
+ dev_err(controller->dev, "cannot set RESET state\n");
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ switch (controller->mode) {
|
||||
+ case QUP_IO_M_MODE_FIFO:
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_READ_CNT);
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_WRITE_CNT);
|
||||
+ /* must be zero for FIFO */
|
||||
+ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
+ break;
|
||||
+ case QUP_IO_M_MODE_BAM:
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_INPUT_CNT);
|
||||
+ writel_relaxed(controller->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);
|
||||
+ if (!controller->qup_v1) {
|
||||
+ void __iomem *input_cnt;
|
||||
+
|
||||
+ input_cnt = controller->base + QUP_MX_INPUT_CNT;
|
||||
+ /*
|
||||
+ * for DMA transfers, both QUP_MX_INPUT_CNT and
|
||||
+ * QUP_MX_OUTPUT_CNT must be zero to all cases
|
||||
+ * but one. That case is a non-balanced
|
||||
+ * transfer when there is only a rx_buf.
|
||||
+ */
|
||||
+ if (xfer->tx_buf)
|
||||
+ writel_relaxed(0, input_cnt);
|
||||
+ else
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ input_cnt);
|
||||
+
|
||||
+ writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
+ }
|
||||
+ break;
|
||||
+ case QUP_IO_M_MODE_BLOCK:
|
||||
+ writel_relaxed(controller->n_words,
|
||||
+ controller->base + QUP_MX_INPUT_CNT);
|
||||
+ writel_relaxed(controller->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);
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(controller->dev, "unknown mode = %d\n",
|
||||
+ controller->mode);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ 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);
|
||||
+
|
||||
+ if (!spi_qup_is_dma_xfer(controller->mode))
|
||||
+ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
||||
+ else
|
||||
+ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
|
||||
+
|
||||
+ 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);
|
||||
+
|
||||
+ control = readl_relaxed(controller->base + SPI_IO_CONTROL);
|
||||
+
|
||||
+ if (spi->mode & SPI_CPOL)
|
||||
+ control |= SPI_IO_C_CLK_IDLE_HIGH;
|
||||
+ else
|
||||
+ control &= ~SPI_IO_C_CLK_IDLE_HIGH;
|
||||
+
|
||||
+ writel_relaxed(control, controller->base + SPI_IO_CONTROL);
|
||||
+
|
||||
+ config = readl_relaxed(controller->base + SPI_CONFIG);
|
||||
+
|
||||
+ if (spi->mode & SPI_LOOP)
|
||||
+ config |= SPI_CONFIG_LOOPBACK;
|
||||
+ else
|
||||
+ config &= ~SPI_CONFIG_LOOPBACK;
|
||||
+
|
||||
+ if (spi->mode & SPI_CPHA)
|
||||
+ config &= ~SPI_CONFIG_INPUT_FIRST;
|
||||
+ else
|
||||
+ config |= SPI_CONFIG_INPUT_FIRST;
|
||||
+
|
||||
+ /*
|
||||
+ * HS_MODE improves signal stability for spi-clk high rates,
|
||||
+ * but is invalid in loop back mode.
|
||||
+ */
|
||||
+ if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
|
||||
+ config |= SPI_CONFIG_HS_MODE;
|
||||
+ else
|
||||
+ config &= ~SPI_CONFIG_HS_MODE;
|
||||
+
|
||||
+ writel_relaxed(config, controller->base + SPI_CONFIG);
|
||||
+
|
||||
+ config = readl_relaxed(controller->base + QUP_CONFIG);
|
||||
+ config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
|
||||
+ config |= xfer->bits_per_word - 1;
|
||||
+ config |= QUP_CONFIG_SPI_MODE;
|
||||
+
|
||||
+ if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
+ 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 */
|
||||
+ if (!controller->qup_v1) {
|
||||
+ u32 mask = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
|
||||
+ * status change in BAM mode
|
||||
+ */
|
||||
+
|
||||
+ if (spi_qup_is_dma_xfer(controller->mode))
|
||||
+ mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
|
||||
+
|
||||
+ writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
|
||||
unsigned long timeout)
|
||||
{
|
||||
+ struct spi_master *master = spi->master;
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
|
||||
int ret;
|
||||
|
||||
+ ret = spi_qup_io_config(spi, xfer);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
/* before issuing the descriptors, set the QUP to run */
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
if (ret) {
|
||||
@@ -467,12 +624,17 @@ unsigned long timeout)
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
|
||||
+static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
|
||||
unsigned long timeout)
|
||||
{
|
||||
+ struct spi_master *master = spi->master;
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
+ ret = spi_qup_io_config(spi, xfer);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
if (ret) {
|
||||
dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
@@ -619,159 +781,6 @@ static int spi_qup_io_prep(struct spi_de
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/* prep qup for another spi transaction of specific type */
|
||||
-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, control;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- reinit_completion(&controller->done);
|
||||
- reinit_completion(&controller->dma_tx_done);
|
||||
-
|
||||
- spin_lock_irqsave(&controller->lock, flags);
|
||||
- controller->xfer = xfer;
|
||||
- controller->error = 0;
|
||||
- controller->rx_bytes = 0;
|
||||
- controller->tx_bytes = 0;
|
||||
- spin_unlock_irqrestore(&controller->lock, flags);
|
||||
-
|
||||
-
|
||||
- if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
||||
- dev_err(controller->dev, "cannot set RESET state\n");
|
||||
- return -EIO;
|
||||
- }
|
||||
-
|
||||
- switch (controller->mode) {
|
||||
- case QUP_IO_M_MODE_FIFO:
|
||||
- writel_relaxed(controller->n_words,
|
||||
- controller->base + QUP_MX_READ_CNT);
|
||||
- writel_relaxed(controller->n_words,
|
||||
- controller->base + QUP_MX_WRITE_CNT);
|
||||
- /* must be zero for FIFO */
|
||||
- writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||
- writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
- break;
|
||||
- case QUP_IO_M_MODE_BAM:
|
||||
- writel_relaxed(controller->n_words,
|
||||
- controller->base + QUP_MX_INPUT_CNT);
|
||||
- writel_relaxed(controller->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);
|
||||
- if (!controller->qup_v1) {
|
||||
- void __iomem *input_cnt;
|
||||
-
|
||||
- input_cnt = controller->base + QUP_MX_INPUT_CNT;
|
||||
- /*
|
||||
- * for DMA transfers, both QUP_MX_INPUT_CNT and
|
||||
- * QUP_MX_OUTPUT_CNT must be zero to all cases
|
||||
- * but one. That case is a non-balanced
|
||||
- * transfer when there is only a rx_buf.
|
||||
- */
|
||||
- if (xfer->tx_buf)
|
||||
- writel_relaxed(0, input_cnt);
|
||||
- else
|
||||
- writel_relaxed(controller->n_words,
|
||||
- input_cnt);
|
||||
-
|
||||
- writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
- }
|
||||
- break;
|
||||
- case QUP_IO_M_MODE_BLOCK:
|
||||
- writel_relaxed(controller->n_words,
|
||||
- controller->base + QUP_MX_INPUT_CNT);
|
||||
- writel_relaxed(controller->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);
|
||||
- break;
|
||||
- default:
|
||||
- dev_err(controller->dev, "unknown mode = %d\n",
|
||||
- controller->mode);
|
||||
- return -EIO;
|
||||
- }
|
||||
-
|
||||
- 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);
|
||||
-
|
||||
- if (!spi_qup_is_dma_xfer(controller->mode))
|
||||
- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
||||
- else
|
||||
- iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
|
||||
-
|
||||
- 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);
|
||||
-
|
||||
- control = readl_relaxed(controller->base + SPI_IO_CONTROL);
|
||||
-
|
||||
- if (spi->mode & SPI_CPOL)
|
||||
- control |= SPI_IO_C_CLK_IDLE_HIGH;
|
||||
- else
|
||||
- control &= ~SPI_IO_C_CLK_IDLE_HIGH;
|
||||
-
|
||||
- writel_relaxed(control, controller->base + SPI_IO_CONTROL);
|
||||
-
|
||||
- config = readl_relaxed(controller->base + SPI_CONFIG);
|
||||
-
|
||||
- if (spi->mode & SPI_LOOP)
|
||||
- config |= SPI_CONFIG_LOOPBACK;
|
||||
- else
|
||||
- config &= ~SPI_CONFIG_LOOPBACK;
|
||||
-
|
||||
- if (spi->mode & SPI_CPHA)
|
||||
- config &= ~SPI_CONFIG_INPUT_FIRST;
|
||||
- else
|
||||
- config |= SPI_CONFIG_INPUT_FIRST;
|
||||
-
|
||||
- /*
|
||||
- * HS_MODE improves signal stability for spi-clk high rates,
|
||||
- * but is invalid in loop back mode.
|
||||
- */
|
||||
- if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
|
||||
- config |= SPI_CONFIG_HS_MODE;
|
||||
- else
|
||||
- config &= ~SPI_CONFIG_HS_MODE;
|
||||
-
|
||||
- writel_relaxed(config, controller->base + SPI_CONFIG);
|
||||
-
|
||||
- config = readl_relaxed(controller->base + QUP_CONFIG);
|
||||
- config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
|
||||
- config |= xfer->bits_per_word - 1;
|
||||
- config |= QUP_CONFIG_SPI_MODE;
|
||||
-
|
||||
- if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
- 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 */
|
||||
- if (!controller->qup_v1) {
|
||||
- u32 mask = 0;
|
||||
-
|
||||
- /*
|
||||
- * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
|
||||
- * status change in BAM mode
|
||||
- */
|
||||
-
|
||||
- if (spi_qup_is_dma_xfer(controller->mode))
|
||||
- mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
|
||||
-
|
||||
- writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int spi_qup_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
@@ -784,18 +793,14 @@ static int spi_qup_transfer_one(struct s
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = spi_qup_io_config(spi, xfer);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
|
||||
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
|
||||
timeout = 100 * msecs_to_jiffies(timeout);
|
||||
|
||||
if (spi_qup_is_dma_xfer(controller->mode))
|
||||
- ret = spi_qup_do_dma(master, xfer, timeout);
|
||||
+ ret = spi_qup_do_dma(spi, xfer, timeout);
|
||||
else
|
||||
- ret = spi_qup_do_pio(master, xfer, timeout);
|
||||
+ ret = spi_qup_do_pio(spi, xfer, timeout);
|
||||
|
||||
if (ret)
|
||||
goto exit;
|
|
@ -0,0 +1,268 @@
|
|||
From 05a08cc5620df0fcf8e260feee04b9671705723e Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Tue, 26 Apr 2016 15:46:24 -0500
|
||||
Subject: [PATCH 14/37] spi: qup: allow block mode to generate multiple
|
||||
transactions
|
||||
|
||||
This let's you write more to the SPI bus than 64K-1 which is important
|
||||
if the block size of a SPI device is >= 64K or some other device wants
|
||||
to something larger.
|
||||
|
||||
This has the benefit of completly removing spi_message from the spi-qup
|
||||
transactions
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 120 ++++++++++++++++++++++++++++++-------------------
|
||||
1 file changed, 75 insertions(+), 45 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -120,7 +120,7 @@
|
||||
|
||||
#define SPI_NUM_CHIPSELECTS 4
|
||||
|
||||
-#define SPI_MAX_DMA_XFER (SZ_64K - 64)
|
||||
+#define SPI_MAX_XFER (SZ_64K - 64)
|
||||
|
||||
/* high speed mode is when bus rate is greater then 26MHz */
|
||||
#define SPI_HS_MIN_RATE 26000000
|
||||
@@ -150,6 +150,8 @@ struct spi_qup {
|
||||
int n_words;
|
||||
int tx_bytes;
|
||||
int rx_bytes;
|
||||
+ const u8 *tx_buf;
|
||||
+ u8 *rx_buf;
|
||||
int qup_v1;
|
||||
|
||||
int mode;
|
||||
@@ -172,6 +174,12 @@ static inline bool spi_qup_is_dma_xfer(i
|
||||
return false;
|
||||
}
|
||||
|
||||
+/* get's the transaction size length */
|
||||
+static inline unsigned spi_qup_len(struct spi_qup *controller)
|
||||
+{
|
||||
+ return controller->n_words * controller->w_size;
|
||||
+}
|
||||
+
|
||||
static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
|
||||
{
|
||||
u32 opstate = readl_relaxed(controller->base + QUP_STATE);
|
||||
@@ -224,10 +232,9 @@ static int spi_qup_set_state(struct spi_
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void spi_qup_read_from_fifo(struct spi_qup *controller,
|
||||
- struct spi_transfer *xfer, u32 num_words)
|
||||
+static void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words)
|
||||
{
|
||||
- u8 *rx_buf = xfer->rx_buf;
|
||||
+ u8 *rx_buf = controller->rx_buf;
|
||||
int i, shift, num_bytes;
|
||||
u32 word;
|
||||
|
||||
@@ -235,7 +242,7 @@ static void spi_qup_read_from_fifo(struc
|
||||
|
||||
word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
||||
|
||||
- num_bytes = min_t(int, xfer->len - controller->rx_bytes,
|
||||
+ num_bytes = min_t(int, spi_qup_len(controller) - controller->rx_bytes,
|
||||
controller->w_size);
|
||||
|
||||
if (!rx_buf) {
|
||||
@@ -257,13 +264,12 @@ static void spi_qup_read_from_fifo(struc
|
||||
}
|
||||
}
|
||||
|
||||
-static void spi_qup_read(struct spi_qup *controller,
|
||||
- struct spi_transfer *xfer)
|
||||
+static void spi_qup_read(struct spi_qup *controller)
|
||||
{
|
||||
u32 remainder, words_per_block, num_words;
|
||||
bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
|
||||
|
||||
- remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes,
|
||||
+ remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->rx_bytes,
|
||||
controller->w_size);
|
||||
words_per_block = controller->in_blk_sz >> 2;
|
||||
|
||||
@@ -284,7 +290,7 @@ static void spi_qup_read(struct spi_qup
|
||||
}
|
||||
|
||||
/* read up to the maximum transfer size available */
|
||||
- spi_qup_read_from_fifo(controller, xfer, num_words);
|
||||
+ spi_qup_read_from_fifo(controller, num_words);
|
||||
|
||||
remainder -= num_words;
|
||||
|
||||
@@ -306,17 +312,16 @@ static void spi_qup_read(struct spi_qup
|
||||
|
||||
}
|
||||
|
||||
-static void spi_qup_write_to_fifo(struct spi_qup *controller,
|
||||
- struct spi_transfer *xfer, u32 num_words)
|
||||
+static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
|
||||
{
|
||||
- const u8 *tx_buf = xfer->tx_buf;
|
||||
+ const u8 *tx_buf = controller->tx_buf;
|
||||
int i, num_bytes;
|
||||
u32 word, data;
|
||||
|
||||
for (; num_words; num_words--) {
|
||||
word = 0;
|
||||
|
||||
- num_bytes = min_t(int, xfer->len - controller->tx_bytes,
|
||||
+ num_bytes = min_t(int, spi_qup_len(controller) - controller->tx_bytes,
|
||||
controller->w_size);
|
||||
if (tx_buf)
|
||||
for (i = 0; i < num_bytes; i++) {
|
||||
@@ -337,13 +342,12 @@ static void spi_qup_dma_done(void *data)
|
||||
complete(done);
|
||||
}
|
||||
|
||||
-static void spi_qup_write(struct spi_qup *controller,
|
||||
- struct spi_transfer *xfer)
|
||||
+static void spi_qup_write(struct spi_qup *controller)
|
||||
{
|
||||
bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
|
||||
u32 remainder, words_per_block, num_words;
|
||||
|
||||
- remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes,
|
||||
+ remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->tx_bytes,
|
||||
controller->w_size);
|
||||
words_per_block = controller->out_blk_sz >> 2;
|
||||
|
||||
@@ -363,7 +367,7 @@ static void spi_qup_write(struct spi_qup
|
||||
num_words = 1;
|
||||
}
|
||||
|
||||
- spi_qup_write_to_fifo(controller, xfer, num_words);
|
||||
+ spi_qup_write_to_fifo(controller, num_words);
|
||||
|
||||
remainder -= num_words;
|
||||
|
||||
@@ -629,35 +633,61 @@ static int spi_qup_do_pio(struct spi_dev
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
- int ret;
|
||||
+ int ret, n_words, iterations, offset = 0;
|
||||
|
||||
- ret = spi_qup_io_config(spi, xfer);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
+ n_words = qup->n_words;
|
||||
+ iterations = n_words / SPI_MAX_XFER; /* round down */
|
||||
|
||||
- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
- if (ret) {
|
||||
- dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
- return ret;
|
||||
- }
|
||||
+ qup->rx_buf = xfer->rx_buf;
|
||||
+ qup->tx_buf = xfer->tx_buf;
|
||||
|
||||
- ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
|
||||
- if (ret) {
|
||||
- dev_warn(qup->dev, "cannot set PAUSE state\n");
|
||||
- return ret;
|
||||
- }
|
||||
+ do {
|
||||
+ if (iterations)
|
||||
+ qup->n_words = SPI_MAX_XFER;
|
||||
+ else
|
||||
+ qup->n_words = n_words % SPI_MAX_XFER;
|
||||
+
|
||||
+ if (qup->tx_buf && offset)
|
||||
+ qup->tx_buf = xfer->tx_buf + offset * SPI_MAX_XFER;
|
||||
+
|
||||
+ if (qup->rx_buf && offset)
|
||||
+ qup->rx_buf = xfer->rx_buf + offset * SPI_MAX_XFER;
|
||||
+
|
||||
+ /* if the transaction is small enough, we need
|
||||
+ * to fallback to FIFO mode */
|
||||
+ if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
|
||||
+ qup->mode = QUP_IO_M_MODE_FIFO;
|
||||
|
||||
- if (qup->mode == QUP_IO_M_MODE_FIFO)
|
||||
- spi_qup_write(qup, xfer);
|
||||
+ ret = spi_qup_io_config(spi, xfer);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
- if (ret) {
|
||||
- dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
- return ret;
|
||||
- }
|
||||
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
+ if (ret) {
|
||||
+ dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
- if (!wait_for_completion_timeout(&qup->done, timeout))
|
||||
- return -ETIMEDOUT;
|
||||
+ ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
|
||||
+ if (ret) {
|
||||
+ dev_warn(qup->dev, "cannot set PAUSE state\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if (qup->mode == QUP_IO_M_MODE_FIFO)
|
||||
+ spi_qup_write(qup);
|
||||
+
|
||||
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
+ if (ret) {
|
||||
+ dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if (!wait_for_completion_timeout(&qup->done, timeout))
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ offset++;
|
||||
+ } while (iterations--);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -722,17 +752,17 @@ static irqreturn_t spi_qup_qup_irq(int i
|
||||
complete(&controller->dma_tx_done);
|
||||
} else {
|
||||
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
||||
- spi_qup_read(controller, xfer);
|
||||
+ spi_qup_read(controller);
|
||||
|
||||
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||
- spi_qup_write(controller, xfer);
|
||||
+ spi_qup_write(controller);
|
||||
}
|
||||
|
||||
/* re-read opflags as flags may have changed due to actions above */
|
||||
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||
opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
|
||||
- if ((controller->rx_bytes == xfer->len &&
|
||||
+ if ((controller->rx_bytes == spi_qup_len(controller) &&
|
||||
(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
|
||||
done = true;
|
||||
|
||||
@@ -794,7 +824,7 @@ static int spi_qup_transfer_one(struct s
|
||||
return ret;
|
||||
|
||||
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
|
||||
- timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
|
||||
+ timeout = DIV_ROUND_UP(min_t(unsigned long, SPI_MAX_XFER, xfer->len) * 8, timeout);
|
||||
timeout = 100 * msecs_to_jiffies(timeout);
|
||||
|
||||
if (spi_qup_is_dma_xfer(controller->mode))
|
||||
@@ -983,7 +1013,7 @@ static int spi_qup_probe(struct platform
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->auto_runtime_pm = true;
|
||||
master->dma_alignment = dma_get_cache_alignment();
|
||||
- master->max_dma_len = SPI_MAX_DMA_XFER;
|
||||
+ master->max_dma_len = SPI_MAX_XFER;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
From a24914d34a4c6df4323c6d98950166600da79bc6 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Wed, 4 May 2016 16:33:42 -0500
|
||||
Subject: [PATCH 15/37] spi: qup: refactor spi_qup_prep_sg to be more take
|
||||
specific sgl and nent
|
||||
|
||||
This is in preparation for splitting DMA into multiple transacations,
|
||||
this contains no code changes just refactoring.
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 28 +++++++++++-----------------
|
||||
1 file changed, 11 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -379,27 +379,19 @@ static void spi_qup_write(struct spi_qup
|
||||
} while (remainder);
|
||||
}
|
||||
|
||||
-static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
|
||||
- enum dma_transfer_direction dir,
|
||||
- dma_async_tx_callback callback,
|
||||
- void *data)
|
||||
+static int spi_qup_prep_sg(struct spi_master *master, struct scatterlist *sgl,
|
||||
+ unsigned int nents, enum dma_transfer_direction dir,
|
||||
+ dma_async_tx_callback callback, void *data)
|
||||
{
|
||||
unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
- struct scatterlist *sgl;
|
||||
struct dma_chan *chan;
|
||||
dma_cookie_t cookie;
|
||||
- unsigned int nents;
|
||||
|
||||
- if (dir == DMA_MEM_TO_DEV) {
|
||||
+ if (dir == DMA_MEM_TO_DEV)
|
||||
chan = master->dma_tx;
|
||||
- nents = xfer->tx_sg.nents;
|
||||
- sgl = xfer->tx_sg.sgl;
|
||||
- } else {
|
||||
+ else
|
||||
chan = master->dma_rx;
|
||||
- nents = xfer->rx_sg.nents;
|
||||
- sgl = xfer->rx_sg.sgl;
|
||||
- }
|
||||
|
||||
desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
|
||||
if (IS_ERR_OR_NULL(desc))
|
||||
@@ -602,8 +594,9 @@ unsigned long timeout)
|
||||
}
|
||||
|
||||
if (xfer->rx_buf) {
|
||||
- ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
|
||||
- &qup->done);
|
||||
+ ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
|
||||
+ xfer->rx_sg.nents, DMA_DEV_TO_MEM,
|
||||
+ rx_done, &qup->done);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -611,8 +604,9 @@ unsigned long timeout)
|
||||
}
|
||||
|
||||
if (xfer->tx_buf) {
|
||||
- ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done,
|
||||
- &qup->dma_tx_done);
|
||||
+ ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
|
||||
+ xfer->tx_sg.nents, DMA_MEM_TO_DEV,
|
||||
+ tx_done, &qup->dma_tx_done);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
From 6b2bb8803f19116bad41a271f9035d4c853f4553 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 5 May 2016 10:07:11 -0500
|
||||
Subject: [PATCH 16/37] spi: qup: allow mulitple DMA transactions per spi xfer
|
||||
|
||||
Much like the block mode changes, we are breaking up DMA transactions
|
||||
into 64K chunks so we can reset the QUP engine.
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 120 +++++++++++++++++++++++++++++++++++--------------
|
||||
1 file changed, 86 insertions(+), 34 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -566,6 +566,21 @@ static int spi_qup_io_config(struct spi_
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static unsigned int spi_qup_sgl_get_size(struct scatterlist *sgl, unsigned int nents)
|
||||
+{
|
||||
+ struct scatterlist *sg;
|
||||
+ int i;
|
||||
+ unsigned int length = 0;
|
||||
+
|
||||
+ if (!nents)
|
||||
+ return 0;
|
||||
+
|
||||
+ for_each_sg(sgl, sg, nents, i)
|
||||
+ length += sg_dma_len(sg);
|
||||
+
|
||||
+ return length;
|
||||
+}
|
||||
+
|
||||
static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
|
||||
unsigned long timeout)
|
||||
{
|
||||
@@ -573,53 +588,90 @@ unsigned long timeout)
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
|
||||
int ret;
|
||||
+ struct scatterlist *tx_sgl, *rx_sgl;
|
||||
|
||||
- ret = spi_qup_io_config(spi, xfer);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- /* before issuing the descriptors, set the QUP to run */
|
||||
- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
- if (ret) {
|
||||
- dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- if (!qup->qup_v1) {
|
||||
- if (xfer->rx_buf)
|
||||
- rx_done = spi_qup_dma_done;
|
||||
-
|
||||
- if (xfer->tx_buf)
|
||||
- tx_done = spi_qup_dma_done;
|
||||
- }
|
||||
-
|
||||
- if (xfer->rx_buf) {
|
||||
- ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
|
||||
- xfer->rx_sg.nents, DMA_DEV_TO_MEM,
|
||||
- rx_done, &qup->done);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
+ rx_sgl = xfer->rx_sg.sgl;
|
||||
+ tx_sgl = xfer->tx_sg.sgl;
|
||||
|
||||
- dma_async_issue_pending(master->dma_rx);
|
||||
- }
|
||||
+ do {
|
||||
+ int rx_nents = 0, tx_nents = 0;
|
||||
|
||||
- if (xfer->tx_buf) {
|
||||
- ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
|
||||
- xfer->tx_sg.nents, DMA_MEM_TO_DEV,
|
||||
- tx_done, &qup->dma_tx_done);
|
||||
+ if (rx_sgl) {
|
||||
+ rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
|
||||
+ if (rx_nents < 0)
|
||||
+ rx_nents = sg_nents(rx_sgl);
|
||||
+
|
||||
+ qup->n_words = spi_qup_sgl_get_size(rx_sgl, rx_nents) /
|
||||
+ qup->w_size;
|
||||
+ }
|
||||
+
|
||||
+ if (tx_sgl) {
|
||||
+ tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
|
||||
+ if (tx_nents < 0)
|
||||
+ tx_nents = sg_nents(tx_sgl);
|
||||
+
|
||||
+ qup->n_words = spi_qup_sgl_get_size(tx_sgl, tx_nents) /
|
||||
+ qup->w_size;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ ret = spi_qup_io_config(spi, xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- dma_async_issue_pending(master->dma_tx);
|
||||
- }
|
||||
+ /* before issuing the descriptors, set the QUP to run */
|
||||
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
+ if (ret) {
|
||||
+ dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ if (!qup->qup_v1) {
|
||||
+ if (rx_sgl) {
|
||||
+ rx_done = spi_qup_dma_done;
|
||||
+ }
|
||||
+
|
||||
+ if (tx_sgl) {
|
||||
+ tx_done = spi_qup_dma_done;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (rx_sgl) {
|
||||
+ ret = spi_qup_prep_sg(master, rx_sgl, rx_nents,
|
||||
+ DMA_DEV_TO_MEM, rx_done,
|
||||
+ &qup->done);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ dma_async_issue_pending(master->dma_rx);
|
||||
+ }
|
||||
+
|
||||
+ if (tx_sgl) {
|
||||
+ ret = spi_qup_prep_sg(master, tx_sgl, tx_nents,
|
||||
+ DMA_MEM_TO_DEV, tx_done,
|
||||
+ &qup->dma_tx_done);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ dma_async_issue_pending(master->dma_tx);
|
||||
+ }
|
||||
+
|
||||
+ if (rx_sgl && !wait_for_completion_timeout(&qup->done, timeout)) {
|
||||
+ pr_emerg(" rx timed out");
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ if (tx_sgl && !wait_for_completion_timeout(&qup->dma_tx_done, timeout)) {
|
||||
+ pr_emerg(" tx timed out\n");
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
|
||||
- if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout))
|
||||
- return -ETIMEDOUT;
|
||||
+ for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl));
|
||||
+ for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl));
|
||||
|
||||
- if (xfer->tx_buf && !wait_for_completion_timeout(&qup->dma_tx_done, timeout))
|
||||
- ret = -ETIMEDOUT;
|
||||
+ } while (rx_sgl || tx_sgl);
|
||||
|
||||
- return ret;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
|
|
@ -0,0 +1,86 @@
|
|||
From 5ffa559b35dd90469e1f7fc21a77c6a2d5a8ca0f Mon Sep 17 00:00:00 2001
|
||||
From: Varadarajan Narayanan <varada@codeaurora.org>
|
||||
Date: Wed, 25 May 2016 13:40:03 +0530
|
||||
Subject: [PATCH 17/37] spi: qup: Fix sg nents calculation
|
||||
|
||||
lib/scatterlist.c:sg_nents_for_len() returns the number of SG
|
||||
entries that total up to greater than or equal to the given
|
||||
length. However, the spi-qup driver assumed that the returned
|
||||
nents is for a total less than or equal to the given length. The
|
||||
spi-qup driver requests nents for SPI_MAX_XFER, however the API
|
||||
returns nents for SPI_MAX_XFER+delta (actually SZ_64K).
|
||||
|
||||
Based on this, spi_qup_do_dma() calculates n_words and programs
|
||||
that into QUP_MX_{IN|OUT}PUT_CNT register. The calculated
|
||||
n_words value is more than the maximum value that can fit in the
|
||||
the 16-bit COUNT field of the QUP_MX_{IN|OUT}PUT_CNT register.
|
||||
And, the field gets programmed to zero. Since the COUNT field is
|
||||
zero, the i/o doesn't start eventually resulting in the i/o
|
||||
timing out.
|
||||
|
||||
Signed-off-by: Varadarajan Narayanan <varada@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 36 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -581,6 +581,38 @@ static unsigned int spi_qup_sgl_get_size
|
||||
return length;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * spi_qup_sg_nents_for_len - return total count of entries in scatterlist
|
||||
+ * needed to satisfy the supplied length
|
||||
+ * @sg: The scatterlist
|
||||
+ * @len: The total required length
|
||||
+ *
|
||||
+ * Description:
|
||||
+ * Determines the number of entries in sg that sum upto a maximum of
|
||||
+ * the supplied length, taking into acount chaining as well
|
||||
+ *
|
||||
+ * Returns:
|
||||
+ * the number of sg entries needed, negative error on failure
|
||||
+ *
|
||||
+ **/
|
||||
+int spi_qup_sg_nents_for_len(struct scatterlist *sg, u64 len)
|
||||
+{
|
||||
+ int nents;
|
||||
+ u64 total;
|
||||
+
|
||||
+ if (!len)
|
||||
+ return 0;
|
||||
+
|
||||
+ for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
|
||||
+ nents++;
|
||||
+ total += sg_dma_len(sg);
|
||||
+ if (total > len)
|
||||
+ return (nents - 1);
|
||||
+ }
|
||||
+
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
|
||||
unsigned long timeout)
|
||||
{
|
||||
@@ -597,7 +629,8 @@ unsigned long timeout)
|
||||
int rx_nents = 0, tx_nents = 0;
|
||||
|
||||
if (rx_sgl) {
|
||||
- rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
|
||||
+ rx_nents = spi_qup_sg_nents_for_len(rx_sgl,
|
||||
+ SPI_MAX_XFER);
|
||||
if (rx_nents < 0)
|
||||
rx_nents = sg_nents(rx_sgl);
|
||||
|
||||
@@ -606,7 +639,8 @@ unsigned long timeout)
|
||||
}
|
||||
|
||||
if (tx_sgl) {
|
||||
- tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
|
||||
+ tx_nents = spi_qup_sg_nents_for_len(tx_sgl,
|
||||
+ SPI_MAX_XFER);
|
||||
if (tx_nents < 0)
|
||||
tx_nents = sg_nents(tx_sgl);
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
From 94aa51061597d9db86f3ad4d54eb4a560fa66f2f Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Wed, 13 Apr 2016 14:03:14 -0500
|
||||
Subject: [PATCH 26/37] cpufreq: dt: qcom: ipq4019: Add compat for qcom
|
||||
ipq4019
|
||||
|
||||
Instantiate cpufreq-dt-platdev driver for ipq4019 to support changing
|
||||
CPU frequencies.
|
||||
|
||||
This depends on Viresh Kumar's patches in this series:
|
||||
http://comments.gmane.org/gmane.linux.power-management.general/73887
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/cpufreq/cpufreq-dt-platdev.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
|
||||
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
|
||||
@@ -35,6 +35,8 @@ static const struct of_device_id machine
|
||||
|
||||
{ .compatible = "marvell,berlin", },
|
||||
|
||||
+ { .compatible = "qcom,ipq4019", },
|
||||
+
|
||||
{ .compatible = "samsung,exynos3250", },
|
||||
{ .compatible = "samsung,exynos4210", },
|
||||
{ .compatible = "samsung,exynos4212", },
|
|
@ -0,0 +1,33 @@
|
|||
From 4f0328557a670d481f2609474b56890c28ab4694 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
Date: Thu, 28 Apr 2016 12:55:08 -0500
|
||||
Subject: [PATCH 27/37] clk: ipq4019: report accurate fixed clock rates
|
||||
|
||||
This looks like a copy-and-paste gone wrong, but update all
|
||||
the fixed clock rates to report the correct values.
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq4019.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq4019.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq4019.c
|
||||
@@ -1317,12 +1317,12 @@ static int gcc_ipq4019_probe(struct plat
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
- clk_register_fixed_rate(dev, "fepll125", "xo", 0, 200000000);
|
||||
- clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 200000000);
|
||||
- clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 200000000);
|
||||
- clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 200000000);
|
||||
+ clk_register_fixed_rate(dev, "fepll125", "xo", 0, 125000000);
|
||||
+ clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 125000000);
|
||||
+ clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 250000000);
|
||||
+ clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 250000000);
|
||||
clk_register_fixed_rate(dev, "fepll200", "xo", 0, 200000000);
|
||||
- clk_register_fixed_rate(dev, "fepll500", "xo", 0, 200000000);
|
||||
+ clk_register_fixed_rate(dev, "fepll500", "xo", 0, 500000000);
|
||||
clk_register_fixed_rate(dev, "ddrpllapss", "xo", 0, 666000000);
|
||||
|
||||
return qcom_cc_probe(pdev, &gcc_ipq4019_desc);
|
|
@ -0,0 +1,77 @@
|
|||
From ce6cb947b9535b2d81717925cb6a3bc9f8500f44 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 17 Mar 2016 15:01:09 -0500
|
||||
Subject: [PATCH 28/37] qcom: ipq4019: add cpu operating points for cpufreq
|
||||
support
|
||||
|
||||
This adds some operating points for cpu frequeny scaling
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 34 ++++++++++++++++++++++++++--------
|
||||
1 file changed, 26 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
@@ -40,14 +40,7 @@
|
||||
reg = <0x0>;
|
||||
clocks = <&gcc GCC_APPS_CLK_SRC>;
|
||||
clock-frequency = <0>;
|
||||
- operating-points = <
|
||||
- /* kHz uV (fixed) */
|
||||
- 48000 1100000
|
||||
- 200000 1100000
|
||||
- 500000 1100000
|
||||
- 666000 1100000
|
||||
- >;
|
||||
- clock-latency = <256000>;
|
||||
+ operating-points-v2 = <&cpu0_opp_table>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
@@ -59,6 +52,7 @@
|
||||
reg = <0x1>;
|
||||
clocks = <&gcc GCC_APPS_CLK_SRC>;
|
||||
clock-frequency = <0>;
|
||||
+ operating-points-v2 = <&cpu0_opp_table>;
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
@@ -70,6 +64,7 @@
|
||||
reg = <0x2>;
|
||||
clocks = <&gcc GCC_APPS_CLK_SRC>;
|
||||
clock-frequency = <0>;
|
||||
+ operating-points-v2 = <&cpu0_opp_table>;
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
@@ -81,6 +76,29 @@
|
||||
reg = <0x3>;
|
||||
clocks = <&gcc GCC_APPS_CLK_SRC>;
|
||||
clock-frequency = <0>;
|
||||
+ operating-points-v2 = <&cpu0_opp_table>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ cpu0_opp_table: opp_table0 {
|
||||
+ compatible = "operating-points-v2";
|
||||
+ opp-shared;
|
||||
+
|
||||
+ opp@48000000 {
|
||||
+ opp-hz = /bits/ 64 <48000000>;
|
||||
+ clock-latency-ns = <256000>;
|
||||
+ };
|
||||
+ opp@200000000 {
|
||||
+ opp-hz = /bits/ 64 <200000000>;
|
||||
+ clock-latency-ns = <256000>;
|
||||
+ };
|
||||
+ opp@500000000 {
|
||||
+ opp-hz = /bits/ 64 <500000000>;
|
||||
+ clock-latency-ns = <256000>;
|
||||
+ };
|
||||
+ opp@666000000 {
|
||||
+ opp-hz = /bits/ 64 <666000000>;
|
||||
+ clock-latency-ns = <256000>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
From f033e344b8a02e98beaf16c1eb188bc175219756 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 28 Mar 2016 11:16:51 -0500
|
||||
Subject: [PATCH 29/37] qcom: ipq4019: turn on DMA for i2c
|
||||
|
||||
These are the required nodes for i2c-qup to use DMA
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
@@ -179,6 +179,8 @@
|
||||
clock-names = "iface", "core";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
+ dmas = <&blsp_dma 9>, <&blsp_dma 8>;
|
||||
+ dma-names = "rx", "tx";
|
||||
status = "disabled";
|
||||
};
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
From 79f698655871f2eeceb1f9051e60d97bc434f52f Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
Date: Fri, 29 Apr 2016 12:48:02 -0500
|
||||
Subject: [PATCH 30/37] qcom: ipq4019: use correct clock for i2c bus 0
|
||||
|
||||
For the record the mapping is as follows:
|
||||
|
||||
QUP0 = SPI QUP1
|
||||
QUP1 = SPI QUP2
|
||||
QUP2 = I2C QUP1
|
||||
QUP3 = I2C QUP2
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
@@ -175,7 +175,7 @@
|
||||
reg = <0x78b7000 0x6000>;
|
||||
interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&gcc GCC_BLSP1_AHB_CLK>,
|
||||
- <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
|
||||
+ <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
|
||||
clock-names = "iface", "core";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
|
@ -0,0 +1,23 @@
|
|||
From 20249a0d746265a6663208fa768614784553edac Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 11 Apr 2016 14:49:12 -0500
|
||||
Subject: [PATCH 31/37] qcom: ipq4019: enable DMA for spi
|
||||
|
||||
These are the required nodes for spi-qup to use DMA
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
@@ -167,6 +167,8 @@
|
||||
clock-names = "core", "iface";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
+ dmas = <&blsp_dma 5>, <&blsp_dma 4>;
|
||||
+ dma-names = "rx", "tx";
|
||||
status = "disabled";
|
||||
};
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
From 4d7fe4171b01cbfc01e4a00f44b3e7f7a8013eb3 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Fri, 8 Apr 2016 15:26:10 -0500
|
||||
Subject: [PATCH 32/37] qcom: ipq4019: use v2 of the kpss bringup mechanism
|
||||
|
||||
v1 was the incorrect choice here and sometimes the board would not come
|
||||
up properly
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 32 ++++++++++++++++++++++++--------
|
||||
1 file changed, 24 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
@@ -34,7 +34,8 @@
|
||||
cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
- enable-method = "qcom,kpss-acc-v1";
|
||||
+ enable-method = "qcom,kpss-acc-v2";
|
||||
+ next-level-cache = <&L2>;
|
||||
qcom,acc = <&acc0>;
|
||||
qcom,saw = <&saw0>;
|
||||
reg = <0x0>;
|
||||
@@ -46,7 +47,8 @@
|
||||
cpu@1 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
- enable-method = "qcom,kpss-acc-v1";
|
||||
+ enable-method = "qcom,kpss-acc-v2";
|
||||
+ next-level-cache = <&L2>;
|
||||
qcom,acc = <&acc1>;
|
||||
qcom,saw = <&saw1>;
|
||||
reg = <0x1>;
|
||||
@@ -58,7 +60,8 @@
|
||||
cpu@2 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
- enable-method = "qcom,kpss-acc-v1";
|
||||
+ enable-method = "qcom,kpss-acc-v2";
|
||||
+ next-level-cache = <&L2>;
|
||||
qcom,acc = <&acc2>;
|
||||
qcom,saw = <&saw2>;
|
||||
reg = <0x2>;
|
||||
@@ -70,7 +73,8 @@
|
||||
cpu@3 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a7";
|
||||
- enable-method = "qcom,kpss-acc-v1";
|
||||
+ enable-method = "qcom,kpss-acc-v2";
|
||||
+ next-level-cache = <&L2>;
|
||||
qcom,acc = <&acc3>;
|
||||
qcom,saw = <&saw3>;
|
||||
reg = <0x3>;
|
||||
@@ -100,6 +104,12 @@
|
||||
opp-hz = /bits/ 64 <666000000>;
|
||||
clock-latency-ns = <256000>;
|
||||
};
|
||||
+
|
||||
+ L2: l2-cache {
|
||||
+ compatible = "qcom,arch-cache";
|
||||
+ cache-level = <2>;
|
||||
+ qcom,saw = <&saw_l2>;
|
||||
+ };
|
||||
};
|
||||
|
||||
pmu {
|
||||
@@ -212,22 +222,22 @@
|
||||
};
|
||||
|
||||
acc0: clock-controller@b088000 {
|
||||
- compatible = "qcom,kpss-acc-v1";
|
||||
+ compatible = "qcom,kpss-acc-v2";
|
||||
reg = <0x0b088000 0x1000>, <0xb008000 0x1000>;
|
||||
};
|
||||
|
||||
acc1: clock-controller@b098000 {
|
||||
- compatible = "qcom,kpss-acc-v1";
|
||||
+ compatible = "qcom,kpss-acc-v2";
|
||||
reg = <0x0b098000 0x1000>, <0xb008000 0x1000>;
|
||||
};
|
||||
|
||||
acc2: clock-controller@b0a8000 {
|
||||
- compatible = "qcom,kpss-acc-v1";
|
||||
+ compatible = "qcom,kpss-acc-v2";
|
||||
reg = <0x0b0a8000 0x1000>, <0xb008000 0x1000>;
|
||||
};
|
||||
|
||||
acc3: clock-controller@b0b8000 {
|
||||
- compatible = "qcom,kpss-acc-v1";
|
||||
+ compatible = "qcom,kpss-acc-v2";
|
||||
reg = <0x0b0b8000 0x1000>, <0xb008000 0x1000>;
|
||||
};
|
||||
|
||||
@@ -255,6 +265,12 @@
|
||||
regulator;
|
||||
};
|
||||
|
||||
+ saw_l2: regulator@b012000 {
|
||||
+ compatible = "qcom,saw2";
|
||||
+ reg = <0xb012000 0x1000>;
|
||||
+ regulator;
|
||||
+ };
|
||||
+
|
||||
serial@78af000 {
|
||||
compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
|
||||
reg = <0x78af000 0x200>;
|
|
@ -0,0 +1,28 @@
|
|||
From 25ee3761c072037b48246b992c3aec671c77a406 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Wed, 4 May 2016 12:25:41 -0700
|
||||
Subject: [PATCH 33/37] dts: ipq4019: support ARMv7 PMU
|
||||
|
||||
Add support for cortex-a7-pmu present on ipq4019 SoCs.
|
||||
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
@@ -118,6 +118,12 @@
|
||||
IRQ_TYPE_LEVEL_HIGH)>;
|
||||
};
|
||||
|
||||
+ pmu {
|
||||
+ compatible = "arm,cortex-a7-pmu";
|
||||
+ interrupts = <GIC_PPI 7 (GIC_CPU_MASK_SIMPLE(4) |
|
||||
+ IRQ_TYPE_LEVEL_HIGH)>;
|
||||
+ };
|
||||
+
|
||||
clocks {
|
||||
sleep_clk: sleep_clk {
|
||||
compatible = "fixed-clock";
|
|
@ -0,0 +1,488 @@
|
|||
From e8d6fd46f5f3c5860fa7fa98004de9bd97c0d869 Mon Sep 17 00:00:00 2001
|
||||
From: Senthilkumar N L <snlakshm@codeaurora.org>
|
||||
Date: Tue, 6 Jan 2015 12:52:23 +0530
|
||||
Subject: [PATCH 34/37] qcom: ipq4019: Add IPQ4019 USB HS/SS PHY drivers
|
||||
|
||||
These drivers handles control and configuration of the HS
|
||||
and SS USB PHY transceivers.
|
||||
|
||||
Signed-off-by: Senthilkumar N L <snlakshm@codeaurora.org>
|
||||
---
|
||||
drivers/usb/phy/Kconfig | 11 ++
|
||||
drivers/usb/phy/Makefile | 2 +
|
||||
drivers/usb/phy/phy-qca-baldur.c | 262 ++++++++++++++++++++++++++++++++++++++
|
||||
drivers/usb/phy/phy-qca-uniphy.c | 171 +++++++++++++++++++++++++
|
||||
4 files changed, 446 insertions(+)
|
||||
create mode 100644 drivers/usb/phy/phy-qca-baldur.c
|
||||
create mode 100644 drivers/usb/phy/phy-qca-uniphy.c
|
||||
|
||||
--- a/drivers/usb/phy/Kconfig
|
||||
+++ b/drivers/usb/phy/Kconfig
|
||||
@@ -194,6 +194,17 @@ config USB_MXS_PHY
|
||||
|
||||
MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
|
||||
|
||||
+config USB_IPQ4019_PHY
|
||||
+ tristate "IPQ4019 PHY wrappers support"
|
||||
+ depends on (USB || USB_GADGET) && ARCH_QCOM
|
||||
+ select USB_PHY
|
||||
+ help
|
||||
+ Enable this to support the USB PHY transceivers on QCA961x chips.
|
||||
+ It handles PHY initialization, clock management required after
|
||||
+ resetting the hardware and power management.
|
||||
+ This driver is required even for peripheral only or host only
|
||||
+ mode configurations.
|
||||
+
|
||||
config USB_ULPI
|
||||
bool "Generic ULPI Transceiver Driver"
|
||||
depends on ARM || ARM64
|
||||
--- a/drivers/usb/phy/Makefile
|
||||
+++ b/drivers/usb/phy/Makefile
|
||||
@@ -21,6 +21,8 @@ obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio
|
||||
obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
|
||||
obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
|
||||
obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o
|
||||
+obj-$(CONFIG_USB_IPQ4019_PHY) += phy-qca-baldur.o
|
||||
+obj-$(CONFIG_USB_IPQ4019_PHY) += phy-qca-uniphy.o
|
||||
obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
|
||||
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
|
||||
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/usb/phy/phy-qca-baldur.c
|
||||
@@ -0,0 +1,262 @@
|
||||
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * 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 <linux/clk.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/regulator/consumer.h>
|
||||
+#include <linux/usb/phy.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/of_device.h>
|
||||
+
|
||||
+/**
|
||||
+ * USB Hardware registers
|
||||
+ */
|
||||
+#define PHY_CTRL0_ADDR 0x000
|
||||
+#define PHY_CTRL1_ADDR 0x004
|
||||
+#define PHY_CTRL2_ADDR 0x008
|
||||
+#define PHY_CTRL3_ADDR 0x00C
|
||||
+#define PHY_CTRL4_ADDR 0x010
|
||||
+#define PHY_MISC_ADDR 0x024
|
||||
+#define PHY_IPG_ADDR 0x030
|
||||
+
|
||||
+#define PHY_CTRL0_EMU_ADDR 0x180
|
||||
+#define PHY_CTRL1_EMU_ADDR 0x184
|
||||
+#define PHY_CTRL2_EMU_ADDR 0x188
|
||||
+#define PHY_CTRL3_EMU_ADDR 0x18C
|
||||
+#define PHY_CTRL4_EMU_ADDR 0x190
|
||||
+#define PHY_MISC_EMU_ADDR 0x1A4
|
||||
+#define PHY_IPG_EMU_ADDR 0x1B0
|
||||
+
|
||||
+#define PHY_CTRL0_VAL 0xA4600015
|
||||
+#define PHY_CTRL1_VAL 0x09500000
|
||||
+#define PHY_CTRL2_VAL 0x00058180
|
||||
+#define PHY_CTRL3_VAL 0x6DB6DCD6
|
||||
+#define PHY_CTRL4_VAL 0x836DB6DB
|
||||
+#define PHY_MISC_VAL 0x3803FB0C
|
||||
+#define PHY_IPG_VAL 0x47323232
|
||||
+
|
||||
+#define PHY_CTRL0_EMU_VAL 0xb4000015
|
||||
+#define PHY_CTRL1_EMU_VAL 0x09500000
|
||||
+#define PHY_CTRL2_EMU_VAL 0x00058180
|
||||
+#define PHY_CTRL3_EMU_VAL 0x6DB6DCD6
|
||||
+#define PHY_CTRL4_EMU_VAL 0x836DB6DB
|
||||
+#define PHY_MISC_EMU_VAL 0x3803FB0C
|
||||
+#define PHY_IPG_EMU_VAL 0x47323232
|
||||
+
|
||||
+#define USB30_HS_PHY_HOST_MODE (0x01 << 21)
|
||||
+#define USB20_HS_PHY_HOST_MODE (0x01 << 5)
|
||||
+
|
||||
+/* used to differentiate between USB3 HS and USB2 HS PHY */
|
||||
+struct qca_baldur_hs_data {
|
||||
+ unsigned int usb3_hs_phy;
|
||||
+ unsigned int phy_config_offset;
|
||||
+};
|
||||
+
|
||||
+struct qca_baldur_hs_phy {
|
||||
+ struct device *dev;
|
||||
+ struct usb_phy phy;
|
||||
+
|
||||
+ void __iomem *base;
|
||||
+ void __iomem *qscratch_base;
|
||||
+
|
||||
+ struct reset_control *por_rst;
|
||||
+ struct reset_control *srif_rst;
|
||||
+
|
||||
+ unsigned int host;
|
||||
+ unsigned int emulation;
|
||||
+ const struct qca_baldur_hs_data *data;
|
||||
+};
|
||||
+
|
||||
+#define phy_to_dw_phy(x) container_of((x), struct qca_baldur_hs_phy, phy)
|
||||
+
|
||||
+static int qca_baldur_phy_read(struct usb_phy *x, u32 reg)
|
||||
+{
|
||||
+ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
|
||||
+
|
||||
+ return readl(phy->base + reg);
|
||||
+}
|
||||
+
|
||||
+static int qca_baldur_phy_write(struct usb_phy *x, u32 val, u32 reg)
|
||||
+{
|
||||
+ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
|
||||
+
|
||||
+ writel(val, phy->base + reg);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca_baldur_hs_phy_init(struct usb_phy *x)
|
||||
+{
|
||||
+ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
|
||||
+
|
||||
+ /* assert HS PHY POR reset */
|
||||
+ reset_control_assert(phy->por_rst);
|
||||
+ msleep(10);
|
||||
+
|
||||
+ /* assert HS PHY SRIF reset */
|
||||
+ reset_control_assert(phy->srif_rst);
|
||||
+ msleep(10);
|
||||
+
|
||||
+ /* deassert HS PHY SRIF reset and program HS PHY registers */
|
||||
+ reset_control_deassert(phy->srif_rst);
|
||||
+ msleep(10);
|
||||
+
|
||||
+ if (!phy->emulation) {
|
||||
+ /* perform PHY register writes */
|
||||
+ writel(PHY_CTRL0_VAL, phy->base + PHY_CTRL0_ADDR);
|
||||
+ writel(PHY_CTRL1_VAL, phy->base + PHY_CTRL1_ADDR);
|
||||
+ writel(PHY_CTRL2_VAL, phy->base + PHY_CTRL2_ADDR);
|
||||
+ writel(PHY_CTRL3_VAL, phy->base + PHY_CTRL3_ADDR);
|
||||
+ writel(PHY_CTRL4_VAL, phy->base + PHY_CTRL4_ADDR);
|
||||
+ writel(PHY_MISC_VAL, phy->base + PHY_MISC_ADDR);
|
||||
+ writel(PHY_IPG_VAL, phy->base + PHY_IPG_ADDR);
|
||||
+ } else {
|
||||
+ /* perform PHY register writes */
|
||||
+ writel(PHY_CTRL0_EMU_VAL, phy->base + PHY_CTRL0_EMU_ADDR);
|
||||
+ writel(PHY_CTRL1_EMU_VAL, phy->base + PHY_CTRL1_EMU_ADDR);
|
||||
+ writel(PHY_CTRL2_EMU_VAL, phy->base + PHY_CTRL2_EMU_ADDR);
|
||||
+ writel(PHY_CTRL3_EMU_VAL, phy->base + PHY_CTRL3_EMU_ADDR);
|
||||
+ writel(PHY_CTRL4_EMU_VAL, phy->base + PHY_CTRL4_EMU_ADDR);
|
||||
+ writel(PHY_MISC_EMU_VAL, phy->base + PHY_MISC_EMU_ADDR);
|
||||
+ writel(PHY_IPG_EMU_VAL, phy->base + PHY_IPG_EMU_ADDR);
|
||||
+ }
|
||||
+
|
||||
+ msleep(10);
|
||||
+
|
||||
+ /* de-assert USB3 HS PHY POR reset */
|
||||
+ reset_control_deassert(phy->por_rst);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca_baldur_hs_get_resources(struct qca_baldur_hs_phy *phy)
|
||||
+{
|
||||
+ struct platform_device *pdev = to_platform_device(phy->dev);
|
||||
+ struct resource *res;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ phy->base = devm_ioremap_resource(phy->dev, res);
|
||||
+ if (IS_ERR(phy->base))
|
||||
+ return PTR_ERR(phy->base);
|
||||
+
|
||||
+ phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
|
||||
+ if (IS_ERR(phy->por_rst))
|
||||
+ return PTR_ERR(phy->por_rst);
|
||||
+
|
||||
+ phy->srif_rst = devm_reset_control_get(phy->dev, "srif_rst");
|
||||
+ if (IS_ERR(phy->srif_rst))
|
||||
+ return PTR_ERR(phy->srif_rst);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void qca_baldur_hs_put_resources(struct qca_baldur_hs_phy *phy)
|
||||
+{
|
||||
+ reset_control_assert(phy->srif_rst);
|
||||
+ reset_control_assert(phy->por_rst);
|
||||
+}
|
||||
+
|
||||
+static int qca_baldur_hs_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct qca_baldur_hs_phy *phy = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ usb_remove_phy(&phy->phy);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void qca_baldur_hs_phy_shutdown(struct usb_phy *x)
|
||||
+{
|
||||
+ struct qca_baldur_hs_phy *phy = phy_to_dw_phy(x);
|
||||
+
|
||||
+ qca_baldur_hs_put_resources(phy);
|
||||
+}
|
||||
+
|
||||
+static struct usb_phy_io_ops qca_baldur_io_ops = {
|
||||
+ .read = qca_baldur_phy_read,
|
||||
+ .write = qca_baldur_phy_write,
|
||||
+};
|
||||
+
|
||||
+static const struct qca_baldur_hs_data usb3_hs_data = {
|
||||
+ .usb3_hs_phy = 1,
|
||||
+ .phy_config_offset = USB30_HS_PHY_HOST_MODE,
|
||||
+};
|
||||
+
|
||||
+static const struct qca_baldur_hs_data usb2_hs_data = {
|
||||
+ .usb3_hs_phy = 0,
|
||||
+ .phy_config_offset = USB20_HS_PHY_HOST_MODE,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id qca_baldur_hs_id_table[] = {
|
||||
+ { .compatible = "qca,baldur-usb3-hsphy", .data = &usb3_hs_data },
|
||||
+ { .compatible = "qca,baldur-usb2-hsphy", .data = &usb2_hs_data },
|
||||
+ { /* Sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, qca_baldur_hs_id_table);
|
||||
+
|
||||
+static int qca_baldur_hs_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ const struct of_device_id *match;
|
||||
+ struct qca_baldur_hs_phy *phy;
|
||||
+ int err;
|
||||
+
|
||||
+ match = of_match_device(qca_baldur_hs_id_table, &pdev->dev);
|
||||
+ if (!match)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
+ if (!phy)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ platform_set_drvdata(pdev, phy);
|
||||
+ phy->dev = &pdev->dev;
|
||||
+
|
||||
+ phy->data = match->data;
|
||||
+
|
||||
+ err = qca_baldur_hs_get_resources(phy);
|
||||
+ if (err < 0) {
|
||||
+ dev_err(&pdev->dev, "failed to request resources: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ phy->phy.dev = phy->dev;
|
||||
+ phy->phy.label = "qca-baldur-hsphy";
|
||||
+ phy->phy.init = qca_baldur_hs_phy_init;
|
||||
+ phy->phy.shutdown = qca_baldur_hs_phy_shutdown;
|
||||
+ phy->phy.type = USB_PHY_TYPE_USB2;
|
||||
+ phy->phy.io_ops = &qca_baldur_io_ops;
|
||||
+
|
||||
+ err = usb_add_phy_dev(&phy->phy);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver qca_baldur_hs_driver = {
|
||||
+ .probe = qca_baldur_hs_probe,
|
||||
+ .remove = qca_baldur_hs_remove,
|
||||
+ .driver = {
|
||||
+ .name = "qca-baldur-hsphy",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = qca_baldur_hs_id_table,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(qca_baldur_hs_driver);
|
||||
+
|
||||
+MODULE_ALIAS("platform:qca-baldur-hsphy");
|
||||
+MODULE_LICENSE("Dual BSD/GPL");
|
||||
+MODULE_DESCRIPTION("USB3 QCA BALDUR HSPHY driver");
|
||||
--- /dev/null
|
||||
+++ b/drivers/usb/phy/phy-qca-uniphy.c
|
||||
@@ -0,0 +1,171 @@
|
||||
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * 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 <linux/clk.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/regulator/consumer.h>
|
||||
+#include <linux/usb/phy.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/of_device.h>
|
||||
+
|
||||
+struct qca_uni_ss_phy {
|
||||
+ struct usb_phy phy;
|
||||
+ struct device *dev;
|
||||
+
|
||||
+ void __iomem *base;
|
||||
+
|
||||
+ struct reset_control *por_rst;
|
||||
+
|
||||
+ unsigned int host;
|
||||
+};
|
||||
+
|
||||
+#define phy_to_dw_phy(x) container_of((x), struct qca_uni_ss_phy, phy)
|
||||
+
|
||||
+/**
|
||||
+ * Write register
|
||||
+ *
|
||||
+ * @base - PHY base virtual address.
|
||||
+ * @offset - register offset.
|
||||
+ */
|
||||
+static u32 qca_uni_ss_read(void __iomem *base, u32 offset)
|
||||
+{
|
||||
+ u32 value;
|
||||
+ value = readl_relaxed(base + offset);
|
||||
+ return value;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Write register
|
||||
+ *
|
||||
+ * @base - PHY base virtual address.
|
||||
+ * @offset - register offset.
|
||||
+ * @val - value to write.
|
||||
+ */
|
||||
+static void qca_uni_ss_write(void __iomem *base, u32 offset, u32 val)
|
||||
+{
|
||||
+ writel(val, base + offset);
|
||||
+ udelay(100);
|
||||
+}
|
||||
+
|
||||
+static void qca_uni_ss_phy_shutdown(struct usb_phy *x)
|
||||
+{
|
||||
+ struct qca_uni_ss_phy *phy = phy_to_dw_phy(x);
|
||||
+
|
||||
+ /* assert SS PHY POR reset */
|
||||
+ reset_control_assert(phy->por_rst);
|
||||
+}
|
||||
+
|
||||
+static int qca_uni_ss_phy_init(struct usb_phy *x)
|
||||
+{
|
||||
+ struct qca_uni_ss_phy *phy = phy_to_dw_phy(x);
|
||||
+
|
||||
+ /* assert SS PHY POR reset */
|
||||
+ reset_control_assert(phy->por_rst);
|
||||
+
|
||||
+ msleep(10);
|
||||
+
|
||||
+ msleep(10);
|
||||
+
|
||||
+ /* deassert SS PHY POR reset */
|
||||
+ reset_control_deassert(phy->por_rst);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca_uni_ss_get_resources(struct platform_device *pdev,
|
||||
+ struct qca_uni_ss_phy *phy)
|
||||
+{
|
||||
+ struct resource *res;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ phy->base = devm_ioremap_resource(phy->dev, res);
|
||||
+ if (IS_ERR(phy->base))
|
||||
+ return PTR_ERR(phy->base);
|
||||
+
|
||||
+ phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
|
||||
+ if (IS_ERR(phy->por_rst))
|
||||
+ return PTR_ERR(phy->por_rst);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca_uni_ss_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct qca_uni_ss_phy *phy = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ usb_remove_phy(&phy->phy);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id qca_uni_ss_id_table[] = {
|
||||
+ { .compatible = "qca,uni-ssphy" },
|
||||
+ { /* Sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, qca_uni_ss_id_table);
|
||||
+
|
||||
+static int qca_uni_ss_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ const struct of_device_id *match;
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct qca_uni_ss_phy *phy;
|
||||
+ int ret;
|
||||
+
|
||||
+ match = of_match_device(qca_uni_ss_id_table, &pdev->dev);
|
||||
+ if (!match)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
+ if (!phy)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ platform_set_drvdata(pdev, phy);
|
||||
+ phy->dev = &pdev->dev;
|
||||
+
|
||||
+ ret = qca_uni_ss_get_resources(pdev, phy);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(&pdev->dev, "failed to request resources: %d\n", ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ phy->phy.dev = phy->dev;
|
||||
+ phy->phy.label = "qca-uni-ssphy";
|
||||
+ phy->phy.init = qca_uni_ss_phy_init;
|
||||
+ phy->phy.shutdown = qca_uni_ss_phy_shutdown;
|
||||
+ phy->phy.type = USB_PHY_TYPE_USB3;
|
||||
+
|
||||
+ ret = usb_add_phy_dev(&phy->phy);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver qca_uni_ss_driver = {
|
||||
+ .probe = qca_uni_ss_probe,
|
||||
+ .remove = qca_uni_ss_remove,
|
||||
+ .driver = {
|
||||
+ .name = "qca-uni-ssphy",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = qca_uni_ss_id_table,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(qca_uni_ss_driver);
|
||||
+
|
||||
+MODULE_ALIAS("platform:qca-uni-ssphy");
|
||||
+MODULE_LICENSE("Dual BSD/GPL");
|
||||
+MODULE_DESCRIPTION("USB3 QCA UNI SSPHY driver");
|
|
@ -0,0 +1,88 @@
|
|||
From d2ed553484fecdf02fa53bf431599412348afa95 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 17 Mar 2016 16:22:28 -0500
|
||||
Subject: [PATCH 35/37] qcom: ipq4019: add USB nodes to ipq4019 SoC device
|
||||
tree
|
||||
|
||||
This adds the SoC nodes to the ipq4019 device tree
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 67 +++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 67 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
||||
@@ -313,5 +313,72 @@
|
||||
compatible = "qcom,pshold";
|
||||
reg = <0x4ab000 0x4>;
|
||||
};
|
||||
+
|
||||
+ usb3_ss_phy: ssphy@9a000 {
|
||||
+ compatible = "qca,uni-ssphy";
|
||||
+ reg = <0x9a000 0x800>;
|
||||
+ reg-names = "phy_base";
|
||||
+ resets = <&gcc USB3_UNIPHY_PHY_ARES>;
|
||||
+ reset-names = "por_rst";
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ usb3_hs_phy: hsphy@a6000 {
|
||||
+ compatible = "qca,baldur-usb3-hsphy";
|
||||
+ reg = <0xa6000 0x40>;
|
||||
+ reg-names = "phy_base";
|
||||
+ resets = <&gcc USB3_HSPHY_POR_ARES>, <&gcc USB3_HSPHY_S_ARES>;
|
||||
+ reset-names = "por_rst", "srif_rst";
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ usb3@0 {
|
||||
+ compatible = "qcom,dwc3";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ clocks = <&gcc GCC_USB3_MASTER_CLK>;
|
||||
+ clock-names = "core";
|
||||
+ ranges;
|
||||
+ status = "disabled";
|
||||
+
|
||||
+ dwc3@8a00000 {
|
||||
+ compatible = "snps,dwc3";
|
||||
+ reg = <0x8a00000 0xf8000>;
|
||||
+ interrupts = <0 132 0>;
|
||||
+ usb-phy = <&usb3_hs_phy>, <&usb3_ss_phy>;
|
||||
+ phy-names = "usb2-phy", "usb3-phy";
|
||||
+ tx-fifo-resize;
|
||||
+ dr_mode = "host";
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ usb2_hs_phy: hsphy@a8000 {
|
||||
+ compatible = "qca,baldur-usb2-hsphy";
|
||||
+ reg = <0xa8000 0x40>;
|
||||
+ reg-names = "phy_base";
|
||||
+ resets = <&gcc USB2_HSPHY_POR_ARES>, <&gcc USB2_HSPHY_S_ARES>;
|
||||
+ reset-names = "por_rst", "srif_rst";
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
+ usb2@0 {
|
||||
+ compatible = "qcom,dwc3";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ clocks = <&gcc GCC_USB2_MASTER_CLK>;
|
||||
+ clock-names = "core";
|
||||
+ ranges;
|
||||
+ status = "disabled";
|
||||
+
|
||||
+ dwc3@6000000 {
|
||||
+ compatible = "snps,dwc3";
|
||||
+ reg = <0x6000000 0xf8000>;
|
||||
+ interrupts = <0 136 0>;
|
||||
+ usb-phy = <&usb2_hs_phy>;
|
||||
+ phy-names = "usb2-phy";
|
||||
+ tx-fifo-resize;
|
||||
+ dr_mode = "host";
|
||||
+ };
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
From c15caf58ff5c3b4093fa388b9c6228bb3c9c5413 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 21 Mar 2016 16:12:05 -0500
|
||||
Subject: [PATCH 36/37] qcom: ipq4019: enable USB bus for DK01.1 board
|
||||
|
||||
This enables the USB block
|
||||
|
||||
Change-Id: I384dd1874bba341713f384cf6199abd446e3f3c0
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 24 ++++++++++++++++++++++++
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
|
||||
@@ -108,5 +108,29 @@
|
||||
watchdog@b017000 {
|
||||
status = "ok";
|
||||
};
|
||||
+
|
||||
+ usb3_ss_phy: ssphy@0 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ dummy_ss_phy: ssphy@1 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb3_hs_phy: hsphy@a6000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb2_hs_phy: hsphy@a8000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb3: usb3@8a00000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb2: usb2@6000000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
};
|
||||
};
|
|
@ -0,0 +1,258 @@
|
|||
From ff1ecc5bfc11377e82894d05aa45a92657ef8a06 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 21 Mar 2016 15:55:21 -0500
|
||||
Subject: [PATCH 37/37] dts: ipq4019: Add support for IPQ4019 DK04 board
|
||||
|
||||
This is pretty similiar to a DK01 but has a bit more IO. Some notable
|
||||
differences are listed below however they are not in the device tree yet
|
||||
as we continue adding more support
|
||||
|
||||
- second serial port
|
||||
- PCIe
|
||||
- NAND
|
||||
- SD/EMMC
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/Makefile | 1 +
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 12 +-
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts | 21 +++
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi | 163 +++++++++++++++++++++++
|
||||
4 files changed, 189 insertions(+), 8 deletions(-)
|
||||
create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts
|
||||
create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
|
||||
|
||||
--- a/arch/arm/boot/dts/Makefile
|
||||
+++ b/arch/arm/boot/dts/Makefile
|
||||
@@ -617,6 +617,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
|
||||
qcom-apq8084-ifc6540.dtb \
|
||||
qcom-apq8084-mtp.dtb \
|
||||
qcom-ipq4019-ap.dk01.1-c1.dtb \
|
||||
+ qcom-ipq4019-ap.dk04.1-c1.dtb \
|
||||
qcom-ipq8064-ap148.dtb \
|
||||
qcom-msm8660-surf.dtb \
|
||||
qcom-msm8960-cdp.dtb \
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
|
||||
@@ -109,11 +109,7 @@
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
- usb3_ss_phy: ssphy@0 {
|
||||
- status = "ok";
|
||||
- };
|
||||
-
|
||||
- dummy_ss_phy: ssphy@1 {
|
||||
+ usb3_ss_phy: ssphy@9a000 {
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
@@ -121,15 +117,15 @@
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
- usb2_hs_phy: hsphy@a8000 {
|
||||
+ usb3@0 {
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
- usb3: usb3@8a00000 {
|
||||
+ usb2_hs_phy: hsphy@a8000 {
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
- usb2: usb2@6000000 {
|
||||
+ usb2@0{
|
||||
status = "ok";
|
||||
};
|
||||
};
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts
|
||||
@@ -0,0 +1,21 @@
|
||||
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * 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 "qcom-ipq4019-ap.dk04.1.dtsi"
|
||||
+
|
||||
+/ {
|
||||
+ model = "Qualcomm Technologies, Inc. IPQ40xx/AP-DK04.1-C1";
|
||||
+};
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
|
||||
@@ -0,0 +1,163 @@
|
||||
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * 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 "qcom-ipq4019.dtsi"
|
||||
+
|
||||
+/ {
|
||||
+ model = "Qualcomm Technologies, Inc. IPQ4019/AP-DK04.1";
|
||||
+ compatible = "qcom,ipq4019";
|
||||
+
|
||||
+ clocks {
|
||||
+ xo: xo {
|
||||
+ compatible = "fixed-clock";
|
||||
+ clock-frequency = <48000000>;
|
||||
+ #clock-cells = <0>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ soc {
|
||||
+ timer {
|
||||
+ compatible = "arm,armv7-timer";
|
||||
+ interrupts = <1 2 0xf08>,
|
||||
+ <1 3 0xf08>,
|
||||
+ <1 4 0xf08>,
|
||||
+ <1 1 0xf08>;
|
||||
+ clock-frequency = <48000000>;
|
||||
+ };
|
||||
+
|
||||
+ pinctrl@0x01000000 {
|
||||
+ serial_0_pins: serial_pinmux {
|
||||
+ mux {
|
||||
+ pins = "gpio16", "gpio17";
|
||||
+ function = "blsp_uart0";
|
||||
+ bias-disable;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ serial_1_pins: serial1_pinmux {
|
||||
+ mux {
|
||||
+ pins = "gpio8", "gpio9";
|
||||
+ function = "blsp_uart1";
|
||||
+ bias-disable;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ spi_0_pins: spi_0_pinmux {
|
||||
+ pinmux {
|
||||
+ function = "blsp_spi0";
|
||||
+ pins = "gpio13", "gpio14", "gpio15";
|
||||
+ };
|
||||
+ pinmux_cs {
|
||||
+ function = "gpio";
|
||||
+ pins = "gpio12";
|
||||
+ };
|
||||
+ pinconf {
|
||||
+ pins = "gpio13", "gpio14", "gpio15";
|
||||
+ drive-strength = <12>;
|
||||
+ bias-disable;
|
||||
+ };
|
||||
+ pinconf_cs {
|
||||
+ pins = "gpio12";
|
||||
+ drive-strength = <2>;
|
||||
+ bias-disable;
|
||||
+ output-high;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ i2c_0_pins: i2c_0_pinmux {
|
||||
+ pinmux {
|
||||
+ function = "blsp_i2c0";
|
||||
+ pins = "gpio10", "gpio11";
|
||||
+ };
|
||||
+ pinconf {
|
||||
+ pins = "gpio10", "gpio11";
|
||||
+ drive-strength = <16>;
|
||||
+ bias-disable;
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ blsp_dma: dma@7884000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ spi_0: spi@78b5000 {
|
||||
+ pinctrl-0 = <&spi_0_pins>;
|
||||
+ pinctrl-names = "default";
|
||||
+ status = "ok";
|
||||
+ cs-gpios = <&tlmm 12 0>;
|
||||
+
|
||||
+ mx25l25635e@0 {
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ reg = <0>;
|
||||
+ compatible = "mx25l25635e";
|
||||
+ spi-max-frequency = <24000000>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ i2c_0: i2c@78b7000 { /* BLSP1 QUP2 */
|
||||
+ pinctrl-0 = <&i2c_0_pins>;
|
||||
+ pinctrl-names = "default";
|
||||
+
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ serial@78af000 {
|
||||
+ pinctrl-0 = <&serial_0_pins>;
|
||||
+ pinctrl-names = "default";
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ serial@78b0000 {
|
||||
+ pinctrl-0 = <&serial_1_pins>;
|
||||
+ pinctrl-names = "default";
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb3_ss_phy: ssphy@9a000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb3_hs_phy: hsphy@a6000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb3: usb3@0 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb2_hs_phy: hsphy@a8000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ usb2: usb2@6000000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ cryptobam: dma@8e04000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ crypto@8e3a000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+
|
||||
+ watchdog@b017000 {
|
||||
+ status = "ok";
|
||||
+ };
|
||||
+ };
|
||||
+};
|
|
@ -0,0 +1,746 @@
|
|||
From patchwork Wed Nov 2 15:56:56 2016
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
X-Patchwork-Id: 9409419
|
||||
Message-Id: <20161102155658.32203-2-georgi.djakov@linaro.org>
|
||||
To: sboyd@codeaurora.org, mturquette@baylibre.com
|
||||
Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
|
||||
robh+dt@kernel.org, mark.rutland@arm.com,
|
||||
linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
||||
georgi.djakov@linaro.org
|
||||
Date: Wed, 2 Nov 2016 17:56:56 +0200
|
||||
|
||||
This adds initial support for clocks controlled by the Resource
|
||||
Power Manager (RPM) processor on some Qualcomm SoCs, which use
|
||||
the qcom_smd_rpm driver to communicate with RPM.
|
||||
Such platforms are msm8916, apq8084 and msm8974.
|
||||
|
||||
The RPM is a dedicated hardware engine for managing the shared
|
||||
SoC resources in order to keep the lowest power profile. It
|
||||
communicates with other hardware subsystems via shared memory
|
||||
and accepts clock requests, aggregates the requests and turns
|
||||
the clocks on/off or scales them on demand.
|
||||
|
||||
This driver is based on the codeaurora.org driver:
|
||||
https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/clk/qcom/clock-rpm.c
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
.../devicetree/bindings/clock/qcom,rpmcc.txt | 36 ++
|
||||
drivers/clk/qcom/Kconfig | 16 +
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-smd-rpm.c | 571 +++++++++++++++++++++
|
||||
include/dt-bindings/clock/qcom,rpmcc.h | 45 ++
|
||||
5 files changed, 669 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
create mode 100644 drivers/clk/qcom/clk-smd-rpm.c
|
||||
create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h
|
||||
|
||||
--
|
||||
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
|
||||
the body of a message to majordomo@vger.kernel.org
|
||||
More majordomo info at http://vger.kernel.org/majordomo-info.html
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
@@ -0,0 +1,36 @@
|
||||
+Qualcomm RPM Clock Controller Binding
|
||||
+------------------------------------------------
|
||||
+The RPM is a dedicated hardware engine for managing the shared
|
||||
+SoC resources in order to keep the lowest power profile. It
|
||||
+communicates with other hardware subsystems via shared memory
|
||||
+and accepts clock requests, aggregates the requests and turns
|
||||
+the clocks on/off or scales them on demand.
|
||||
+
|
||||
+Required properties :
|
||||
+- compatible : shall contain only one of the following. The generic
|
||||
+ compatible "qcom,rpmcc" should be also included.
|
||||
+
|
||||
+ "qcom,rpmcc-msm8916", "qcom,rpmcc"
|
||||
+
|
||||
+- #clock-cells : shall contain 1
|
||||
+
|
||||
+Example:
|
||||
+ smd {
|
||||
+ compatible = "qcom,smd";
|
||||
+
|
||||
+ rpm {
|
||||
+ interrupts = <0 168 1>;
|
||||
+ qcom,ipc = <&apcs 8 0>;
|
||||
+ qcom,smd-edge = <15>;
|
||||
+
|
||||
+ rpm_requests {
|
||||
+ compatible = "qcom,rpm-msm8916";
|
||||
+ qcom,smd-channels = "rpm_requests";
|
||||
+
|
||||
+ rpmcc: clock-controller {
|
||||
+ compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
|
||||
+ #clock-cells = <1>;
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
+ };
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -2,6 +2,9 @@
|
||||
bool
|
||||
select PM_GENERIC_DOMAINS if PM
|
||||
|
||||
+config QCOM_RPMCC
|
||||
+ bool
|
||||
+
|
||||
config COMMON_CLK_QCOM
|
||||
tristate "Support for Qualcomm's clock controllers"
|
||||
depends on OF
|
||||
@@ -9,6 +12,19 @@
|
||||
select REGMAP_MMIO
|
||||
select RESET_CONTROLLER
|
||||
|
||||
+config QCOM_CLK_SMD_RPM
|
||||
+ tristate "RPM over SMD based Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
|
||||
+ select QCOM_RPMCC
|
||||
+ help
|
||||
+ The RPM (Resource Power Manager) is a dedicated hardware engine for
|
||||
+ managing the shared SoC resources in order to keep the lowest power
|
||||
+ profile. It communicates with other hardware subsystems via shared
|
||||
+ memory and accepts clock requests, aggregates the requests and turns
|
||||
+ the clocks on/off or scales them on demand.
|
||||
+ Say Y if you want to support the clocks exposed by the RPM on
|
||||
+ platforms such as apq8016, apq8084, msm8974 etc.
|
||||
+
|
||||
config APQ_GCC_8084
|
||||
tristate "APQ8084 Global Clock Controller"
|
||||
select QCOM_GDSC
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -29,3 +29,4 @@
|
||||
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
|
||||
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
|
||||
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
|
||||
+obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-smd-rpm.c
|
||||
@@ -0,0 +1,571 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2016, Linaro Limited
|
||||
+ * Copyright (c) 2014, 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 <linux/clk-provider.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/soc/qcom/smd-rpm.h>
|
||||
+
|
||||
+#include <dt-bindings/clock/qcom,rpmcc.h>
|
||||
+#include <dt-bindings/mfd/qcom-rpm.h>
|
||||
+
|
||||
+#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
|
||||
+#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
|
||||
+#define QCOM_RPM_SMD_KEY_RATE 0x007a484b
|
||||
+#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45
|
||||
+#define QCOM_RPM_SMD_KEY_STATE 0x54415453
|
||||
+#define QCOM_RPM_SCALING_ENABLE_ID 0x2
|
||||
+
|
||||
+#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \
|
||||
+ key) \
|
||||
+ static struct clk_smd_rpm _platform##_##_active; \
|
||||
+ static struct clk_smd_rpm _platform##_##_name = { \
|
||||
+ .rpm_res_type = (type), \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .rpm_status_id = (stat_id), \
|
||||
+ .rpm_key = (key), \
|
||||
+ .peer = &_platform##_##_active, \
|
||||
+ .rate = INT_MAX, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_smd_rpm_ops, \
|
||||
+ .name = #_name, \
|
||||
+ .parent_names = (const char *[]){ "xo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }; \
|
||||
+ static struct clk_smd_rpm _platform##_##_active = { \
|
||||
+ .rpm_res_type = (type), \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .rpm_status_id = (stat_id), \
|
||||
+ .active_only = true, \
|
||||
+ .rpm_key = (key), \
|
||||
+ .peer = &_platform##_##_name, \
|
||||
+ .rate = INT_MAX, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_smd_rpm_ops, \
|
||||
+ .name = #_active, \
|
||||
+ .parent_names = (const char *[]){ "xo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }
|
||||
+
|
||||
+#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \
|
||||
+ stat_id, r, key) \
|
||||
+ static struct clk_smd_rpm _platform##_##_active; \
|
||||
+ static struct clk_smd_rpm _platform##_##_name = { \
|
||||
+ .rpm_res_type = (type), \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .rpm_status_id = (stat_id), \
|
||||
+ .rpm_key = (key), \
|
||||
+ .branch = true, \
|
||||
+ .peer = &_platform##_##_active, \
|
||||
+ .rate = (r), \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_smd_rpm_branch_ops, \
|
||||
+ .name = #_name, \
|
||||
+ .parent_names = (const char *[]){ "xo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }; \
|
||||
+ static struct clk_smd_rpm _platform##_##_active = { \
|
||||
+ .rpm_res_type = (type), \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .rpm_status_id = (stat_id), \
|
||||
+ .active_only = true, \
|
||||
+ .rpm_key = (key), \
|
||||
+ .branch = true, \
|
||||
+ .peer = &_platform##_##_name, \
|
||||
+ .rate = (r), \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_smd_rpm_branch_ops, \
|
||||
+ .name = #_active, \
|
||||
+ .parent_names = (const char *[]){ "xo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }
|
||||
+
|
||||
+#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \
|
||||
+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
|
||||
+ 0, QCOM_RPM_SMD_KEY_RATE)
|
||||
+
|
||||
+#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \
|
||||
+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \
|
||||
+ r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE)
|
||||
+
|
||||
+#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \
|
||||
+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
|
||||
+ 0, QCOM_RPM_SMD_KEY_STATE)
|
||||
+
|
||||
+#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \
|
||||
+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
|
||||
+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
|
||||
+ QCOM_RPM_KEY_SOFTWARE_ENABLE)
|
||||
+
|
||||
+#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \
|
||||
+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
|
||||
+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
|
||||
+ QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY)
|
||||
+
|
||||
+#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw)
|
||||
+
|
||||
+struct clk_smd_rpm {
|
||||
+ const int rpm_res_type;
|
||||
+ const int rpm_key;
|
||||
+ const int rpm_clk_id;
|
||||
+ const int rpm_status_id;
|
||||
+ const bool active_only;
|
||||
+ bool enabled;
|
||||
+ bool branch;
|
||||
+ struct clk_smd_rpm *peer;
|
||||
+ struct clk_hw hw;
|
||||
+ unsigned long rate;
|
||||
+ struct qcom_smd_rpm *rpm;
|
||||
+};
|
||||
+
|
||||
+struct clk_smd_rpm_req {
|
||||
+ __le32 key;
|
||||
+ __le32 nbytes;
|
||||
+ __le32 value;
|
||||
+};
|
||||
+
|
||||
+struct rpm_cc {
|
||||
+ struct qcom_rpm *rpm;
|
||||
+ struct clk_hw_onecell_data data;
|
||||
+ struct clk_hw *hws[];
|
||||
+};
|
||||
+
|
||||
+struct rpm_smd_clk_desc {
|
||||
+ struct clk_smd_rpm **clks;
|
||||
+ size_t num_clks;
|
||||
+};
|
||||
+
|
||||
+static DEFINE_MUTEX(rpm_smd_clk_lock);
|
||||
+
|
||||
+static int clk_smd_rpm_handoff(struct clk_smd_rpm *r)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct clk_smd_rpm_req req = {
|
||||
+ .key = cpu_to_le32(r->rpm_key),
|
||||
+ .nbytes = cpu_to_le32(sizeof(u32)),
|
||||
+ .value = cpu_to_le32(INT_MAX),
|
||||
+ };
|
||||
+
|
||||
+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
|
||||
+ r->rpm_res_type, r->rpm_clk_id, &req,
|
||||
+ sizeof(req));
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
|
||||
+ r->rpm_res_type, r->rpm_clk_id, &req,
|
||||
+ sizeof(req));
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r,
|
||||
+ unsigned long rate)
|
||||
+{
|
||||
+ struct clk_smd_rpm_req req = {
|
||||
+ .key = cpu_to_le32(r->rpm_key),
|
||||
+ .nbytes = cpu_to_le32(sizeof(u32)),
|
||||
+ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
|
||||
+ };
|
||||
+
|
||||
+ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
|
||||
+ r->rpm_res_type, r->rpm_clk_id, &req,
|
||||
+ sizeof(req));
|
||||
+}
|
||||
+
|
||||
+static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r,
|
||||
+ unsigned long rate)
|
||||
+{
|
||||
+ struct clk_smd_rpm_req req = {
|
||||
+ .key = cpu_to_le32(r->rpm_key),
|
||||
+ .nbytes = cpu_to_le32(sizeof(u32)),
|
||||
+ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
|
||||
+ };
|
||||
+
|
||||
+ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
|
||||
+ r->rpm_res_type, r->rpm_clk_id, &req,
|
||||
+ sizeof(req));
|
||||
+}
|
||||
+
|
||||
+static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate,
|
||||
+ unsigned long *active, unsigned long *sleep)
|
||||
+{
|
||||
+ *active = rate;
|
||||
+
|
||||
+ /*
|
||||
+ * Active-only clocks don't care what the rate is during sleep. So,
|
||||
+ * they vote for zero.
|
||||
+ */
|
||||
+ if (r->active_only)
|
||||
+ *sleep = 0;
|
||||
+ else
|
||||
+ *sleep = *active;
|
||||
+}
|
||||
+
|
||||
+static int clk_smd_rpm_prepare(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
|
||||
+ struct clk_smd_rpm *peer = r->peer;
|
||||
+ unsigned long this_rate = 0, this_sleep_rate = 0;
|
||||
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
|
||||
+ unsigned long active_rate, sleep_rate;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mutex_lock(&rpm_smd_clk_lock);
|
||||
+
|
||||
+ /* Don't send requests to the RPM if the rate has not been set. */
|
||||
+ if (!r->rate)
|
||||
+ goto out;
|
||||
+
|
||||
+ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
|
||||
+
|
||||
+ /* Take peer clock's rate into account only if it's enabled. */
|
||||
+ if (peer->enabled)
|
||||
+ to_active_sleep(peer, peer->rate,
|
||||
+ &peer_rate, &peer_sleep_rate);
|
||||
+
|
||||
+ active_rate = max(this_rate, peer_rate);
|
||||
+
|
||||
+ if (r->branch)
|
||||
+ active_rate = !!active_rate;
|
||||
+
|
||||
+ ret = clk_smd_rpm_set_rate_active(r, active_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
|
||||
+ if (r->branch)
|
||||
+ sleep_rate = !!sleep_rate;
|
||||
+
|
||||
+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
|
||||
+ if (ret)
|
||||
+ /* Undo the active set vote and restore it */
|
||||
+ ret = clk_smd_rpm_set_rate_active(r, peer_rate);
|
||||
+
|
||||
+out:
|
||||
+ if (!ret)
|
||||
+ r->enabled = true;
|
||||
+
|
||||
+ mutex_unlock(&rpm_smd_clk_lock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void clk_smd_rpm_unprepare(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
|
||||
+ struct clk_smd_rpm *peer = r->peer;
|
||||
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
|
||||
+ unsigned long active_rate, sleep_rate;
|
||||
+ int ret;
|
||||
+
|
||||
+ mutex_lock(&rpm_smd_clk_lock);
|
||||
+
|
||||
+ if (!r->rate)
|
||||
+ goto out;
|
||||
+
|
||||
+ /* Take peer clock's rate into account only if it's enabled. */
|
||||
+ if (peer->enabled)
|
||||
+ to_active_sleep(peer, peer->rate, &peer_rate,
|
||||
+ &peer_sleep_rate);
|
||||
+
|
||||
+ active_rate = r->branch ? !!peer_rate : peer_rate;
|
||||
+ ret = clk_smd_rpm_set_rate_active(r, active_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
|
||||
+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ r->enabled = false;
|
||||
+
|
||||
+out:
|
||||
+ mutex_unlock(&rpm_smd_clk_lock);
|
||||
+}
|
||||
+
|
||||
+static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
|
||||
+ struct clk_smd_rpm *peer = r->peer;
|
||||
+ unsigned long active_rate, sleep_rate;
|
||||
+ unsigned long this_rate = 0, this_sleep_rate = 0;
|
||||
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mutex_lock(&rpm_smd_clk_lock);
|
||||
+
|
||||
+ if (!r->enabled)
|
||||
+ goto out;
|
||||
+
|
||||
+ to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
|
||||
+
|
||||
+ /* Take peer clock's rate into account only if it's enabled. */
|
||||
+ if (peer->enabled)
|
||||
+ to_active_sleep(peer, peer->rate,
|
||||
+ &peer_rate, &peer_sleep_rate);
|
||||
+
|
||||
+ active_rate = max(this_rate, peer_rate);
|
||||
+ ret = clk_smd_rpm_set_rate_active(r, active_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
|
||||
+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ r->rate = rate;
|
||||
+
|
||||
+out:
|
||||
+ mutex_unlock(&rpm_smd_clk_lock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long *parent_rate)
|
||||
+{
|
||||
+ /*
|
||||
+ * RPM handles rate rounding and we don't have a way to
|
||||
+ * know what the rate will be, so just return whatever
|
||||
+ * rate is requested.
|
||||
+ */
|
||||
+ return rate;
|
||||
+}
|
||||
+
|
||||
+static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
|
||||
+
|
||||
+ /*
|
||||
+ * RPM handles rate rounding and we don't have a way to
|
||||
+ * know what the rate will be, so just return whatever
|
||||
+ * rate was set.
|
||||
+ */
|
||||
+ return r->rate;
|
||||
+}
|
||||
+
|
||||
+static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct clk_smd_rpm_req req = {
|
||||
+ .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE),
|
||||
+ .nbytes = cpu_to_le32(sizeof(u32)),
|
||||
+ .value = cpu_to_le32(1),
|
||||
+ };
|
||||
+
|
||||
+ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE,
|
||||
+ QCOM_SMD_RPM_MISC_CLK,
|
||||
+ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
|
||||
+ if (ret) {
|
||||
+ pr_err("RPM clock scaling (sleep set) not enabled!\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE,
|
||||
+ QCOM_SMD_RPM_MISC_CLK,
|
||||
+ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
|
||||
+ if (ret) {
|
||||
+ pr_err("RPM clock scaling (active set) not enabled!\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ pr_debug("%s: RPM clock scaling is enabled\n", __func__);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct clk_ops clk_smd_rpm_ops = {
|
||||
+ .prepare = clk_smd_rpm_prepare,
|
||||
+ .unprepare = clk_smd_rpm_unprepare,
|
||||
+ .set_rate = clk_smd_rpm_set_rate,
|
||||
+ .round_rate = clk_smd_rpm_round_rate,
|
||||
+ .recalc_rate = clk_smd_rpm_recalc_rate,
|
||||
+};
|
||||
+
|
||||
+static const struct clk_ops clk_smd_rpm_branch_ops = {
|
||||
+ .prepare = clk_smd_rpm_prepare,
|
||||
+ .unprepare = clk_smd_rpm_unprepare,
|
||||
+ .round_rate = clk_smd_rpm_round_rate,
|
||||
+ .recalc_rate = clk_smd_rpm_recalc_rate,
|
||||
+};
|
||||
+
|
||||
+/* msm8916 */
|
||||
+DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
|
||||
+DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
|
||||
+DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
|
||||
+DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4);
|
||||
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5);
|
||||
+
|
||||
+static struct clk_smd_rpm *msm8916_clks[] = {
|
||||
+ [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk,
|
||||
+ [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk,
|
||||
+ [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk,
|
||||
+ [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk,
|
||||
+ [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk,
|
||||
+ [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk,
|
||||
+ [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk,
|
||||
+ [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk,
|
||||
+ [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1,
|
||||
+ [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a,
|
||||
+ [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2,
|
||||
+ [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a,
|
||||
+ [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1,
|
||||
+ [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a,
|
||||
+ [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2,
|
||||
+ [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a,
|
||||
+ [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin,
|
||||
+ [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin,
|
||||
+ [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin,
|
||||
+ [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin,
|
||||
+ [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin,
|
||||
+ [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin,
|
||||
+ [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin,
|
||||
+ [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin,
|
||||
+};
|
||||
+
|
||||
+static const struct rpm_smd_clk_desc rpm_clk_msm8916 = {
|
||||
+ .clks = msm8916_clks,
|
||||
+ .num_clks = ARRAY_SIZE(msm8916_clks),
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id rpm_smd_clk_match_table[] = {
|
||||
+ { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
|
||||
+
|
||||
+static int rpm_smd_clk_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct clk_hw **hws;
|
||||
+ struct rpm_cc *rcc;
|
||||
+ struct clk_hw_onecell_data *data;
|
||||
+ int ret;
|
||||
+ size_t num_clks, i;
|
||||
+ struct qcom_smd_rpm *rpm;
|
||||
+ struct clk_smd_rpm **rpm_smd_clks;
|
||||
+ const struct rpm_smd_clk_desc *desc;
|
||||
+
|
||||
+ rpm = dev_get_drvdata(pdev->dev.parent);
|
||||
+ if (!rpm) {
|
||||
+ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ desc = of_device_get_match_data(&pdev->dev);
|
||||
+ if (!desc)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ rpm_smd_clks = desc->clks;
|
||||
+ num_clks = desc->num_clks;
|
||||
+
|
||||
+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
|
||||
+ GFP_KERNEL);
|
||||
+ if (!rcc)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ hws = rcc->hws;
|
||||
+ data = &rcc->data;
|
||||
+ data->num = num_clks;
|
||||
+
|
||||
+ for (i = 0; i < num_clks; i++) {
|
||||
+ if (!rpm_smd_clks[i]) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ rpm_smd_clks[i]->rpm = rpm;
|
||||
+
|
||||
+ ret = clk_smd_rpm_handoff(rpm_smd_clks[i]);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ ret = clk_smd_rpm_enable_scaling(rpm);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ for (i = 0; i < num_clks; i++) {
|
||||
+ if (!rpm_smd_clks[i]) {
|
||||
+ data->hws[i] = ERR_PTR(-ENOENT);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
|
||||
+ data);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ return 0;
|
||||
+err:
|
||||
+ dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rpm_smd_clk_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ of_clk_del_provider(pdev->dev.of_node);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver rpm_smd_clk_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "qcom-clk-smd-rpm",
|
||||
+ .of_match_table = rpm_smd_clk_match_table,
|
||||
+ },
|
||||
+ .probe = rpm_smd_clk_probe,
|
||||
+ .remove = rpm_smd_clk_remove,
|
||||
+};
|
||||
+
|
||||
+static int __init rpm_smd_clk_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&rpm_smd_clk_driver);
|
||||
+}
|
||||
+core_initcall(rpm_smd_clk_init);
|
||||
+
|
||||
+static void __exit rpm_smd_clk_exit(void)
|
||||
+{
|
||||
+ platform_driver_unregister(&rpm_smd_clk_driver);
|
||||
+}
|
||||
+module_exit(rpm_smd_clk_exit);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:qcom-clk-smd-rpm");
|
||||
--- /dev/null
|
||||
+++ b/include/dt-bindings/clock/qcom,rpmcc.h
|
||||
@@ -0,0 +1,45 @@
|
||||
+/*
|
||||
+ * Copyright 2015 Linaro Limited
|
||||
+ *
|
||||
+ * 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.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H
|
||||
+#define _DT_BINDINGS_CLK_MSM_RPMCC_H
|
||||
+
|
||||
+/* msm8916 */
|
||||
+#define RPM_SMD_XO_CLK_SRC 0
|
||||
+#define RPM_SMD_XO_A_CLK_SRC 1
|
||||
+#define RPM_SMD_PCNOC_CLK 2
|
||||
+#define RPM_SMD_PCNOC_A_CLK 3
|
||||
+#define RPM_SMD_SNOC_CLK 4
|
||||
+#define RPM_SMD_SNOC_A_CLK 5
|
||||
+#define RPM_SMD_BIMC_CLK 6
|
||||
+#define RPM_SMD_BIMC_A_CLK 7
|
||||
+#define RPM_SMD_QDSS_CLK 8
|
||||
+#define RPM_SMD_QDSS_A_CLK 9
|
||||
+#define RPM_SMD_BB_CLK1 10
|
||||
+#define RPM_SMD_BB_CLK1_A 11
|
||||
+#define RPM_SMD_BB_CLK2 12
|
||||
+#define RPM_SMD_BB_CLK2_A 13
|
||||
+#define RPM_SMD_RF_CLK1 14
|
||||
+#define RPM_SMD_RF_CLK1_A 15
|
||||
+#define RPM_SMD_RF_CLK2 16
|
||||
+#define RPM_SMD_RF_CLK2_A 17
|
||||
+#define RPM_SMD_BB_CLK1_PIN 18
|
||||
+#define RPM_SMD_BB_CLK1_A_PIN 19
|
||||
+#define RPM_SMD_BB_CLK2_PIN 20
|
||||
+#define RPM_SMD_BB_CLK2_A_PIN 21
|
||||
+#define RPM_SMD_RF_CLK1_PIN 22
|
||||
+#define RPM_SMD_RF_CLK1_A_PIN 23
|
||||
+#define RPM_SMD_RF_CLK2_PIN 24
|
||||
+#define RPM_SMD_RF_CLK2_A_PIN 25
|
||||
+
|
||||
+#endif
|
|
@ -0,0 +1,586 @@
|
|||
From 872f91b5ea720c72f81fb46d353c43ecb3263ffa Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 2 Nov 2016 17:56:57 +0200
|
||||
Subject: clk: qcom: Add support for RPM Clocks
|
||||
|
||||
This adds initial support for clocks controlled by the Resource
|
||||
Power Manager (RPM) processor on some Qualcomm SoCs, which use
|
||||
the qcom_rpm driver to communicate with RPM.
|
||||
Such platforms are apq8064 and msm8960.
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Acked-by: Rob Herring <robh@kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
.../devicetree/bindings/clock/qcom,rpmcc.txt | 1 +
|
||||
drivers/clk/qcom/Kconfig | 13 +
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-rpm.c | 489 +++++++++++++++++++++
|
||||
include/dt-bindings/clock/qcom,rpmcc.h | 24 +
|
||||
5 files changed, 528 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/clk-rpm.c
|
||||
|
||||
--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
@@ -11,6 +11,7 @@ Required properties :
|
||||
compatible "qcom,rpmcc" should be also included.
|
||||
|
||||
"qcom,rpmcc-msm8916", "qcom,rpmcc"
|
||||
+ "qcom,rpmcc-apq8064", "qcom,rpmcc"
|
||||
|
||||
- #clock-cells : shall contain 1
|
||||
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -12,6 +12,19 @@ config COMMON_CLK_QCOM
|
||||
select REGMAP_MMIO
|
||||
select RESET_CONTROLLER
|
||||
|
||||
+config QCOM_CLK_RPM
|
||||
+ tristate "RPM based Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM && MFD_QCOM_RPM
|
||||
+ select QCOM_RPMCC
|
||||
+ help
|
||||
+ The RPM (Resource Power Manager) is a dedicated hardware engine for
|
||||
+ managing the shared SoC resources in order to keep the lowest power
|
||||
+ profile. It communicates with other hardware subsystems via shared
|
||||
+ memory and accepts clock requests, aggregates the requests and turns
|
||||
+ the clocks on/off or scales them on demand.
|
||||
+ Say Y if you want to support the clocks exposed by the RPM on
|
||||
+ platforms such as ipq806x, msm8660, msm8960 etc.
|
||||
+
|
||||
config QCOM_CLK_SMD_RPM
|
||||
tristate "RPM over SMD based Clock Controller"
|
||||
depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -23,3 +23,4 @@ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm897
|
||||
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
|
||||
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
|
||||
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||
+obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-rpm.c
|
||||
@@ -0,0 +1,489 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2016, Linaro Limited
|
||||
+ * Copyright (c) 2014, 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 <linux/clk-provider.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/mfd/qcom_rpm.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#include <dt-bindings/mfd/qcom-rpm.h>
|
||||
+#include <dt-bindings/clock/qcom,rpmcc.h>
|
||||
+
|
||||
+#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63
|
||||
+#define QCOM_RPM_SCALING_ENABLE_ID 0x2
|
||||
+
|
||||
+#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \
|
||||
+ static struct clk_rpm _platform##_##_active; \
|
||||
+ static struct clk_rpm _platform##_##_name = { \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .peer = &_platform##_##_active, \
|
||||
+ .rate = INT_MAX, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_rpm_ops, \
|
||||
+ .name = #_name, \
|
||||
+ .parent_names = (const char *[]){ "pxo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }; \
|
||||
+ static struct clk_rpm _platform##_##_active = { \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .peer = &_platform##_##_name, \
|
||||
+ .active_only = true, \
|
||||
+ .rate = INT_MAX, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_rpm_ops, \
|
||||
+ .name = #_active, \
|
||||
+ .parent_names = (const char *[]){ "pxo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }
|
||||
+
|
||||
+#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \
|
||||
+ static struct clk_rpm _platform##_##_active; \
|
||||
+ static struct clk_rpm _platform##_##_name = { \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .active_only = true, \
|
||||
+ .peer = &_platform##_##_active, \
|
||||
+ .rate = (r), \
|
||||
+ .branch = true, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_rpm_branch_ops, \
|
||||
+ .name = #_name, \
|
||||
+ .parent_names = (const char *[]){ "pxo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }; \
|
||||
+ static struct clk_rpm _platform##_##_active = { \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .peer = &_platform##_##_name, \
|
||||
+ .rate = (r), \
|
||||
+ .branch = true, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_rpm_branch_ops, \
|
||||
+ .name = #_active, \
|
||||
+ .parent_names = (const char *[]){ "pxo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }
|
||||
+
|
||||
+#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \
|
||||
+ static struct clk_rpm _platform##_##_active; \
|
||||
+ static struct clk_rpm _platform##_##_name = { \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .peer = &_platform##_##_active, \
|
||||
+ .rate = (r), \
|
||||
+ .branch = true, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_rpm_branch_ops, \
|
||||
+ .name = #_name, \
|
||||
+ .parent_names = (const char *[]){ "cxo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }; \
|
||||
+ static struct clk_rpm _platform##_##_active = { \
|
||||
+ .rpm_clk_id = (r_id), \
|
||||
+ .active_only = true, \
|
||||
+ .peer = &_platform##_##_name, \
|
||||
+ .rate = (r), \
|
||||
+ .branch = true, \
|
||||
+ .hw.init = &(struct clk_init_data){ \
|
||||
+ .ops = &clk_rpm_branch_ops, \
|
||||
+ .name = #_active, \
|
||||
+ .parent_names = (const char *[]){ "cxo_board" }, \
|
||||
+ .num_parents = 1, \
|
||||
+ }, \
|
||||
+ }
|
||||
+
|
||||
+#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw)
|
||||
+
|
||||
+struct clk_rpm {
|
||||
+ const int rpm_clk_id;
|
||||
+ const bool active_only;
|
||||
+ unsigned long rate;
|
||||
+ bool enabled;
|
||||
+ bool branch;
|
||||
+ struct clk_rpm *peer;
|
||||
+ struct clk_hw hw;
|
||||
+ struct qcom_rpm *rpm;
|
||||
+};
|
||||
+
|
||||
+struct rpm_cc {
|
||||
+ struct qcom_rpm *rpm;
|
||||
+ struct clk_hw_onecell_data data;
|
||||
+ struct clk_hw *hws[];
|
||||
+};
|
||||
+
|
||||
+struct rpm_clk_desc {
|
||||
+ struct clk_rpm **clks;
|
||||
+ size_t num_clks;
|
||||
+};
|
||||
+
|
||||
+static DEFINE_MUTEX(rpm_clk_lock);
|
||||
+
|
||||
+static int clk_rpm_handoff(struct clk_rpm *r)
|
||||
+{
|
||||
+ int ret;
|
||||
+ u32 value = INT_MAX;
|
||||
+
|
||||
+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
|
||||
+ r->rpm_clk_id, &value, 1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
|
||||
+ r->rpm_clk_id, &value, 1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate)
|
||||
+{
|
||||
+ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
|
||||
+
|
||||
+ return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
|
||||
+ r->rpm_clk_id, &value, 1);
|
||||
+}
|
||||
+
|
||||
+static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate)
|
||||
+{
|
||||
+ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
|
||||
+
|
||||
+ return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
|
||||
+ r->rpm_clk_id, &value, 1);
|
||||
+}
|
||||
+
|
||||
+static void to_active_sleep(struct clk_rpm *r, unsigned long rate,
|
||||
+ unsigned long *active, unsigned long *sleep)
|
||||
+{
|
||||
+ *active = rate;
|
||||
+
|
||||
+ /*
|
||||
+ * Active-only clocks don't care what the rate is during sleep. So,
|
||||
+ * they vote for zero.
|
||||
+ */
|
||||
+ if (r->active_only)
|
||||
+ *sleep = 0;
|
||||
+ else
|
||||
+ *sleep = *active;
|
||||
+}
|
||||
+
|
||||
+static int clk_rpm_prepare(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_rpm *r = to_clk_rpm(hw);
|
||||
+ struct clk_rpm *peer = r->peer;
|
||||
+ unsigned long this_rate = 0, this_sleep_rate = 0;
|
||||
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
|
||||
+ unsigned long active_rate, sleep_rate;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mutex_lock(&rpm_clk_lock);
|
||||
+
|
||||
+ /* Don't send requests to the RPM if the rate has not been set. */
|
||||
+ if (!r->rate)
|
||||
+ goto out;
|
||||
+
|
||||
+ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
|
||||
+
|
||||
+ /* Take peer clock's rate into account only if it's enabled. */
|
||||
+ if (peer->enabled)
|
||||
+ to_active_sleep(peer, peer->rate,
|
||||
+ &peer_rate, &peer_sleep_rate);
|
||||
+
|
||||
+ active_rate = max(this_rate, peer_rate);
|
||||
+
|
||||
+ if (r->branch)
|
||||
+ active_rate = !!active_rate;
|
||||
+
|
||||
+ ret = clk_rpm_set_rate_active(r, active_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
|
||||
+ if (r->branch)
|
||||
+ sleep_rate = !!sleep_rate;
|
||||
+
|
||||
+ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
|
||||
+ if (ret)
|
||||
+ /* Undo the active set vote and restore it */
|
||||
+ ret = clk_rpm_set_rate_active(r, peer_rate);
|
||||
+
|
||||
+out:
|
||||
+ if (!ret)
|
||||
+ r->enabled = true;
|
||||
+
|
||||
+ mutex_unlock(&rpm_clk_lock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void clk_rpm_unprepare(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_rpm *r = to_clk_rpm(hw);
|
||||
+ struct clk_rpm *peer = r->peer;
|
||||
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
|
||||
+ unsigned long active_rate, sleep_rate;
|
||||
+ int ret;
|
||||
+
|
||||
+ mutex_lock(&rpm_clk_lock);
|
||||
+
|
||||
+ if (!r->rate)
|
||||
+ goto out;
|
||||
+
|
||||
+ /* Take peer clock's rate into account only if it's enabled. */
|
||||
+ if (peer->enabled)
|
||||
+ to_active_sleep(peer, peer->rate, &peer_rate,
|
||||
+ &peer_sleep_rate);
|
||||
+
|
||||
+ active_rate = r->branch ? !!peer_rate : peer_rate;
|
||||
+ ret = clk_rpm_set_rate_active(r, active_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
|
||||
+ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ r->enabled = false;
|
||||
+
|
||||
+out:
|
||||
+ mutex_unlock(&rpm_clk_lock);
|
||||
+}
|
||||
+
|
||||
+static int clk_rpm_set_rate(struct clk_hw *hw,
|
||||
+ unsigned long rate, unsigned long parent_rate)
|
||||
+{
|
||||
+ struct clk_rpm *r = to_clk_rpm(hw);
|
||||
+ struct clk_rpm *peer = r->peer;
|
||||
+ unsigned long active_rate, sleep_rate;
|
||||
+ unsigned long this_rate = 0, this_sleep_rate = 0;
|
||||
+ unsigned long peer_rate = 0, peer_sleep_rate = 0;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mutex_lock(&rpm_clk_lock);
|
||||
+
|
||||
+ if (!r->enabled)
|
||||
+ goto out;
|
||||
+
|
||||
+ to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
|
||||
+
|
||||
+ /* Take peer clock's rate into account only if it's enabled. */
|
||||
+ if (peer->enabled)
|
||||
+ to_active_sleep(peer, peer->rate,
|
||||
+ &peer_rate, &peer_sleep_rate);
|
||||
+
|
||||
+ active_rate = max(this_rate, peer_rate);
|
||||
+ ret = clk_rpm_set_rate_active(r, active_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ sleep_rate = max(this_sleep_rate, peer_sleep_rate);
|
||||
+ ret = clk_rpm_set_rate_sleep(r, sleep_rate);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ r->rate = rate;
|
||||
+
|
||||
+out:
|
||||
+ mutex_unlock(&rpm_clk_lock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long *parent_rate)
|
||||
+{
|
||||
+ /*
|
||||
+ * RPM handles rate rounding and we don't have a way to
|
||||
+ * know what the rate will be, so just return whatever
|
||||
+ * rate is requested.
|
||||
+ */
|
||||
+ return rate;
|
||||
+}
|
||||
+
|
||||
+static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct clk_rpm *r = to_clk_rpm(hw);
|
||||
+
|
||||
+ /*
|
||||
+ * RPM handles rate rounding and we don't have a way to
|
||||
+ * know what the rate will be, so just return whatever
|
||||
+ * rate was set.
|
||||
+ */
|
||||
+ return r->rate;
|
||||
+}
|
||||
+
|
||||
+static const struct clk_ops clk_rpm_ops = {
|
||||
+ .prepare = clk_rpm_prepare,
|
||||
+ .unprepare = clk_rpm_unprepare,
|
||||
+ .set_rate = clk_rpm_set_rate,
|
||||
+ .round_rate = clk_rpm_round_rate,
|
||||
+ .recalc_rate = clk_rpm_recalc_rate,
|
||||
+};
|
||||
+
|
||||
+static const struct clk_ops clk_rpm_branch_ops = {
|
||||
+ .prepare = clk_rpm_prepare,
|
||||
+ .unprepare = clk_rpm_unprepare,
|
||||
+ .round_rate = clk_rpm_round_rate,
|
||||
+ .recalc_rate = clk_rpm_recalc_rate,
|
||||
+};
|
||||
+
|
||||
+/* apq8064 */
|
||||
+DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
|
||||
+DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
|
||||
+
|
||||
+static struct clk_rpm *apq8064_clks[] = {
|
||||
+ [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
|
||||
+ [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
|
||||
+ [RPM_CFPB_CLK] = &apq8064_cfpb_clk,
|
||||
+ [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk,
|
||||
+ [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk,
|
||||
+ [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk,
|
||||
+ [RPM_EBI1_CLK] = &apq8064_ebi1_clk,
|
||||
+ [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk,
|
||||
+ [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk,
|
||||
+ [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk,
|
||||
+ [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk,
|
||||
+ [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk,
|
||||
+ [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk,
|
||||
+ [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk,
|
||||
+ [RPM_SFPB_CLK] = &apq8064_sfpb_clk,
|
||||
+ [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk,
|
||||
+ [RPM_QDSS_CLK] = &apq8064_qdss_clk,
|
||||
+ [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
|
||||
+};
|
||||
+
|
||||
+static const struct rpm_clk_desc rpm_clk_apq8064 = {
|
||||
+ .clks = apq8064_clks,
|
||||
+ .num_clks = ARRAY_SIZE(apq8064_clks),
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id rpm_clk_match_table[] = {
|
||||
+ { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
|
||||
+
|
||||
+static int rpm_clk_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct clk_hw **hws;
|
||||
+ struct rpm_cc *rcc;
|
||||
+ struct clk_hw_onecell_data *data;
|
||||
+ int ret;
|
||||
+ size_t num_clks, i;
|
||||
+ struct qcom_rpm *rpm;
|
||||
+ struct clk_rpm **rpm_clks;
|
||||
+ const struct rpm_clk_desc *desc;
|
||||
+
|
||||
+ rpm = dev_get_drvdata(pdev->dev.parent);
|
||||
+ if (!rpm) {
|
||||
+ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ desc = of_device_get_match_data(&pdev->dev);
|
||||
+ if (!desc)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ rpm_clks = desc->clks;
|
||||
+ num_clks = desc->num_clks;
|
||||
+
|
||||
+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
|
||||
+ GFP_KERNEL);
|
||||
+ if (!rcc)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ hws = rcc->hws;
|
||||
+ data = &rcc->data;
|
||||
+ data->num = num_clks;
|
||||
+
|
||||
+ for (i = 0; i < num_clks; i++) {
|
||||
+ if (!rpm_clks[i])
|
||||
+ continue;
|
||||
+
|
||||
+ rpm_clks[i]->rpm = rpm;
|
||||
+
|
||||
+ ret = clk_rpm_handoff(rpm_clks[i]);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < num_clks; i++) {
|
||||
+ if (!rpm_clks[i]) {
|
||||
+ data->hws[i] = ERR_PTR(-ENOENT);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
|
||||
+ data);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ return 0;
|
||||
+err:
|
||||
+ dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rpm_clk_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ of_clk_del_provider(pdev->dev.of_node);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver rpm_clk_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "qcom-clk-rpm",
|
||||
+ .of_match_table = rpm_clk_match_table,
|
||||
+ },
|
||||
+ .probe = rpm_clk_probe,
|
||||
+ .remove = rpm_clk_remove,
|
||||
+};
|
||||
+
|
||||
+static int __init rpm_clk_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&rpm_clk_driver);
|
||||
+}
|
||||
+core_initcall(rpm_clk_init);
|
||||
+
|
||||
+static void __exit rpm_clk_exit(void)
|
||||
+{
|
||||
+ platform_driver_unregister(&rpm_clk_driver);
|
||||
+}
|
||||
+module_exit(rpm_clk_exit);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:qcom-clk-rpm");
|
||||
--- a/include/dt-bindings/clock/qcom,rpmcc.h
|
||||
+++ b/include/dt-bindings/clock/qcom,rpmcc.h
|
||||
@@ -14,6 +14,30 @@
|
||||
#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H
|
||||
#define _DT_BINDINGS_CLK_MSM_RPMCC_H
|
||||
|
||||
+/* apq8064 */
|
||||
+#define RPM_PXO_CLK 0
|
||||
+#define RPM_PXO_A_CLK 1
|
||||
+#define RPM_CXO_CLK 2
|
||||
+#define RPM_CXO_A_CLK 3
|
||||
+#define RPM_APPS_FABRIC_CLK 4
|
||||
+#define RPM_APPS_FABRIC_A_CLK 5
|
||||
+#define RPM_CFPB_CLK 6
|
||||
+#define RPM_CFPB_A_CLK 7
|
||||
+#define RPM_QDSS_CLK 8
|
||||
+#define RPM_QDSS_A_CLK 9
|
||||
+#define RPM_DAYTONA_FABRIC_CLK 10
|
||||
+#define RPM_DAYTONA_FABRIC_A_CLK 11
|
||||
+#define RPM_EBI1_CLK 12
|
||||
+#define RPM_EBI1_A_CLK 13
|
||||
+#define RPM_MM_FABRIC_CLK 14
|
||||
+#define RPM_MM_FABRIC_A_CLK 15
|
||||
+#define RPM_MMFPB_CLK 16
|
||||
+#define RPM_MMFPB_A_CLK 17
|
||||
+#define RPM_SYS_FABRIC_CLK 18
|
||||
+#define RPM_SYS_FABRIC_A_CLK 19
|
||||
+#define RPM_SFPB_CLK 20
|
||||
+#define RPM_SFPB_A_CLK 21
|
||||
+
|
||||
/* msm8916 */
|
||||
#define RPM_SMD_XO_CLK_SRC 0
|
||||
#define RPM_SMD_XO_A_CLK_SRC 1
|
|
@ -0,0 +1,94 @@
|
|||
From c260524aba53b57f72b5446ed553080df30b4d09 Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 23 Nov 2016 16:52:49 +0200
|
||||
Subject: clk: qcom: clk-rpm: Fix clk_hw references
|
||||
|
||||
Fix the clk_hw references to the actual clocks and add a xlate function
|
||||
to return the hw pointers from the already existing static array.
|
||||
|
||||
Reported-by: Michael Scott <michael.scott@linaro.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
drivers/clk/qcom/clk-rpm.c | 36 ++++++++++++++++++++++--------------
|
||||
1 file changed, 22 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/drivers/clk/qcom/clk-rpm.c
|
||||
+++ b/drivers/clk/qcom/clk-rpm.c
|
||||
@@ -127,8 +127,8 @@ struct clk_rpm {
|
||||
|
||||
struct rpm_cc {
|
||||
struct qcom_rpm *rpm;
|
||||
- struct clk_hw_onecell_data data;
|
||||
- struct clk_hw *hws[];
|
||||
+ struct clk_rpm **clks;
|
||||
+ size_t num_clks;
|
||||
};
|
||||
|
||||
struct rpm_clk_desc {
|
||||
@@ -391,11 +391,23 @@ static const struct of_device_id rpm_clk
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
|
||||
|
||||
+static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct rpm_cc *rcc = data;
|
||||
+ unsigned int idx = clkspec->args[0];
|
||||
+
|
||||
+ if (idx >= rcc->num_clks) {
|
||||
+ pr_err("%s: invalid index %u\n", __func__, idx);
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+ }
|
||||
+
|
||||
+ return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT);
|
||||
+}
|
||||
+
|
||||
static int rpm_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
- struct clk_hw **hws;
|
||||
struct rpm_cc *rcc;
|
||||
- struct clk_hw_onecell_data *data;
|
||||
int ret;
|
||||
size_t num_clks, i;
|
||||
struct qcom_rpm *rpm;
|
||||
@@ -415,14 +427,12 @@ static int rpm_clk_probe(struct platform
|
||||
rpm_clks = desc->clks;
|
||||
num_clks = desc->num_clks;
|
||||
|
||||
- rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks,
|
||||
- GFP_KERNEL);
|
||||
+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL);
|
||||
if (!rcc)
|
||||
return -ENOMEM;
|
||||
|
||||
- hws = rcc->hws;
|
||||
- data = &rcc->data;
|
||||
- data->num = num_clks;
|
||||
+ rcc->clks = rpm_clks;
|
||||
+ rcc->num_clks = num_clks;
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
if (!rpm_clks[i])
|
||||
@@ -436,18 +446,16 @@ static int rpm_clk_probe(struct platform
|
||||
}
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
- if (!rpm_clks[i]) {
|
||||
- data->hws[i] = ERR_PTR(-ENOENT);
|
||||
+ if (!rpm_clks[i])
|
||||
continue;
|
||||
- }
|
||||
|
||||
ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
- ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
|
||||
- data);
|
||||
+ ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get,
|
||||
+ rcc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
In commit "regulator: qcom: Rework to single platform device" the smb208 regulator
|
||||
used in IPQ8064 was left out.
|
||||
|
||||
Add it to that new framework and update Docs accordingly.
|
||||
|
||||
Signed-off-by: Adrian Panella <ianchi74@outlook.com>
|
||||
|
||||
--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
||||
+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
||||
@@ -171,6 +171,9 @@ pm8018:
|
||||
s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
|
||||
l12, l14, lvs1
|
||||
|
||||
+smb208:
|
||||
+ s1a, s1b, s2a, s2b
|
||||
+
|
||||
The content of each sub-node is defined by the standard binding for regulators -
|
||||
see regulator.txt - with additional custom properties described below:
|
||||
|
||||
--- a/drivers/regulator/qcom_rpm-regulator.c
|
||||
+++ b/drivers/regulator/qcom_rpm-regulator.c
|
||||
@@ -933,12 +933,21 @@ static const struct rpm_regulator_data r
|
||||
{ }
|
||||
};
|
||||
|
||||
+static const struct rpm_regulator_data rpm_smb208_regulators[] = {
|
||||
+ { "s1a", QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" },
|
||||
+ { "s1b", QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" },
|
||||
+ { "s2a", QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" },
|
||||
+ { "s2b", QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" },
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
static const struct of_device_id rpm_of_match[] = {
|
||||
{ .compatible = "qcom,rpm-pm8018-regulators",
|
||||
.data = &rpm_pm8018_regulators },
|
||||
{ .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
|
||||
{ .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
|
||||
{ .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators },
|
||||
+ { .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpm_of_match);
|
|
@ -0,0 +1,25 @@
|
|||
From dd43e356db678a564ad2cc111962d72ba3162a38 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Wed, 18 Nov 2015 12:38:56 +0530
|
||||
Subject: ipq806x: gcc: Added the enable regs and mask for PRNG
|
||||
|
||||
kernel got hanged while reading from /dev/hwrng at the
|
||||
time of PRNG clock enable
|
||||
|
||||
Change-Id: I89856c7e19e6639508e6a2774304583a3ec91172
|
||||
Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq806x.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
||||
@@ -1153,6 +1153,8 @@ static struct clk_rcg prng_src = {
|
||||
.parent_map = gcc_pxo_pll8_map,
|
||||
},
|
||||
.clkr = {
|
||||
+ .enable_reg = 0x2e80,
|
||||
+ .enable_mask = BIT(11),
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "prng_src",
|
||||
.parent_names = gcc_pxo_pll8,
|
|
@ -0,0 +1,81 @@
|
|||
--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
@@ -12,6 +12,7 @@ Required properties :
|
||||
|
||||
"qcom,rpmcc-msm8916", "qcom,rpmcc"
|
||||
"qcom,rpmcc-apq8064", "qcom,rpmcc"
|
||||
+ "qcom,rpmcc-ipq806x", "qcom,rpmcc"
|
||||
|
||||
- #clock-cells : shall contain 1
|
||||
|
||||
--- a/drivers/clk/qcom/clk-rpm.c
|
||||
+++ b/drivers/clk/qcom/clk-rpm.c
|
||||
@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a
|
||||
DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
|
||||
DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
|
||||
|
||||
+/* ipq806x */
|
||||
+DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
|
||||
+DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
|
||||
+DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
|
||||
+DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
|
||||
+DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
|
||||
+DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
|
||||
+DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK);
|
||||
+DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK);
|
||||
+
|
||||
static struct clk_rpm *apq8064_clks[] = {
|
||||
[RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
|
||||
[RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
|
||||
@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] =
|
||||
[RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
|
||||
};
|
||||
|
||||
+static struct clk_rpm *ipq806x_clks[] = {
|
||||
+ [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk,
|
||||
+ [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk,
|
||||
+ [RPM_CFPB_CLK] = &ipq806x_cfpb_clk,
|
||||
+ [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk,
|
||||
+ [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk,
|
||||
+ [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk,
|
||||
+ [RPM_EBI1_CLK] = &ipq806x_ebi1_clk,
|
||||
+ [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk,
|
||||
+ [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk,
|
||||
+ [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk,
|
||||
+ [RPM_SFPB_CLK] = &ipq806x_sfpb_clk,
|
||||
+ [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk,
|
||||
+ [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk,
|
||||
+ [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk,
|
||||
+ [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk,
|
||||
+ [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk,
|
||||
+};
|
||||
+
|
||||
static const struct rpm_clk_desc rpm_clk_apq8064 = {
|
||||
.clks = apq8064_clks,
|
||||
.num_clks = ARRAY_SIZE(apq8064_clks),
|
||||
};
|
||||
|
||||
+static const struct rpm_clk_desc rpm_clk_ipq806x = {
|
||||
+ .clks = ipq806x_clks,
|
||||
+ .num_clks = ARRAY_SIZE(ipq806x_clks),
|
||||
+};
|
||||
+
|
||||
static const struct of_device_id rpm_clk_match_table[] = {
|
||||
{ .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
|
||||
+ { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
|
||||
--- a/include/dt-bindings/clock/qcom,rpmcc.h
|
||||
+++ b/include/dt-bindings/clock/qcom,rpmcc.h
|
||||
@@ -37,6 +37,10 @@
|
||||
#define RPM_SYS_FABRIC_A_CLK 19
|
||||
#define RPM_SFPB_CLK 20
|
||||
#define RPM_SFPB_A_CLK 21
|
||||
+#define RPM_NSS_FABRIC_0_CLK 22
|
||||
+#define RPM_NSS_FABRIC_0_A_CLK 23
|
||||
+#define RPM_NSS_FABRIC_1_CLK 24
|
||||
+#define RPM_NSS_FABRIC_1_A_CLK 25
|
||||
|
||||
/* msm8916 */
|
||||
#define RPM_SMD_XO_CLK_SRC 0
|
|
@ -0,0 +1,11 @@
|
|||
--- a/arch/arm/Makefile
|
||||
+++ b/arch/arm/Makefile
|
||||
@@ -65,7 +65,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-i
|
||||
# macro, but instead defines a whole series of macros which makes
|
||||
# testing for a specific architecture or later rather impossible.
|
||||
arch-$(CONFIG_CPU_32v7M) =-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m
|
||||
-arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a)
|
||||
+arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15
|
||||
arch-$(CONFIG_CPU_32v6) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6)
|
||||
# Only override the compiler option if ARMv6. The ARMv6K extensions are
|
||||
# always available in ARMv7
|
|
@ -0,0 +1,166 @@
|
|||
From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Tue, 12 Aug 2014 20:49:27 +0200
|
||||
Subject: [PATCH 24/53] GPIO: add named gpio exports
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/gpio/gpiolib-sysfs.c | 10 +++++-
|
||||
include/asm-generic/gpio.h | 6 ++++
|
||||
include/linux/gpio/consumer.h | 8 +++++
|
||||
4 files changed, 91 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/gpio/gpiolib-of.c
|
||||
+++ b/drivers/gpio/gpiolib-of.c
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/platform_device.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
||||
@@ -538,3 +540,69 @@ void of_gpiochip_remove(struct gpio_chip
|
||||
gpiochip_remove_pin_ranges(chip);
|
||||
of_node_put(chip->of_node);
|
||||
}
|
||||
+
|
||||
+static struct of_device_id gpio_export_ids[] = {
|
||||
+ { .compatible = "gpio-export" },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+
|
||||
+static int __init of_gpio_export_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct device_node *cnp;
|
||||
+ u32 val;
|
||||
+ int nb = 0;
|
||||
+
|
||||
+ for_each_child_of_node(np, cnp) {
|
||||
+ const char *name = NULL;
|
||||
+ int gpio;
|
||||
+ bool dmc;
|
||||
+ int max_gpio = 1;
|
||||
+ int i;
|
||||
+
|
||||
+ of_property_read_string(cnp, "gpio-export,name", &name);
|
||||
+
|
||||
+ if (!name)
|
||||
+ max_gpio = of_gpio_count(cnp);
|
||||
+
|
||||
+ for (i = 0; i < max_gpio; i++) {
|
||||
+ unsigned flags = 0;
|
||||
+ enum of_gpio_flags of_flags;
|
||||
+
|
||||
+ gpio = of_get_gpio_flags(cnp, i, &of_flags);
|
||||
+
|
||||
+ if (of_flags == OF_GPIO_ACTIVE_LOW)
|
||||
+ flags |= GPIOF_ACTIVE_LOW;
|
||||
+
|
||||
+ if (!of_property_read_u32(cnp, "gpio-export,output", &val))
|
||||
+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
|
||||
+ else
|
||||
+ flags |= GPIOF_IN;
|
||||
+
|
||||
+ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
|
||||
+ continue;
|
||||
+
|
||||
+ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
|
||||
+ gpio_export_with_name(gpio, dmc, name);
|
||||
+ nb++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver gpio_export_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "gpio-export",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = of_match_ptr(gpio_export_ids),
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init of_gpio_export_init(void)
|
||||
+{
|
||||
+ return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe);
|
||||
+}
|
||||
+device_initcall(of_gpio_export_init);
|
||||
--- a/drivers/gpio/gpiolib-sysfs.c
|
||||
+++ b/drivers/gpio/gpiolib-sysfs.c
|
||||
@@ -544,7 +544,7 @@ static struct class gpio_class = {
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_device *gdev;
|
||||
@@ -606,6 +606,8 @@ int gpiod_export(struct gpio_desc *desc,
|
||||
offset = gpio_chip_hwgpio(desc);
|
||||
if (chip->names && chip->names[offset])
|
||||
ioname = chip->names[offset];
|
||||
+ if (name)
|
||||
+ ioname = name;
|
||||
|
||||
dev = device_create_with_groups(&gpio_class, &gdev->dev,
|
||||
MKDEV(0, 0), data, gpio_groups,
|
||||
@@ -627,6 +629,12 @@ err_unlock:
|
||||
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(__gpiod_export);
|
||||
+
|
||||
+int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
||||
+{
|
||||
+ return __gpiod_export(desc, direction_may_change, NULL);
|
||||
+}
|
||||
EXPORT_SYMBOL_GPL(gpiod_export);
|
||||
|
||||
static int match_export(struct device *dev, const void *desc)
|
||||
--- a/include/asm-generic/gpio.h
|
||||
+++ b/include/asm-generic/gpio.h
|
||||
@@ -126,6 +126,12 @@ static inline int gpio_export(unsigned g
|
||||
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
|
||||
}
|
||||
|
||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
|
||||
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
|
||||
+{
|
||||
+ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
|
||||
+}
|
||||
+
|
||||
static inline int gpio_export_link(struct device *dev, const char *name,
|
||||
unsigned gpio)
|
||||
{
|
||||
--- a/include/linux/gpio/consumer.h
|
||||
+++ b/include/linux/gpio/consumer.h
|
||||
@@ -427,6 +427,7 @@ static inline struct gpio_desc *devm_get
|
||||
|
||||
#if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
|
||||
|
||||
+int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
|
||||
int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
|
||||
int gpiod_export_link(struct device *dev, const char *name,
|
||||
struct gpio_desc *desc);
|
||||
@@ -434,6 +435,13 @@ void gpiod_unexport(struct gpio_desc *de
|
||||
|
||||
#else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
|
||||
|
||||
+static inline int _gpiod_export(struct gpio_desc *desc,
|
||||
+ bool direction_may_change,
|
||||
+ const char *name)
|
||||
+{
|
||||
+ return -ENOSYS;
|
||||
+}
|
||||
+
|
||||
static inline int gpiod_export(struct gpio_desc *desc,
|
||||
bool direction_may_change)
|
||||
{
|
|
@ -0,0 +1,185 @@
|
|||
Author: Adrian Panella <ianchi74@outlook.com>
|
||||
Date: Fri Jun 10 19:10:15 2016 -0500
|
||||
|
||||
generic: Mangle bootloader's kernel arguments
|
||||
|
||||
The command-line arguments provided by the boot loader will be
|
||||
appended to a new device tree property: bootloader-args.
|
||||
If there is a property "append-rootblock" in DT under /chosen
|
||||
and a root= option in bootloaders command line it will be parsed
|
||||
and added to DT bootargs with the form: <append-rootblock>XX.
|
||||
Only command line ATAG will be processed, the rest of the ATAGs
|
||||
sent by bootloader will be ignored.
|
||||
This is usefull in dual boot systems, to get the current root partition
|
||||
without afecting the rest of the system.
|
||||
|
||||
|
||||
Signed-off-by: Adrian Panella <ianchi74@outlook.com>
|
||||
|
||||
--- a/arch/arm/Kconfig
|
||||
+++ b/arch/arm/Kconfig
|
||||
@@ -1949,6 +1949,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN
|
||||
The command-line arguments provided by the boot loader will be
|
||||
appended to the the device tree bootargs property.
|
||||
|
||||
+config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
|
||||
+ bool "Append rootblock parsing bootloader's kernel arguments"
|
||||
+ help
|
||||
+ The command-line arguments provided by the boot loader will be
|
||||
+ appended to a new device tree property: bootloader-args.
|
||||
+ If there is a property "append-rootblock" in DT under /chosen
|
||||
+ and a root= option in bootloaders command line it will be parsed
|
||||
+ and added to DT bootargs with the form: <append-rootblock>XX.
|
||||
+ Only command line ATAG will be processed, the rest of the ATAGs
|
||||
+ sent by bootloader will be ignored.
|
||||
+
|
||||
endchoice
|
||||
|
||||
config CMDLINE
|
||||
--- a/arch/arm/boot/compressed/atags_to_fdt.c
|
||||
+++ b/arch/arm/boot/compressed/atags_to_fdt.c
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
|
||||
#define do_extend_cmdline 1
|
||||
+#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
|
||||
+#define do_extend_cmdline 1
|
||||
#else
|
||||
#define do_extend_cmdline 0
|
||||
#endif
|
||||
@@ -66,6 +68,59 @@ static uint32_t get_cell_size(const void
|
||||
return cell_size;
|
||||
}
|
||||
|
||||
+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
|
||||
+
|
||||
+static char *append_rootblock(char *dest, const char *str, int len, void *fdt)
|
||||
+{
|
||||
+ char *ptr, *end;
|
||||
+ char *root="root=";
|
||||
+ int i, l;
|
||||
+ const char *rootblock;
|
||||
+
|
||||
+ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually
|
||||
+ ptr = str - 1;
|
||||
+
|
||||
+ do {
|
||||
+ //first find an 'r' at the begining or after a space
|
||||
+ do {
|
||||
+ ptr++;
|
||||
+ ptr = strchr(ptr, 'r');
|
||||
+ if(!ptr) return dest;
|
||||
+
|
||||
+ } while (ptr != str && *(ptr-1) != ' ');
|
||||
+
|
||||
+ //then check for the rest
|
||||
+ for(i = 1; i <= 4; i++)
|
||||
+ if(*(ptr+i) != *(root+i)) break;
|
||||
+
|
||||
+ } while (i != 5);
|
||||
+
|
||||
+ end = strchr(ptr, ' ');
|
||||
+ end = end ? (end - 1) : (strchr(ptr, 0) - 1);
|
||||
+
|
||||
+ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX )
|
||||
+ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++);
|
||||
+ ptr = end + 1;
|
||||
+
|
||||
+ /* if append-rootblock property is set use it to append to command line */
|
||||
+ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l);
|
||||
+ if(rootblock != NULL) {
|
||||
+ if(*dest != ' ') {
|
||||
+ *dest = ' ';
|
||||
+ dest++;
|
||||
+ len++;
|
||||
+ }
|
||||
+ if (len + l + i <= COMMAND_LINE_SIZE) {
|
||||
+ memcpy(dest, rootblock, l);
|
||||
+ dest += l - 1;
|
||||
+ memcpy(dest, ptr, i);
|
||||
+ dest += i;
|
||||
+ }
|
||||
+ }
|
||||
+ return dest;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
|
||||
{
|
||||
char cmdline[COMMAND_LINE_SIZE];
|
||||
@@ -85,12 +140,21 @@ static void merge_fdt_bootargs(void *fdt
|
||||
|
||||
/* and append the ATAG_CMDLINE */
|
||||
if (fdt_cmdline) {
|
||||
+
|
||||
+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
|
||||
+ //save original bootloader args
|
||||
+ //and append ubi.mtd with root partition number to current cmdline
|
||||
+ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline);
|
||||
+ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt);
|
||||
+
|
||||
+#else
|
||||
len = strlen(fdt_cmdline);
|
||||
if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
|
||||
*ptr++ = ' ';
|
||||
memcpy(ptr, fdt_cmdline, len);
|
||||
ptr += len;
|
||||
}
|
||||
+#endif
|
||||
}
|
||||
*ptr = '\0';
|
||||
|
||||
@@ -147,7 +211,9 @@ int atags_to_fdt(void *atag_list, void *
|
||||
else
|
||||
setprop_string(fdt, "/chosen", "bootargs",
|
||||
atag->u.cmdline.cmdline);
|
||||
- } else if (atag->hdr.tag == ATAG_MEM) {
|
||||
+ }
|
||||
+#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
|
||||
+ else if (atag->hdr.tag == ATAG_MEM) {
|
||||
if (memcount >= sizeof(mem_reg_property)/4)
|
||||
continue;
|
||||
if (!atag->u.mem.size)
|
||||
@@ -186,6 +252,10 @@ int atags_to_fdt(void *atag_list, void *
|
||||
setprop(fdt, "/memory", "reg", mem_reg_property,
|
||||
4 * memcount * memsize);
|
||||
}
|
||||
+#else
|
||||
+
|
||||
+ }
|
||||
+#endif
|
||||
|
||||
return fdt_pack(fdt);
|
||||
}
|
||||
--- a/init/main.c
|
||||
+++ b/init/main.c
|
||||
@@ -88,6 +88,10 @@
|
||||
#include <asm/sections.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
|
||||
+#include <linux/of.h>
|
||||
+#endif
|
||||
+
|
||||
static int kernel_init(void *);
|
||||
|
||||
extern void init_IRQ(void);
|
||||
@@ -538,6 +542,18 @@ asmlinkage __visible void __init start_k
|
||||
page_alloc_init();
|
||||
|
||||
pr_notice("Kernel command line: %s\n", boot_command_line);
|
||||
+
|
||||
+#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
|
||||
+ //Show bootloader's original command line for reference
|
||||
+ if(of_chosen) {
|
||||
+ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL);
|
||||
+ if(prop)
|
||||
+ pr_notice("Bootloader command line (ignored): %s\n", prop);
|
||||
+ else
|
||||
+ pr_notice("Bootloader command line not present\n");
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
parse_early_param();
|
||||
after_dashes = parse_args("Booting kernel",
|
||||
static_command_line, __start___param,
|
5046
target/linux/ipq806x/patches-4.9/999-dts.patch
Normal file
5046
target/linux/ipq806x/patches-4.9/999-dts.patch
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue