ipq806x: delete from CC branch; it is broken and unsupported
Signed-off-by: Felix Fietkau <nbd@openwrt.org> git-svn-id: svn://svn.openwrt.org/openwrt/branches/chaos_calmer@46845 3c298f89-4303-0410-b956-a3cf2f4a3e73master
parent
4f27e9cc71
commit
0dc3d0e7c7
|
@ -1,22 +0,0 @@
|
||||||
# Copyright (c) 2013 The Linux Foundation. All rights reserved.
|
|
||||||
#
|
|
||||||
include $(TOPDIR)/rules.mk
|
|
||||||
|
|
||||||
ARCH:=arm
|
|
||||||
BOARD:=ipq806x
|
|
||||||
BOARDNAME:=Qualcomm Atheros IPQ806X
|
|
||||||
FEATURES:=squashfs
|
|
||||||
CPU_TYPE:=cortex-a7
|
|
||||||
MAINTAINER:=John Crispin <blogic@openwrt.org>
|
|
||||||
|
|
||||||
KERNEL_PATCHVER:=3.18
|
|
||||||
|
|
||||||
KERNELNAME:=zImage Image dtbs
|
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/target.mk
|
|
||||||
DEFAULT_PACKAGES += \
|
|
||||||
kmod-leds-gpio kmod-gpio-button-hotplug swconfig \
|
|
||||||
kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform
|
|
||||||
|
|
||||||
|
|
||||||
$(eval $(call BuildTarget))
|
|
|
@ -1,3 +0,0 @@
|
||||||
define Package/base-files/install-target
|
|
||||||
rm -f $(1)/etc/config/network
|
|
||||||
endef
|
|
|
@ -1,4 +0,0 @@
|
||||||
# Copyright (c) 2013 The Linux Foundation. All rights reserved.
|
|
||||||
::sysinit:/etc/init.d/rcS S boot
|
|
||||||
::shutdown:/etc/init.d/rcS K shutdown
|
|
||||||
ttyMSM0::askfirst:/bin/ash --login
|
|
|
@ -1,38 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015 The Linux Foundation. All rights reserved.
|
|
||||||
# Copyright (C) 2011 OpenWrt.org
|
|
||||||
#
|
|
||||||
|
|
||||||
[ -e /etc/config/network ] && exit 0
|
|
||||||
|
|
||||||
touch /etc/config/network
|
|
||||||
|
|
||||||
. /lib/functions/uci-defaults.sh
|
|
||||||
. /lib/ipq806x.sh
|
|
||||||
|
|
||||||
ucidef_set_interface_loopback
|
|
||||||
|
|
||||||
board=$(ipq806x_board_name)
|
|
||||||
|
|
||||||
case "$board" in
|
|
||||||
ap148)
|
|
||||||
ucidef_set_interfaces_lan_wan "eth1" "eth0"
|
|
||||||
ucidef_add_switch "switch0" "1" "1"
|
|
||||||
ucidef_add_switch_vlan "switch0" "1" "6 1 2 3 4"
|
|
||||||
ucidef_add_switch_vlan "switch0" "2" "0 5"
|
|
||||||
;;
|
|
||||||
db149)
|
|
||||||
ucidef_set_interfaces_lan_wan "eth1 eth2 eth3" "eth0"
|
|
||||||
ucidef_add_switch "switch0" "1" "1"
|
|
||||||
ucidef_add_switch_vlan "switch0" "1" "6 1 2 3 4"
|
|
||||||
ucidef_add_switch_vlan "switch0" "2" "0 5"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unsupported hardware. Network interfaces not intialized"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
uci commit network
|
|
||||||
|
|
||||||
exit 0
|
|
|
@ -1,43 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
|
||||||
# Copyright (C) 2011 OpenWrt.org
|
|
||||||
#
|
|
||||||
|
|
||||||
IPQ806X_BOARD_NAME=
|
|
||||||
IPQ806X_MODEL=
|
|
||||||
|
|
||||||
ipq806x_board_detect() {
|
|
||||||
local machine
|
|
||||||
local name
|
|
||||||
|
|
||||||
machine=$(cat /proc/device-tree/model)
|
|
||||||
|
|
||||||
case "$machine" in
|
|
||||||
*"AP148")
|
|
||||||
name="ap148"
|
|
||||||
;;
|
|
||||||
*"DB149")
|
|
||||||
name="db149"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
[ -z "$name" ] && name="unknown"
|
|
||||||
|
|
||||||
[ -z "$IPQ806X_BOARD_NAME" ] && IPQ806X_BOARD_NAME="$name"
|
|
||||||
[ -z "$IPQ806X_MODEL" ] && IPQ806X_MODEL="$machine"
|
|
||||||
|
|
||||||
[ -e "/tmp/sysinfo/" ] || mkdir -p "/tmp/sysinfo/"
|
|
||||||
|
|
||||||
echo "$IPQ806X_BOARD_NAME" > /tmp/sysinfo/board_name
|
|
||||||
echo "$IPQ806X_MODEL" > /tmp/sysinfo/model
|
|
||||||
}
|
|
||||||
|
|
||||||
ipq806x_board_name() {
|
|
||||||
local name
|
|
||||||
|
|
||||||
[ -f /tmp/sysinfo/board_name ] && name=$(cat /tmp/sysinfo/board_name)
|
|
||||||
[ -z "$name" ] && name="unknown"
|
|
||||||
|
|
||||||
echo "$name"
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
do_ipq806x() {
|
|
||||||
. /lib/ipq806x.sh
|
|
||||||
|
|
||||||
ipq806x_board_detect
|
|
||||||
}
|
|
||||||
|
|
||||||
boot_hook_add preinit_main do_ipq806x
|
|
|
@ -1,420 +0,0 @@
|
||||||
CONFIG_ALIGNMENT_TRAP=y
|
|
||||||
# CONFIG_AMBA_PL08X is not set
|
|
||||||
# CONFIG_APM_EMULATION is not set
|
|
||||||
CONFIG_APQ_GCC_8084=y
|
|
||||||
CONFIG_APQ_MMCC_8084=y
|
|
||||||
CONFIG_AR8216_PHY=y
|
|
||||||
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
|
|
||||||
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
|
|
||||||
CONFIG_ARCH_HAS_SG_CHAIN=y
|
|
||||||
CONFIG_ARCH_HAS_TICK_BROADCAST=y
|
|
||||||
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
|
|
||||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
|
||||||
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
|
|
||||||
CONFIG_ARCH_MSM8960=y
|
|
||||||
CONFIG_ARCH_MSM8974=y
|
|
||||||
CONFIG_ARCH_MSM8X60=y
|
|
||||||
CONFIG_ARCH_MULTIPLATFORM=y
|
|
||||||
# CONFIG_ARCH_MULTI_CPU_AUTO is not set
|
|
||||||
CONFIG_ARCH_MULTI_V6_V7=y
|
|
||||||
CONFIG_ARCH_MULTI_V7=y
|
|
||||||
CONFIG_ARCH_NR_GPIO=0
|
|
||||||
CONFIG_ARCH_QCOM=y
|
|
||||||
CONFIG_ARCH_REQUIRE_GPIOLIB=y
|
|
||||||
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
|
|
||||||
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
|
|
||||||
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
|
|
||||||
CONFIG_ARCH_SUPPORTS_UPROBES=y
|
|
||||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
|
||||||
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
|
|
||||||
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
|
|
||||||
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
|
|
||||||
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
|
|
||||||
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
|
|
||||||
CONFIG_ARM=y
|
|
||||||
CONFIG_ARM_AMBA=y
|
|
||||||
CONFIG_ARM_APPENDED_DTB=y
|
|
||||||
CONFIG_ARM_ARCH_TIMER=y
|
|
||||||
CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
|
|
||||||
# CONFIG_ARM_ATAG_DTB_COMPAT is not set
|
|
||||||
CONFIG_ARM_CPU_SUSPEND=y
|
|
||||||
CONFIG_ARM_GIC=y
|
|
||||||
CONFIG_ARM_HAS_SG_CHAIN=y
|
|
||||||
# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
|
|
||||||
CONFIG_ARM_L1_CACHE_SHIFT=6
|
|
||||||
CONFIG_ARM_L1_CACHE_SHIFT_6=y
|
|
||||||
# CONFIG_ARM_LPAE is not set
|
|
||||||
CONFIG_ARM_PATCH_PHYS_VIRT=y
|
|
||||||
CONFIG_ARM_QCOM_CPUFREQ=y
|
|
||||||
# CONFIG_ARM_SP805_WATCHDOG is not set
|
|
||||||
CONFIG_ARM_THUMB=y
|
|
||||||
# CONFIG_ARM_THUMBEE is not set
|
|
||||||
CONFIG_ARM_UNWIND=y
|
|
||||||
CONFIG_ARM_VIRT_EXT=y
|
|
||||||
CONFIG_AT803X_PHY=y
|
|
||||||
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
|
|
||||||
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
|
|
||||||
CONFIG_BOUNCE=y
|
|
||||||
CONFIG_BUILD_BIN2C=y
|
|
||||||
# CONFIG_CACHE_L2X0 is not set
|
|
||||||
CONFIG_CLEANCACHE=y
|
|
||||||
CONFIG_CLKDEV_LOOKUP=y
|
|
||||||
CONFIG_CLKSRC_OF=y
|
|
||||||
CONFIG_CLKSRC_QCOM=y
|
|
||||||
CONFIG_CLONE_BACKWARDS=y
|
|
||||||
CONFIG_COMMON_CLK=y
|
|
||||||
CONFIG_COMMON_CLK_QCOM=y
|
|
||||||
CONFIG_COMPACTION=y
|
|
||||||
CONFIG_COREDUMP=y
|
|
||||||
# CONFIG_CPUFREQ_DT is not set
|
|
||||||
CONFIG_CPU_32v6K=y
|
|
||||||
CONFIG_CPU_32v7=y
|
|
||||||
CONFIG_CPU_ABRT_EV7=y
|
|
||||||
# CONFIG_CPU_BPREDICT_DISABLE is not set
|
|
||||||
CONFIG_CPU_CACHE_V7=y
|
|
||||||
CONFIG_CPU_CACHE_VIPT=y
|
|
||||||
CONFIG_CPU_COPY_V6=y
|
|
||||||
CONFIG_CPU_CP15=y
|
|
||||||
CONFIG_CPU_CP15_MMU=y
|
|
||||||
CONFIG_CPU_FREQ=y
|
|
||||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
|
|
||||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
|
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
|
|
||||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
|
|
||||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
|
|
||||||
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
|
|
||||||
# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
|
|
||||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
|
||||||
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
|
|
||||||
# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
|
|
||||||
CONFIG_CPU_FREQ_STAT=y
|
|
||||||
# CONFIG_CPU_FREQ_STAT_DETAILS is not set
|
|
||||||
CONFIG_CPU_HAS_ASID=y
|
|
||||||
# CONFIG_CPU_ICACHE_DISABLE is not set
|
|
||||||
CONFIG_CPU_IDLE=y
|
|
||||||
CONFIG_CPU_IDLE_GOV_LADDER=y
|
|
||||||
CONFIG_CPU_IDLE_GOV_MENU=y
|
|
||||||
CONFIG_CPU_PABRT_V7=y
|
|
||||||
CONFIG_CPU_PM=y
|
|
||||||
CONFIG_CPU_RMAP=y
|
|
||||||
# CONFIG_CPU_THERMAL is not set
|
|
||||||
CONFIG_CPU_TLB_V7=y
|
|
||||||
CONFIG_CPU_V7=y
|
|
||||||
CONFIG_CRC16=y
|
|
||||||
# CONFIG_CRC32_SARWATE is not set
|
|
||||||
CONFIG_CRC32_SLICEBY8=y
|
|
||||||
CONFIG_CROSS_MEMORY_ATTACH=y
|
|
||||||
# CONFIG_CRYPTO_SHA1_ARM_NEON is not set
|
|
||||||
# CONFIG_CRYPTO_SHA512_ARM_NEON is not set
|
|
||||||
CONFIG_CRYPTO_XZ=y
|
|
||||||
CONFIG_DCACHE_WORD_ACCESS=y
|
|
||||||
CONFIG_DEBUG_BUGVERBOSE=y
|
|
||||||
CONFIG_DEBUG_GPIO=y
|
|
||||||
CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
|
|
||||||
CONFIG_DEBUG_PREEMPT=y
|
|
||||||
# CONFIG_DEBUG_UART_8250 is not set
|
|
||||||
# CONFIG_DEBUG_UART_PL01X is not set
|
|
||||||
# CONFIG_DEBUG_USER is not set
|
|
||||||
CONFIG_DECOMPRESS_GZIP=y
|
|
||||||
CONFIG_DMADEVICES=y
|
|
||||||
CONFIG_DMA_ENGINE=y
|
|
||||||
CONFIG_DMA_OF=y
|
|
||||||
CONFIG_DMA_VIRTUAL_CHANNELS=y
|
|
||||||
CONFIG_DTC=y
|
|
||||||
CONFIG_DWMAC_IPQ806X=y
|
|
||||||
# CONFIG_DW_DMAC_CORE is not set
|
|
||||||
# CONFIG_DW_DMAC_PCI is not set
|
|
||||||
CONFIG_DYNAMIC_DEBUG=y
|
|
||||||
CONFIG_ETHERNET_PACKET_MANGLE=y
|
|
||||||
CONFIG_FIXED_PHY=y
|
|
||||||
CONFIG_FREEZER=y
|
|
||||||
CONFIG_GENERIC_ALLOCATOR=y
|
|
||||||
CONFIG_GENERIC_BUG=y
|
|
||||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
|
||||||
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
|
|
||||||
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
|
|
||||||
CONFIG_GENERIC_CPUFREQ_KRAIT=y
|
|
||||||
CONFIG_GENERIC_IDLE_POLL_SETUP=y
|
|
||||||
CONFIG_GENERIC_IO=y
|
|
||||||
CONFIG_GENERIC_IRQ_SHOW=y
|
|
||||||
CONFIG_GENERIC_PCI_IOMAP=y
|
|
||||||
CONFIG_GENERIC_PHY=y
|
|
||||||
CONFIG_GENERIC_PINCONF=y
|
|
||||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
|
||||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
|
||||||
CONFIG_GENERIC_STRNCPY_FROM_USER=y
|
|
||||||
CONFIG_GENERIC_STRNLEN_USER=y
|
|
||||||
CONFIG_GPIOLIB=y
|
|
||||||
CONFIG_GPIOLIB_IRQCHIP=y
|
|
||||||
CONFIG_GPIO_DEVRES=y
|
|
||||||
# CONFIG_GPIO_MSM_V2 is not set
|
|
||||||
CONFIG_GPIO_SYSFS=y
|
|
||||||
CONFIG_HANDLE_DOMAIN_IRQ=y
|
|
||||||
CONFIG_HARDIRQS_SW_RESEND=y
|
|
||||||
CONFIG_HAS_DMA=y
|
|
||||||
CONFIG_HAS_IOMEM=y
|
|
||||||
CONFIG_HAS_IOPORT_MAP=y
|
|
||||||
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
|
|
||||||
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
|
|
||||||
CONFIG_HAVE_ARCH_JUMP_LABEL=y
|
|
||||||
CONFIG_HAVE_ARCH_KGDB=y
|
|
||||||
CONFIG_HAVE_ARCH_PFN_VALID=y
|
|
||||||
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
|
|
||||||
CONFIG_HAVE_ARCH_TRACEHOOK=y
|
|
||||||
CONFIG_HAVE_ARM_ARCH_TIMER=y
|
|
||||||
# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
|
|
||||||
CONFIG_HAVE_BPF_JIT=y
|
|
||||||
CONFIG_HAVE_CC_STACKPROTECTOR=y
|
|
||||||
CONFIG_HAVE_CLK=y
|
|
||||||
CONFIG_HAVE_CLK_PREPARE=y
|
|
||||||
CONFIG_HAVE_CONTEXT_TRACKING=y
|
|
||||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
|
||||||
CONFIG_HAVE_DEBUG_KMEMLEAK=y
|
|
||||||
CONFIG_HAVE_DMA_API_DEBUG=y
|
|
||||||
CONFIG_HAVE_DMA_ATTRS=y
|
|
||||||
CONFIG_HAVE_DMA_CONTIGUOUS=y
|
|
||||||
CONFIG_HAVE_DYNAMIC_FTRACE=y
|
|
||||||
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
|
|
||||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
|
||||||
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
|
|
||||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
|
||||||
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
|
||||||
CONFIG_HAVE_HW_BREAKPOINT=y
|
|
||||||
CONFIG_HAVE_IDE=y
|
|
||||||
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
|
|
||||||
CONFIG_HAVE_KERNEL_GZIP=y
|
|
||||||
CONFIG_HAVE_KERNEL_LZ4=y
|
|
||||||
CONFIG_HAVE_KERNEL_LZMA=y
|
|
||||||
CONFIG_HAVE_KERNEL_LZO=y
|
|
||||||
CONFIG_HAVE_KERNEL_XZ=y
|
|
||||||
CONFIG_HAVE_MEMBLOCK=y
|
|
||||||
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
|
|
||||||
CONFIG_HAVE_NET_DSA=y
|
|
||||||
CONFIG_HAVE_OPROFILE=y
|
|
||||||
CONFIG_HAVE_PERF_EVENTS=y
|
|
||||||
CONFIG_HAVE_PERF_REGS=y
|
|
||||||
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
|
|
||||||
CONFIG_HAVE_PROC_CPU=y
|
|
||||||
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
|
|
||||||
CONFIG_HAVE_SMP=y
|
|
||||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
|
||||||
CONFIG_HAVE_UID16=y
|
|
||||||
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
|
|
||||||
CONFIG_HIGHMEM=y
|
|
||||||
CONFIG_HIGHPTE=y
|
|
||||||
CONFIG_HOTPLUG_CPU=y
|
|
||||||
CONFIG_HWMON=y
|
|
||||||
CONFIG_HW_RANDOM=y
|
|
||||||
CONFIG_HW_RANDOM_MSM=y
|
|
||||||
CONFIG_HZ_FIXED=0
|
|
||||||
CONFIG_I2C=y
|
|
||||||
CONFIG_I2C_BOARDINFO=y
|
|
||||||
CONFIG_I2C_CHARDEV=y
|
|
||||||
CONFIG_I2C_COMPAT=y
|
|
||||||
CONFIG_I2C_HELPER_AUTO=y
|
|
||||||
CONFIG_I2C_QUP=y
|
|
||||||
CONFIG_IKCONFIG=y
|
|
||||||
CONFIG_IKCONFIG_PROC=y
|
|
||||||
CONFIG_INITRAMFS_SOURCE=""
|
|
||||||
CONFIG_IOMMU_API=y
|
|
||||||
CONFIG_IOMMU_HELPER=y
|
|
||||||
CONFIG_IOMMU_PGTABLES_L2=y
|
|
||||||
CONFIG_IOMMU_SUPPORT=y
|
|
||||||
CONFIG_IPQ_GCC_806X=y
|
|
||||||
CONFIG_IRQCHIP=y
|
|
||||||
CONFIG_IRQ_DOMAIN=y
|
|
||||||
CONFIG_IRQ_FORCED_THREADING=y
|
|
||||||
CONFIG_IRQ_WORK=y
|
|
||||||
CONFIG_KPSS_XCC=y
|
|
||||||
CONFIG_KRAITCC=y
|
|
||||||
CONFIG_KRAIT_CLOCKS=y
|
|
||||||
CONFIG_KRAIT_L2_ACCESSORS=y
|
|
||||||
# CONFIG_LEDS_REGULATOR is not set
|
|
||||||
CONFIG_LIBFDT=y
|
|
||||||
CONFIG_LOCKUP_DETECTOR=y
|
|
||||||
CONFIG_LZO_COMPRESS=y
|
|
||||||
CONFIG_LZO_DECOMPRESS=y
|
|
||||||
CONFIG_MDIO_BITBANG=y
|
|
||||||
CONFIG_MDIO_BOARDINFO=y
|
|
||||||
CONFIG_MDIO_GPIO=y
|
|
||||||
CONFIG_MFD_QCOM_RPM=y
|
|
||||||
# CONFIG_MFD_SPMI_PMIC is not set
|
|
||||||
CONFIG_MFD_SYSCON=y
|
|
||||||
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
|
|
||||||
CONFIG_MIGHT_HAVE_PCI=y
|
|
||||||
CONFIG_MIGRATION=y
|
|
||||||
CONFIG_MODULES_USE_ELF_REL=y
|
|
||||||
CONFIG_MSM_GCC_8660=y
|
|
||||||
CONFIG_MSM_GCC_8960=y
|
|
||||||
CONFIG_MSM_GCC_8974=y
|
|
||||||
CONFIG_MSM_IOMMU=y
|
|
||||||
CONFIG_MSM_MMCC_8960=y
|
|
||||||
CONFIG_MSM_MMCC_8974=y
|
|
||||||
CONFIG_MTD_CMDLINE_PARTS=y
|
|
||||||
CONFIG_MTD_M25P80=y
|
|
||||||
CONFIG_MTD_SPI_NOR=y
|
|
||||||
CONFIG_MTD_SPLIT_FIRMWARE=y
|
|
||||||
CONFIG_MTD_SPLIT_FIT_FW=y
|
|
||||||
CONFIG_MULTI_IRQ_HANDLER=y
|
|
||||||
CONFIG_MUTEX_SPIN_ON_OWNER=y
|
|
||||||
CONFIG_NEED_DMA_MAP_STATE=y
|
|
||||||
CONFIG_NEON=y
|
|
||||||
CONFIG_NET_FLOW_LIMIT=y
|
|
||||||
CONFIG_NET_PTP_CLASSIFY=y
|
|
||||||
CONFIG_NET_VENDOR_WIZNET=y
|
|
||||||
CONFIG_NO_BOOTMEM=y
|
|
||||||
CONFIG_NO_HZ=y
|
|
||||||
CONFIG_NO_HZ_COMMON=y
|
|
||||||
CONFIG_NO_HZ_IDLE=y
|
|
||||||
CONFIG_NR_CPUS=4
|
|
||||||
CONFIG_OF=y
|
|
||||||
CONFIG_OF_ADDRESS=y
|
|
||||||
CONFIG_OF_ADDRESS_PCI=y
|
|
||||||
CONFIG_OF_EARLY_FLATTREE=y
|
|
||||||
CONFIG_OF_FLATTREE=y
|
|
||||||
CONFIG_OF_GPIO=y
|
|
||||||
CONFIG_OF_IOMMU=y
|
|
||||||
CONFIG_OF_IRQ=y
|
|
||||||
CONFIG_OF_MDIO=y
|
|
||||||
CONFIG_OF_MTD=y
|
|
||||||
CONFIG_OF_NET=y
|
|
||||||
CONFIG_OF_PCI=y
|
|
||||||
CONFIG_OF_PCI_IRQ=y
|
|
||||||
CONFIG_OF_RESERVED_MEM=y
|
|
||||||
CONFIG_OLD_SIGACTION=y
|
|
||||||
CONFIG_OLD_SIGSUSPEND3=y
|
|
||||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
|
||||||
CONFIG_PAGE_OFFSET=0xC0000000
|
|
||||||
CONFIG_PCI=y
|
|
||||||
CONFIG_PCIEAER=y
|
|
||||||
CONFIG_PCIEPORTBUS=y
|
|
||||||
CONFIG_PCIE_DW=y
|
|
||||||
CONFIG_PCIE_QCOM=y
|
|
||||||
CONFIG_PCI_DOMAINS=y
|
|
||||||
CONFIG_PCI_MSI=y
|
|
||||||
CONFIG_PERF_EVENTS=y
|
|
||||||
CONFIG_PERF_USE_VMALLOC=y
|
|
||||||
CONFIG_PHYLIB=y
|
|
||||||
# CONFIG_PHY_QCOM_APQ8064_SATA is not set
|
|
||||||
CONFIG_PHY_QCOM_IPQ806X_SATA=y
|
|
||||||
CONFIG_PINCTRL=y
|
|
||||||
CONFIG_PINCTRL_APQ8064=y
|
|
||||||
# CONFIG_PINCTRL_APQ8084 is not set
|
|
||||||
CONFIG_PINCTRL_IPQ8064=y
|
|
||||||
CONFIG_PINCTRL_MSM=y
|
|
||||||
# CONFIG_PINCTRL_MSM8960 is not set
|
|
||||||
CONFIG_PINCTRL_MSM8X74=y
|
|
||||||
# CONFIG_PL330_DMA is not set
|
|
||||||
CONFIG_PM=y
|
|
||||||
CONFIG_PM_CLK=y
|
|
||||||
# CONFIG_PM_DEBUG is not set
|
|
||||||
CONFIG_PM_OPP=y
|
|
||||||
CONFIG_PM_SLEEP=y
|
|
||||||
CONFIG_PM_SLEEP_SMP=y
|
|
||||||
CONFIG_POWER_RESET=y
|
|
||||||
# CONFIG_POWER_RESET_BRCMSTB is not set
|
|
||||||
# CONFIG_POWER_RESET_GPIO is not set
|
|
||||||
# CONFIG_POWER_RESET_GPIO_RESTART is not set
|
|
||||||
# CONFIG_POWER_RESET_LTC2952 is not set
|
|
||||||
CONFIG_POWER_RESET_MSM=y
|
|
||||||
# CONFIG_POWER_RESET_SYSCON is not set
|
|
||||||
CONFIG_POWER_SUPPLY=y
|
|
||||||
CONFIG_PPS=y
|
|
||||||
CONFIG_PREEMPT=y
|
|
||||||
CONFIG_PREEMPT_COUNT=y
|
|
||||||
# CONFIG_PREEMPT_NONE is not set
|
|
||||||
CONFIG_PREEMPT_RCU=y
|
|
||||||
CONFIG_PRINTK_TIME=y
|
|
||||||
CONFIG_PROC_PAGE_MONITOR=y
|
|
||||||
CONFIG_PTP_1588_CLOCK=y
|
|
||||||
CONFIG_QCOM_BAM_DMA=y
|
|
||||||
CONFIG_QCOM_GSBI=y
|
|
||||||
CONFIG_QCOM_HFPLL=y
|
|
||||||
CONFIG_QCOM_SCM=y
|
|
||||||
CONFIG_QCOM_WDT=y
|
|
||||||
CONFIG_RAS=y
|
|
||||||
# CONFIG_RCU_BOOST is not set
|
|
||||||
CONFIG_RCU_CPU_STALL_TIMEOUT=21
|
|
||||||
CONFIG_RCU_CPU_STALL_VERBOSE=y
|
|
||||||
CONFIG_RCU_STALL_COMMON=y
|
|
||||||
CONFIG_RD_GZIP=y
|
|
||||||
CONFIG_REGMAP=y
|
|
||||||
CONFIG_REGMAP_MMIO=y
|
|
||||||
CONFIG_REGULATOR=y
|
|
||||||
# CONFIG_REGULATOR_DEBUG is not set
|
|
||||||
CONFIG_REGULATOR_QCOM_RPM=y
|
|
||||||
# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
|
|
||||||
CONFIG_RESET_CONTROLLER=y
|
|
||||||
CONFIG_RFS_ACCEL=y
|
|
||||||
CONFIG_RPS=y
|
|
||||||
CONFIG_RTC_CLASS=y
|
|
||||||
# CONFIG_RTC_DRV_CMOS is not set
|
|
||||||
CONFIG_RWSEM_SPIN_ON_OWNER=y
|
|
||||||
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
|
|
||||||
CONFIG_SCHED_HRTICK=y
|
|
||||||
# CONFIG_SCSI_DMA is not set
|
|
||||||
# CONFIG_SERIAL_AMBA_PL010 is not set
|
|
||||||
# CONFIG_SERIAL_AMBA_PL011 is not set
|
|
||||||
CONFIG_SERIAL_MSM=y
|
|
||||||
CONFIG_SERIAL_MSM_CONSOLE=y
|
|
||||||
# CONFIG_SLAB is not set
|
|
||||||
CONFIG_SLUB=y
|
|
||||||
CONFIG_SLUB_CPU_PARTIAL=y
|
|
||||||
CONFIG_SMP=y
|
|
||||||
CONFIG_SMP_ON_UP=y
|
|
||||||
CONFIG_SPARSE_IRQ=y
|
|
||||||
CONFIG_SPI=y
|
|
||||||
CONFIG_SPI_MASTER=y
|
|
||||||
CONFIG_SPI_QUP=y
|
|
||||||
CONFIG_SPMI=y
|
|
||||||
CONFIG_SPMI_MSM_PMIC_ARB=y
|
|
||||||
# CONFIG_STMMAC_DA is not set
|
|
||||||
CONFIG_STMMAC_DEBUG_FS=y
|
|
||||||
CONFIG_STMMAC_ETH=y
|
|
||||||
CONFIG_STMMAC_PLATFORM=y
|
|
||||||
CONFIG_STOP_MACHINE=y
|
|
||||||
# CONFIG_STRIP_ASM_SYMS is not set
|
|
||||||
CONFIG_SUSPEND=y
|
|
||||||
CONFIG_SUSPEND_FREEZER=y
|
|
||||||
CONFIG_SWCONFIG=y
|
|
||||||
CONFIG_SWIOTLB=y
|
|
||||||
CONFIG_SWP_EMULATE=y
|
|
||||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
|
||||||
CONFIG_THERMAL=y
|
|
||||||
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
|
|
||||||
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
|
|
||||||
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
|
|
||||||
# CONFIG_THERMAL_EMULATION is not set
|
|
||||||
# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
|
|
||||||
CONFIG_THERMAL_GOV_STEP_WISE=y
|
|
||||||
# CONFIG_THERMAL_GOV_USER_SPACE is not set
|
|
||||||
CONFIG_THERMAL_HWMON=y
|
|
||||||
CONFIG_THERMAL_OF=y
|
|
||||||
# CONFIG_THUMB2_KERNEL is not set
|
|
||||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
|
||||||
CONFIG_TIMER_STATS=y
|
|
||||||
CONFIG_TREE_PREEMPT_RCU=y
|
|
||||||
CONFIG_UEVENT_HELPER_PATH=""
|
|
||||||
CONFIG_UID16=y
|
|
||||||
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
|
|
||||||
CONFIG_UNINLINE_SPIN_UNLOCK=y
|
|
||||||
CONFIG_USB_SUPPORT=y
|
|
||||||
CONFIG_USE_OF=y
|
|
||||||
CONFIG_VECTORS_BASE=0xffff0000
|
|
||||||
# CONFIG_VFIO is not set
|
|
||||||
CONFIG_VFP=y
|
|
||||||
CONFIG_VFPv3=y
|
|
||||||
CONFIG_VM_EVENT_COUNTERS=y
|
|
||||||
CONFIG_WATCHDOG_CORE=y
|
|
||||||
# CONFIG_WIZNET_W5100 is not set
|
|
||||||
# CONFIG_WIZNET_W5300 is not set
|
|
||||||
# CONFIG_WL_TI is not set
|
|
||||||
# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
|
|
||||||
# CONFIG_XEN is not set
|
|
||||||
CONFIG_XPS=y
|
|
||||||
CONFIG_XZ_DEC_ARM=y
|
|
||||||
CONFIG_XZ_DEC_BCJ=y
|
|
||||||
CONFIG_ZBOOT_ROM_BSS=0
|
|
||||||
CONFIG_ZBOOT_ROM_TEXT=0
|
|
||||||
CONFIG_ZLIB_INFLATE=y
|
|
||||||
CONFIG_ZONE_DMA_FLAG=0
|
|
|
@ -1,41 +0,0 @@
|
||||||
# Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
|
||||||
#
|
|
||||||
include $(TOPDIR)/rules.mk
|
|
||||||
include $(INCLUDE_DIR)/image.mk
|
|
||||||
|
|
||||||
UBIFS_OPTS = -m 2048 -e 124KiB -c 4096 -U -F
|
|
||||||
UBINIZE_OPTS = -m 2048 -p 128KiB
|
|
||||||
|
|
||||||
E2SIZE=$(shell echo $$(($(CONFIG_TARGET_ROOTFS_PARTSIZE)*1024)))
|
|
||||||
|
|
||||||
define Image/BuildKernel/FIT
|
|
||||||
gzip -9 -c $(KDIR)/Image > $(KDIR)/Image.gz
|
|
||||||
$(call CompressLzma,$(KDIR)/Image,$(KDIR)/Image.gz)
|
|
||||||
$(call Image/BuildKernel/MkFIT,$(1), $(KDIR)/Image.gz, $(LINUX_DIR)/arch/arm/boot/dts/$(1).dtb,gzip,0x42208000,0x42208000)
|
|
||||||
$(CP) $(KDIR)/fit-$(1).itb $(BIN_DIR)/$(IMG_PREFIX)-$(1)-fit-uImage.itb
|
|
||||||
ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),)
|
|
||||||
$(call Image/BuildKernel/MkFIT,$(1), $(KDIR)/Image-initramfs, $(LINUX_DIR)/arch/arm/boot/dts/$(1).dtb, none,0x42208000,0x42208000)
|
|
||||||
$(CP) $(KDIR)/fit-$(1).itb $(BIN_DIR)/$(IMG_PREFIX)-$(1)-fit-uImage-initramfs.itb
|
|
||||||
endif
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Image/Prepare
|
|
||||||
$(CP) $(LINUX_DIR)/vmlinux $(KDIR)/$(IMG_PREFIX)-vmlinux.elf
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Image/BuildKernel
|
|
||||||
$(CP) $(KDIR)/$(IMG_PREFIX)-vmlinux.elf $(BIN_DIR)
|
|
||||||
$(call Image/BuildKernel/FIT,qcom-ipq8064-ap148)
|
|
||||||
$(call Image/BuildKernel/FIT,qcom-ipq8064-db149)
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Image/Build/squashfs
|
|
||||||
$(call prepare_generic_squashfs,$(KDIR)/root.squashfs)
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Image/Build
|
|
||||||
$(call Image/Build/$(1),$(1))
|
|
||||||
dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/$(IMG_PREFIX)-$(1)-root.img bs=2k conv=sync
|
|
||||||
endef
|
|
||||||
|
|
||||||
$(eval $(call BuildImage))
|
|
|
@ -1,32 +0,0 @@
|
||||||
define KernelPackage/usb-phy-qcom-dwc3
|
|
||||||
TITLE:=DWC3 USB QCOM PHY driver
|
|
||||||
DEPENDS:=@TARGET_ipq806x
|
|
||||||
KCONFIG:= CONFIG_PHY_QCOM_DWC3
|
|
||||||
FILES:= $(LINUX_DIR)/drivers/phy/phy-qcom-dwc3.ko
|
|
||||||
AUTOLOAD:=$(call AutoLoad,45,phy-qcom-dwc3,1)
|
|
||||||
$(call AddDepends/usb)
|
|
||||||
endef
|
|
||||||
|
|
||||||
define KernelPackage/usb-phy-qcom-dwc3/description
|
|
||||||
This driver provides support for the integrated DesignWare
|
|
||||||
USB3 IP Core within the QCOM SoCs.
|
|
||||||
endef
|
|
||||||
|
|
||||||
$(eval $(call KernelPackage,usb-phy-qcom-dwc3))
|
|
||||||
|
|
||||||
|
|
||||||
define KernelPackage/usb-dwc3-qcom
|
|
||||||
TITLE:=DWC3 USB QCOM controller driver
|
|
||||||
DEPENDS:=@TARGET_ipq806x +kmod-usb-dwc3 +kmod-usb-phy-dwc3-qcom
|
|
||||||
KCONFIG:= CONFIG_USB_DWC3_QCOM
|
|
||||||
FILES:= $(LINUX_DIR)/drivers/usb/dwc3/dwc3-qcom.ko
|
|
||||||
AUTOLOAD:=$(call AutoLoad,53,dwc3-qcom,1)
|
|
||||||
$(call AddDepends/usb)
|
|
||||||
endef
|
|
||||||
|
|
||||||
define KernelPackage/usb-dwc3-qcom/description
|
|
||||||
This driver provides support for the integrated DesignWare
|
|
||||||
USB3 IP Core within the QCOM SoCs.
|
|
||||||
endef
|
|
||||||
|
|
||||||
$(eval $(call KernelPackage,usb-dwc3-qcom))
|
|
|
@ -1,522 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: spi: qup: Add DMA capabilities
|
|
||||||
From: Andy Gross <agross@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 4432401
|
|
||||||
Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org>
|
|
||||||
To: Mark Brown <broonie@kernel.org>
|
|
||||||
Cc: linux-spi@vger.kernel.org, Sagar Dharia <sdharia@codeaurora.org>,
|
|
||||||
Daniel Sneddon <dsneddon@codeaurora.org>,
|
|
||||||
Bjorn Andersson <bjorn.andersson@sonymobile.com>,
|
|
||||||
"Ivan T. Ivanov" <iivanov@mm-sol.com>,
|
|
||||||
linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
linux-arm-msm@vger.kernel.org, Andy Gross <agross@codeaurora.org>
|
|
||||||
Date: Thu, 26 Jun 2014 16:06:21 -0500
|
|
||||||
|
|
||||||
This patch adds DMA capabilities to the spi-qup driver. If DMA channels are
|
|
||||||
present, the QUP will use DMA instead of block mode for transfers to/from SPI
|
|
||||||
peripherals for transactions larger than the length of a block.
|
|
||||||
|
|
||||||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
.../devicetree/bindings/spi/qcom,spi-qup.txt | 10 +
|
|
||||||
drivers/spi/spi-qup.c | 361 ++++++++++++++++++--
|
|
||||||
2 files changed, 350 insertions(+), 21 deletions(-)
|
|
||||||
|
|
||||||
--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
|
|
||||||
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
|
|
||||||
@@ -27,6 +27,11 @@ Optional properties:
|
|
||||||
- spi-max-frequency: Specifies maximum SPI clock frequency,
|
|
||||||
Units - Hz. Definition as per
|
|
||||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
|
||||||
+- dmas : Two DMA channel specifiers following the convention outlined
|
|
||||||
+ in bindings/dma/dma.txt
|
|
||||||
+- dma-names: Names for the dma channels, if present. There must be at
|
|
||||||
+ least one channel named "tx" for transmit and named "rx" for
|
|
||||||
+ receive.
|
|
||||||
- num-cs: total number of chipselects
|
|
||||||
- cs-gpios: should specify GPIOs used for chipselects.
|
|
||||||
The gpios will be referred to as reg = <index> in the SPI child
|
|
||||||
@@ -51,6 +56,10 @@ Example:
|
|
||||||
clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
|
|
||||||
clock-names = "core", "iface";
|
|
||||||
|
|
||||||
+ dmas = <&blsp2_bam 2>,
|
|
||||||
+ <&blsp2_bam 3>;
|
|
||||||
+ dma-names = "rx", "tx";
|
|
||||||
+
|
|
||||||
pinctrl-names = "default";
|
|
||||||
pinctrl-0 = <&spi8_default>;
|
|
||||||
|
|
||||||
--- a/drivers/spi/spi-qup.c
|
|
||||||
+++ b/drivers/spi/spi-qup.c
|
|
||||||
@@ -22,6 +22,8 @@
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/pm_runtime.h>
|
|
||||||
#include <linux/spi/spi.h>
|
|
||||||
+#include <linux/dmaengine.h>
|
|
||||||
+#include <linux/dma-mapping.h>
|
|
||||||
|
|
||||||
#define QUP_CONFIG 0x0000
|
|
||||||
#define QUP_STATE 0x0004
|
|
||||||
@@ -116,6 +118,8 @@
|
|
||||||
|
|
||||||
#define SPI_NUM_CHIPSELECTS 4
|
|
||||||
|
|
||||||
+#define SPI_MAX_XFER (SZ_64K - 64)
|
|
||||||
+
|
|
||||||
/* high speed mode is when bus rate is greater then 26MHz */
|
|
||||||
#define SPI_HS_MIN_RATE 26000000
|
|
||||||
#define SPI_MAX_RATE 50000000
|
|
||||||
@@ -143,6 +147,17 @@ struct spi_qup {
|
|
||||||
int tx_bytes;
|
|
||||||
int rx_bytes;
|
|
||||||
int qup_v1;
|
|
||||||
+
|
|
||||||
+ int use_dma;
|
|
||||||
+
|
|
||||||
+ struct dma_chan *rx_chan;
|
|
||||||
+ struct dma_slave_config rx_conf;
|
|
||||||
+ struct dma_chan *tx_chan;
|
|
||||||
+ struct dma_slave_config tx_conf;
|
|
||||||
+ dma_addr_t rx_dma;
|
|
||||||
+ dma_addr_t tx_dma;
|
|
||||||
+ void *dummy;
|
|
||||||
+ atomic_t dma_outstanding;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+static void qup_dma_callback(void *data)
|
|
||||||
+{
|
|
||||||
+ struct spi_qup *controller = data;
|
|
||||||
+
|
|
||||||
+ if (atomic_dec_and_test(&controller->dma_outstanding))
|
|
||||||
+ complete(&controller->done);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer)
|
|
||||||
+{
|
|
||||||
+ struct dma_async_tx_descriptor *rxd, *txd;
|
|
||||||
+ dma_cookie_t rx_cookie, tx_cookie;
|
|
||||||
+ u32 xfer_len, rx_align = 0, tx_align = 0, n_words;
|
|
||||||
+ struct scatterlist tx_sg[2], rx_sg[2];
|
|
||||||
+ int ret = 0;
|
|
||||||
+ u32 bytes_to_xfer = xfer->len;
|
|
||||||
+ u32 offset = 0;
|
|
||||||
+ u32 rx_nents = 0, tx_nents = 0;
|
|
||||||
+ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ if (xfer->rx_buf) {
|
|
||||||
+ rx_dma = dma_map_single(controller->dev, xfer->rx_buf,
|
|
||||||
+ xfer->len, DMA_FROM_DEVICE);
|
|
||||||
+
|
|
||||||
+ if (dma_mapping_error(controller->dev, rx_dma)) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check to see if we need dummy buffer for leftover bytes */
|
|
||||||
+ rx_align = xfer->len % controller->in_blk_sz;
|
|
||||||
+ if (rx_align) {
|
|
||||||
+ rx_dummy_dma = dma_map_single(controller->dev,
|
|
||||||
+ controller->dummy, controller->in_fifo_sz,
|
|
||||||
+ DMA_FROM_DEVICE);
|
|
||||||
+
|
|
||||||
+ if (dma_mapping_error(controller->dev, rx_dummy_dma)) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_map_rx_dummy;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (xfer->tx_buf) {
|
|
||||||
+ tx_dma = dma_map_single(controller->dev,
|
|
||||||
+ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE);
|
|
||||||
+
|
|
||||||
+ if (dma_mapping_error(controller->dev, tx_dma)) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_map_tx;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check to see if we need dummy buffer for leftover bytes */
|
|
||||||
+ tx_align = xfer->len % controller->out_blk_sz;
|
|
||||||
+ if (tx_align) {
|
|
||||||
+ memcpy(controller->dummy + SZ_1K,
|
|
||||||
+ xfer->tx_buf + xfer->len - tx_align,
|
|
||||||
+ tx_align);
|
|
||||||
+ memset(controller->dummy + SZ_1K + tx_align, 0,
|
|
||||||
+ controller->out_blk_sz - tx_align);
|
|
||||||
+
|
|
||||||
+ tx_dummy_dma = dma_map_single(controller->dev,
|
|
||||||
+ controller->dummy + SZ_1K,
|
|
||||||
+ controller->out_blk_sz, DMA_TO_DEVICE);
|
|
||||||
+
|
|
||||||
+ if (dma_mapping_error(controller->dev, tx_dummy_dma)) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_map_tx_dummy;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ atomic_set(&controller->dma_outstanding, 0);
|
|
||||||
+
|
|
||||||
+ while (bytes_to_xfer > 0) {
|
|
||||||
+ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER);
|
|
||||||
+ n_words = DIV_ROUND_UP(xfer_len, controller->w_size);
|
|
||||||
+
|
|
||||||
+ /* write out current word count to controller */
|
|
||||||
+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
|
||||||
+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
|
||||||
+
|
|
||||||
+ reinit_completion(&controller->done);
|
|
||||||
+
|
|
||||||
+ if (xfer->tx_buf) {
|
|
||||||
+ /* recalc align for each transaction */
|
|
||||||
+ tx_align = xfer_len % controller->out_blk_sz;
|
|
||||||
+
|
|
||||||
+ if (tx_align)
|
|
||||||
+ tx_nents = 2;
|
|
||||||
+ else
|
|
||||||
+ tx_nents = 1;
|
|
||||||
+
|
|
||||||
+ /* initialize scatterlists */
|
|
||||||
+ sg_init_table(tx_sg, tx_nents);
|
|
||||||
+ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align;
|
|
||||||
+ sg_dma_address(&tx_sg[0]) = tx_dma + offset;
|
|
||||||
+
|
|
||||||
+ /* account for non block size transfer */
|
|
||||||
+ if (tx_align) {
|
|
||||||
+ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz;
|
|
||||||
+ sg_dma_address(&tx_sg[1]) = tx_dummy_dma;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ txd = dmaengine_prep_slave_sg(controller->tx_chan,
|
|
||||||
+ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0);
|
|
||||||
+ if (!txd) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_unmap;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ atomic_inc(&controller->dma_outstanding);
|
|
||||||
+
|
|
||||||
+ txd->callback = qup_dma_callback;
|
|
||||||
+ txd->callback_param = controller;
|
|
||||||
+
|
|
||||||
+ tx_cookie = dmaengine_submit(txd);
|
|
||||||
+
|
|
||||||
+ dma_async_issue_pending(controller->tx_chan);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (xfer->rx_buf) {
|
|
||||||
+ /* recalc align for each transaction */
|
|
||||||
+ rx_align = xfer_len % controller->in_blk_sz;
|
|
||||||
+
|
|
||||||
+ if (rx_align)
|
|
||||||
+ rx_nents = 2;
|
|
||||||
+ else
|
|
||||||
+ rx_nents = 1;
|
|
||||||
+
|
|
||||||
+ /* initialize scatterlists */
|
|
||||||
+ sg_init_table(rx_sg, rx_nents);
|
|
||||||
+ sg_dma_address(&rx_sg[0]) = rx_dma + offset;
|
|
||||||
+ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align;
|
|
||||||
+
|
|
||||||
+ /* account for non block size transfer */
|
|
||||||
+ if (rx_align) {
|
|
||||||
+ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz;
|
|
||||||
+ sg_dma_address(&rx_sg[1]) = rx_dummy_dma;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ rxd = dmaengine_prep_slave_sg(controller->rx_chan,
|
|
||||||
+ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0);
|
|
||||||
+ if (!rxd) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_unmap;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ atomic_inc(&controller->dma_outstanding);
|
|
||||||
+
|
|
||||||
+ rxd->callback = qup_dma_callback;
|
|
||||||
+ rxd->callback_param = controller;
|
|
||||||
+
|
|
||||||
+ rx_cookie = dmaengine_submit(rxd);
|
|
||||||
+
|
|
||||||
+ dma_async_issue_pending(controller->rx_chan);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
|
||||||
+ dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
|
||||||
+ goto err_unmap;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!wait_for_completion_timeout(&controller->done,
|
|
||||||
+ msecs_to_jiffies(1000))) {
|
|
||||||
+ ret = -ETIMEDOUT;
|
|
||||||
+
|
|
||||||
+ /* clear out all the DMA transactions */
|
|
||||||
+ if (xfer->tx_buf)
|
|
||||||
+ dmaengine_terminate_all(controller->tx_chan);
|
|
||||||
+ if (xfer->rx_buf)
|
|
||||||
+ dmaengine_terminate_all(controller->rx_chan);
|
|
||||||
+
|
|
||||||
+ goto err_unmap;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (rx_align)
|
|
||||||
+ memcpy(xfer->rx_buf + offset + xfer->len - rx_align,
|
|
||||||
+ controller->dummy, rx_align);
|
|
||||||
+
|
|
||||||
+ /* adjust remaining bytes to transfer */
|
|
||||||
+ bytes_to_xfer -= xfer_len;
|
|
||||||
+ offset += xfer_len;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ /* reset mini-core state so we can program next transaction */
|
|
||||||
+ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
|
||||||
+ dev_err(controller->dev, "cannot set RESET state\n");
|
|
||||||
+ goto err_unmap;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = 0;
|
|
||||||
+
|
|
||||||
+err_unmap:
|
|
||||||
+ if (tx_align)
|
|
||||||
+ dma_unmap_single(controller->dev, tx_dummy_dma,
|
|
||||||
+ controller->out_fifo_sz, DMA_TO_DEVICE);
|
|
||||||
+err_map_tx_dummy:
|
|
||||||
+ if (xfer->tx_buf)
|
|
||||||
+ dma_unmap_single(controller->dev, tx_dma, xfer->len,
|
|
||||||
+ DMA_TO_DEVICE);
|
|
||||||
+err_map_tx:
|
|
||||||
+ if (rx_align)
|
|
||||||
+ dma_unmap_single(controller->dev, rx_dummy_dma,
|
|
||||||
+ controller->in_fifo_sz, DMA_FROM_DEVICE);
|
|
||||||
+err_map_rx_dummy:
|
|
||||||
+ if (xfer->rx_buf)
|
|
||||||
+ dma_unmap_single(controller->dev, rx_dma, xfer->len,
|
|
||||||
+ DMA_FROM_DEVICE);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct spi_qup *controller = dev_id;
|
|
||||||
@@ -315,11 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i
|
|
||||||
error = -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
|
||||||
- spi_qup_fifo_read(controller, xfer);
|
|
||||||
+ if (!controller->use_dma) {
|
|
||||||
+ if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
|
||||||
+ spi_qup_fifo_read(controller, xfer);
|
|
||||||
|
|
||||||
- if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
|
||||||
- spi_qup_fifo_write(controller, xfer);
|
|
||||||
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
|
||||||
+ spi_qup_fifo_write(controller, xfer);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
spin_lock_irqsave(&controller->lock, flags);
|
|
||||||
controller->error = error;
|
|
||||||
@@ -339,6 +571,8 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
|
||||||
u32 config, iomode, mode;
|
|
||||||
int ret, n_words, w_size;
|
|
||||||
+ size_t dma_align = dma_get_cache_alignment();
|
|
||||||
+ u32 dma_available = 0;
|
|
||||||
|
|
||||||
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
|
||||||
dev_err(controller->dev, "too big size for loopback %d > %d\n",
|
|
||||||
@@ -367,6 +601,11 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
n_words = xfer->len / w_size;
|
|
||||||
controller->w_size = w_size;
|
|
||||||
|
|
||||||
+ if (controller->rx_chan &&
|
|
||||||
+ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
|
|
||||||
+ IS_ALIGNED((size_t)xfer->rx_buf, dma_align))
|
|
||||||
+ dma_available = 1;
|
|
||||||
+
|
|
||||||
if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
|
|
||||||
mode = QUP_IO_M_MODE_FIFO;
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
|
|
||||||
@@ -374,19 +613,31 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
/* must be zero for FIFO */
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
|
||||||
- } else {
|
|
||||||
+ controller->use_dma = 0;
|
|
||||||
+ } else if (!dma_available) {
|
|
||||||
mode = QUP_IO_M_MODE_BLOCK;
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
|
||||||
/* must be zero for BLOCK and BAM */
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
|
||||||
+ controller->use_dma = 0;
|
|
||||||
+ } else {
|
|
||||||
+ mode = QUP_IO_M_MODE_DMOV;
|
|
||||||
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
|
||||||
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
|
||||||
+ controller->use_dma = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
|
|
||||||
/* Set input and output transfer mode */
|
|
||||||
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
|
|
||||||
- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
|
||||||
+
|
|
||||||
+ if (!controller->use_dma)
|
|
||||||
+ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
|
||||||
+ else
|
|
||||||
+ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
|
|
||||||
+
|
|
||||||
iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
|
|
||||||
iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
|
||||||
|
|
||||||
@@ -419,6 +670,14 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
|
|
||||||
config |= xfer->bits_per_word - 1;
|
|
||||||
config |= QUP_CONFIG_SPI_MODE;
|
|
||||||
+
|
|
||||||
+ if (controller->use_dma) {
|
|
||||||
+ if (!xfer->tx_buf)
|
|
||||||
+ config |= QUP_CONFIG_NO_OUTPUT;
|
|
||||||
+ if (!xfer->rx_buf)
|
|
||||||
+ config |= QUP_CONFIG_NO_INPUT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
writel_relaxed(config, controller->base + QUP_CONFIG);
|
|
||||||
|
|
||||||
/* only write to OPERATIONAL_MASK when register is present */
|
|
||||||
@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s
|
|
||||||
controller->tx_bytes = 0;
|
|
||||||
spin_unlock_irqrestore(&controller->lock, flags);
|
|
||||||
|
|
||||||
- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
|
||||||
- dev_warn(controller->dev, "cannot set RUN state\n");
|
|
||||||
- goto exit;
|
|
||||||
- }
|
|
||||||
+ if (controller->use_dma) {
|
|
||||||
+ ret = spi_qup_do_dma(controller, xfer);
|
|
||||||
+ } else {
|
|
||||||
+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
|
||||||
+ dev_warn(controller->dev, "cannot set RUN state\n");
|
|
||||||
+ goto exit;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
|
|
||||||
- dev_warn(controller->dev, "cannot set PAUSE state\n");
|
|
||||||
- goto exit;
|
|
||||||
- }
|
|
||||||
+ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
|
|
||||||
+ dev_warn(controller->dev, "cannot set PAUSE state\n");
|
|
||||||
+ goto exit;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- spi_qup_fifo_write(controller, xfer);
|
|
||||||
+ spi_qup_fifo_write(controller, xfer);
|
|
||||||
|
|
||||||
- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
|
||||||
- dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
|
||||||
- goto exit;
|
|
||||||
- }
|
|
||||||
+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
|
||||||
+ dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
|
||||||
+ goto exit;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (!wait_for_completion_timeout(&controller->done, timeout))
|
|
||||||
- ret = -ETIMEDOUT;
|
|
||||||
+ if (!wait_for_completion_timeout(&controller->done, timeout))
|
|
||||||
+ ret = -ETIMEDOUT;
|
|
||||||
+ }
|
|
||||||
exit:
|
|
||||||
spi_qup_set_state(controller, QUP_STATE_RESET);
|
|
||||||
spin_lock_irqsave(&controller->lock, flags);
|
|
||||||
@@ -554,6 +817,7 @@ static int spi_qup_probe(struct platform
|
|
||||||
master->transfer_one = spi_qup_transfer_one;
|
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
|
||||||
master->auto_runtime_pm = true;
|
|
||||||
+ master->dma_alignment = dma_get_cache_alignment();
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
|
||||||
|
|
||||||
@@ -619,6 +883,56 @@ static int spi_qup_probe(struct platform
|
|
||||||
QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
|
|
||||||
base + QUP_ERROR_FLAGS_EN);
|
|
||||||
|
|
||||||
+ /* allocate dma resources, if available */
|
|
||||||
+ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
|
|
||||||
+ if (controller->rx_chan) {
|
|
||||||
+ controller->tx_chan =
|
|
||||||
+ dma_request_slave_channel(&pdev->dev, "tx");
|
|
||||||
+
|
|
||||||
+ if (!controller->tx_chan) {
|
|
||||||
+ dev_err(&pdev->dev, "Failed to allocate dma tx chan");
|
|
||||||
+ dma_release_channel(controller->rx_chan);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* set DMA parameters */
|
|
||||||
+ controller->rx_conf.device_fc = 1;
|
|
||||||
+ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO;
|
|
||||||
+ controller->rx_conf.src_maxburst = controller->in_blk_sz;
|
|
||||||
+
|
|
||||||
+ controller->tx_conf.device_fc = 1;
|
|
||||||
+ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO;
|
|
||||||
+ controller->tx_conf.dst_maxburst = controller->out_blk_sz;
|
|
||||||
+
|
|
||||||
+ if (dmaengine_slave_config(controller->rx_chan,
|
|
||||||
+ &controller->rx_conf)) {
|
|
||||||
+ dev_err(&pdev->dev, "failed to configure RX channel\n");
|
|
||||||
+
|
|
||||||
+ dma_release_channel(controller->rx_chan);
|
|
||||||
+ dma_release_channel(controller->tx_chan);
|
|
||||||
+ controller->tx_chan = NULL;
|
|
||||||
+ controller->rx_chan = NULL;
|
|
||||||
+ } else if (dmaengine_slave_config(controller->tx_chan,
|
|
||||||
+ &controller->tx_conf)) {
|
|
||||||
+ dev_err(&pdev->dev, "failed to configure TX channel\n");
|
|
||||||
+
|
|
||||||
+ dma_release_channel(controller->rx_chan);
|
|
||||||
+ dma_release_channel(controller->tx_chan);
|
|
||||||
+ controller->tx_chan = NULL;
|
|
||||||
+ controller->rx_chan = NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE,
|
|
||||||
+ GFP_KERNEL);
|
|
||||||
+
|
|
||||||
+ if (!controller->dummy) {
|
|
||||||
+ dma_release_channel(controller->rx_chan);
|
|
||||||
+ dma_release_channel(controller->tx_chan);
|
|
||||||
+ controller->tx_chan = NULL;
|
|
||||||
+ controller->rx_chan = NULL;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
writel_relaxed(0, base + SPI_CONFIG);
|
|
||||||
writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
|
|
||||||
|
|
||||||
@@ -731,6 +1045,11 @@ static int spi_qup_remove(struct platfor
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
+ if (controller->rx_chan)
|
|
||||||
+ dma_release_channel(controller->rx_chan);
|
|
||||||
+ if (controller->tx_chan)
|
|
||||||
+ dma_release_channel(controller->tx_chan);
|
|
||||||
+
|
|
||||||
clk_disable_unprepare(controller->cclk);
|
|
||||||
clk_disable_unprepare(controller->iclk);
|
|
||||||
|
|
|
@ -1,376 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3] spi: qup: Fix incorrect block transfers
|
|
||||||
From: Andy Gross <agross@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 5007321
|
|
||||||
Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org>
|
|
||||||
To: Mark Brown <broonie@kernel.org>
|
|
||||||
Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
|
|
||||||
linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
"Ivan T. Ivanov" <iivanov@mm-sol.com>,
|
|
||||||
Bjorn Andersson <bjorn.andersson@sonymobile.com>,
|
|
||||||
Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>
|
|
||||||
Date: Tue, 30 Sep 2014 16:21:28 -0500
|
|
||||||
|
|
||||||
This patch fixes a number of errors with the QUP block transfer mode. Errors
|
|
||||||
manifested themselves as input underruns, output overruns, and timed out
|
|
||||||
transactions.
|
|
||||||
|
|
||||||
The block mode does not require the priming that occurs in FIFO mode. At the
|
|
||||||
moment that the QUP is placed into the RUN state, the QUP will immediately raise
|
|
||||||
an interrupt if the request is a write. Therefore, there is no need to prime
|
|
||||||
the pump.
|
|
||||||
|
|
||||||
In addition, the block transfers require that whole blocks of data are
|
|
||||||
read/written at a time. The last block of data that completes a transaction may
|
|
||||||
contain less than a full blocks worth of data.
|
|
||||||
|
|
||||||
Each block of data results in an input/output service interrupt accompanied with
|
|
||||||
a input/output block flag set. Additional block reads/writes require clearing
|
|
||||||
of the service flag. It is ok to check for additional blocks of data in the
|
|
||||||
ISR, but you have to ack every block you transfer. Imbalanced acks result in
|
|
||||||
early return from complete transactions with pending interrupts that still have
|
|
||||||
to be ack'd. The next transaction can be affected by these interrupts.
|
|
||||||
Transactions are deemed complete when the MAX_INPUT or MAX_OUTPUT flag are set.
|
|
||||||
|
|
||||||
Changes from v2:
|
|
||||||
- Added in additional completion check so that transaction done is not
|
|
||||||
prematurely signaled.
|
|
||||||
- Fixed various review comments.
|
|
||||||
|
|
||||||
Changes from v1:
|
|
||||||
- Split out read/write block function.
|
|
||||||
- Removed extraneous checks for transfer length
|
|
||||||
|
|
||||||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
drivers/spi/spi-qup.c | 201 ++++++++++++++++++++++++++++++++++++-------------
|
|
||||||
1 file changed, 148 insertions(+), 53 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/spi/spi-qup.c
|
|
||||||
+++ b/drivers/spi/spi-qup.c
|
|
||||||
@@ -82,6 +82,8 @@
|
|
||||||
#define QUP_IO_M_MODE_BAM 3
|
|
||||||
|
|
||||||
/* QUP_OPERATIONAL fields */
|
|
||||||
+#define QUP_OP_IN_BLOCK_READ_REQ BIT(13)
|
|
||||||
+#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12)
|
|
||||||
#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
|
|
||||||
#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
|
|
||||||
#define QUP_OP_IN_SERVICE_FLAG BIT(9)
|
|
||||||
@@ -147,6 +149,7 @@ struct spi_qup {
|
|
||||||
int tx_bytes;
|
|
||||||
int rx_bytes;
|
|
||||||
int qup_v1;
|
|
||||||
+ int mode;
|
|
||||||
|
|
||||||
int use_dma;
|
|
||||||
|
|
||||||
@@ -213,30 +216,14 @@ static int spi_qup_set_state(struct spi_
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
-
|
|
||||||
-static void spi_qup_fifo_read(struct spi_qup *controller,
|
|
||||||
- struct spi_transfer *xfer)
|
|
||||||
+static void spi_qup_fill_read_buffer(struct spi_qup *controller,
|
|
||||||
+ struct spi_transfer *xfer, u32 data)
|
|
||||||
{
|
|
||||||
u8 *rx_buf = xfer->rx_buf;
|
|
||||||
- u32 word, state;
|
|
||||||
- int idx, shift, w_size;
|
|
||||||
-
|
|
||||||
- w_size = controller->w_size;
|
|
||||||
-
|
|
||||||
- while (controller->rx_bytes < xfer->len) {
|
|
||||||
-
|
|
||||||
- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
|
||||||
- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
|
|
||||||
- break;
|
|
||||||
-
|
|
||||||
- word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
|
||||||
-
|
|
||||||
- if (!rx_buf) {
|
|
||||||
- controller->rx_bytes += w_size;
|
|
||||||
- continue;
|
|
||||||
- }
|
|
||||||
+ int idx, shift;
|
|
||||||
|
|
||||||
- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
|
|
||||||
+ if (rx_buf)
|
|
||||||
+ for (idx = 0; idx < controller->w_size; idx++) {
|
|
||||||
/*
|
|
||||||
* The data format depends on bytes per SPI word:
|
|
||||||
* 4 bytes: 0x12345678
|
|
||||||
@@ -244,41 +231,139 @@ static void spi_qup_fifo_read(struct spi
|
|
||||||
* 1 byte : 0x00000012
|
|
||||||
*/
|
|
||||||
shift = BITS_PER_BYTE;
|
|
||||||
- shift *= (w_size - idx - 1);
|
|
||||||
- rx_buf[controller->rx_bytes] = word >> shift;
|
|
||||||
+ shift *= (controller->w_size - idx - 1);
|
|
||||||
+ rx_buf[controller->rx_bytes + idx] = data >> shift;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ controller->rx_bytes += controller->w_size;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void spi_qup_prepare_write_data(struct spi_qup *controller,
|
|
||||||
+ struct spi_transfer *xfer, u32 *data)
|
|
||||||
+{
|
|
||||||
+ const u8 *tx_buf = xfer->tx_buf;
|
|
||||||
+ u32 val;
|
|
||||||
+ int idx;
|
|
||||||
+
|
|
||||||
+ *data = 0;
|
|
||||||
+
|
|
||||||
+ if (tx_buf)
|
|
||||||
+ for (idx = 0; idx < controller->w_size; idx++) {
|
|
||||||
+ val = tx_buf[controller->tx_bytes + idx];
|
|
||||||
+ *data |= val << (BITS_PER_BYTE * (3 - idx));
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ controller->tx_bytes += controller->w_size;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void spi_qup_fifo_read(struct spi_qup *controller,
|
|
||||||
+ struct spi_transfer *xfer)
|
|
||||||
+{
|
|
||||||
+ u32 data;
|
|
||||||
+
|
|
||||||
+ /* clear service request */
|
|
||||||
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
|
||||||
+ controller->base + QUP_OPERATIONAL);
|
|
||||||
+
|
|
||||||
+ while (controller->rx_bytes < xfer->len) {
|
|
||||||
+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
|
|
||||||
+ QUP_OP_IN_FIFO_NOT_EMPTY))
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
|
||||||
+
|
|
||||||
+ spi_qup_fill_read_buffer(controller, xfer, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spi_qup_fifo_write(struct spi_qup *controller,
|
|
||||||
- struct spi_transfer *xfer)
|
|
||||||
+ struct spi_transfer *xfer)
|
|
||||||
{
|
|
||||||
- const u8 *tx_buf = xfer->tx_buf;
|
|
||||||
- u32 word, state, data;
|
|
||||||
- int idx, w_size;
|
|
||||||
+ u32 data;
|
|
||||||
|
|
||||||
- w_size = controller->w_size;
|
|
||||||
+ /* clear service request */
|
|
||||||
+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
|
|
||||||
+ controller->base + QUP_OPERATIONAL);
|
|
||||||
|
|
||||||
while (controller->tx_bytes < xfer->len) {
|
|
||||||
|
|
||||||
- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
|
||||||
- if (state & QUP_OP_OUT_FIFO_FULL)
|
|
||||||
+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
|
|
||||||
+ QUP_OP_OUT_FIFO_FULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
- word = 0;
|
|
||||||
- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
|
|
||||||
+ spi_qup_prepare_write_data(controller, xfer, &data);
|
|
||||||
+ writel_relaxed(data, controller->base + QUP_OUTPUT_FIFO);
|
|
||||||
|
|
||||||
- if (!tx_buf) {
|
|
||||||
- controller->tx_bytes += w_size;
|
|
||||||
- break;
|
|
||||||
- }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
|
|
||||||
- data = tx_buf[controller->tx_bytes];
|
|
||||||
- word |= data << (BITS_PER_BYTE * (3 - idx));
|
|
||||||
- }
|
|
||||||
+static void spi_qup_block_read(struct spi_qup *controller,
|
|
||||||
+ struct spi_transfer *xfer)
|
|
||||||
+{
|
|
||||||
+ u32 data;
|
|
||||||
+ u32 reads_per_blk = controller->in_blk_sz >> 2;
|
|
||||||
+ u32 num_words = (xfer->len - controller->rx_bytes) / controller->w_size;
|
|
||||||
+ int i;
|
|
||||||
+
|
|
||||||
+ do {
|
|
||||||
+ /* ACK by clearing service flag */
|
|
||||||
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
|
||||||
+ controller->base + QUP_OPERATIONAL);
|
|
||||||
+
|
|
||||||
+ /* transfer up to a block size of data in a single pass */
|
|
||||||
+ for (i = 0; num_words && i < reads_per_blk; i++, num_words--) {
|
|
||||||
+
|
|
||||||
+ /* read data and fill up rx buffer */
|
|
||||||
+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
|
||||||
+ spi_qup_fill_read_buffer(controller, xfer, data);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check to see if next block is ready */
|
|
||||||
+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
|
|
||||||
+ QUP_OP_IN_BLOCK_READ_REQ))
|
|
||||||
+ break;
|
|
||||||
|
|
||||||
- writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
|
|
||||||
- }
|
|
||||||
+ } while (num_words);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
|
|
||||||
+ * reads, it has to be cleared again at the very end
|
|
||||||
+ */
|
|
||||||
+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
|
|
||||||
+ QUP_OP_MAX_INPUT_DONE_FLAG)
|
|
||||||
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
|
||||||
+ controller->base + QUP_OPERATIONAL);
|
|
||||||
+
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void spi_qup_block_write(struct spi_qup *controller,
|
|
||||||
+ struct spi_transfer *xfer)
|
|
||||||
+{
|
|
||||||
+ u32 data;
|
|
||||||
+ u32 writes_per_blk = controller->out_blk_sz >> 2;
|
|
||||||
+ u32 num_words = (xfer->len - controller->tx_bytes) / controller->w_size;
|
|
||||||
+ int i;
|
|
||||||
+
|
|
||||||
+ do {
|
|
||||||
+ /* ACK by clearing service flag */
|
|
||||||
+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
|
|
||||||
+ controller->base + QUP_OPERATIONAL);
|
|
||||||
+
|
|
||||||
+ /* transfer up to a block size of data in a single pass */
|
|
||||||
+ for (i = 0; num_words && i < writes_per_blk; i++, num_words--) {
|
|
||||||
+
|
|
||||||
+ /* swizzle the bytes for output and write out */
|
|
||||||
+ spi_qup_prepare_write_data(controller, xfer, &data);
|
|
||||||
+ writel_relaxed(data,
|
|
||||||
+ controller->base + QUP_OUTPUT_FIFO);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check to see if next block is ready */
|
|
||||||
+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
|
|
||||||
+ QUP_OP_OUT_BLOCK_WRITE_REQ))
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ } while (num_words);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qup_dma_callback(void *data)
|
|
||||||
@@ -515,9 +600,9 @@ static irqreturn_t spi_qup_qup_irq(int i
|
|
||||||
|
|
||||||
writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
|
|
||||||
writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
|
|
||||||
- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
|
||||||
|
|
||||||
if (!xfer) {
|
|
||||||
+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
|
||||||
dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
|
|
||||||
qup_err, spi_err, opflags);
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
@@ -546,11 +631,19 @@ static irqreturn_t spi_qup_qup_irq(int i
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!controller->use_dma) {
|
|
||||||
- if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
|
||||||
- spi_qup_fifo_read(controller, xfer);
|
|
||||||
+ if (opflags & QUP_OP_IN_SERVICE_FLAG) {
|
|
||||||
+ if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
|
|
||||||
+ spi_qup_block_read(controller, xfer);
|
|
||||||
+ else
|
|
||||||
+ spi_qup_fifo_read(controller, xfer);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
|
||||||
- spi_qup_fifo_write(controller, xfer);
|
|
||||||
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG) {
|
|
||||||
+ if (opflags & QUP_OP_OUT_BLOCK_WRITE_REQ)
|
|
||||||
+ spi_qup_block_write(controller, xfer);
|
|
||||||
+ else
|
|
||||||
+ spi_qup_fifo_write(controller, xfer);
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_irqsave(&controller->lock, flags);
|
|
||||||
@@ -558,7 +651,8 @@ static irqreturn_t spi_qup_qup_irq(int i
|
|
||||||
controller->xfer = xfer;
|
|
||||||
spin_unlock_irqrestore(&controller->lock, flags);
|
|
||||||
|
|
||||||
- if (controller->rx_bytes == xfer->len || error)
|
|
||||||
+ if ((controller->rx_bytes == xfer->len &&
|
|
||||||
+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
|
|
||||||
complete(&controller->done);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
@@ -569,7 +663,7 @@ static irqreturn_t spi_qup_qup_irq(int i
|
|
||||||
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
|
||||||
{
|
|
||||||
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
|
||||||
- u32 config, iomode, mode;
|
|
||||||
+ u32 config, iomode;
|
|
||||||
int ret, n_words, w_size;
|
|
||||||
size_t dma_align = dma_get_cache_alignment();
|
|
||||||
u32 dma_available = 0;
|
|
||||||
@@ -607,7 +701,7 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
dma_available = 1;
|
|
||||||
|
|
||||||
if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
|
|
||||||
- mode = QUP_IO_M_MODE_FIFO;
|
|
||||||
+ controller->mode = QUP_IO_M_MODE_FIFO;
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
|
|
||||||
/* must be zero for FIFO */
|
|
||||||
@@ -615,7 +709,7 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
|
||||||
controller->use_dma = 0;
|
|
||||||
} else if (!dma_available) {
|
|
||||||
- mode = QUP_IO_M_MODE_BLOCK;
|
|
||||||
+ controller->mode = QUP_IO_M_MODE_BLOCK;
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
|
||||||
/* must be zero for BLOCK and BAM */
|
|
||||||
@@ -623,7 +717,7 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
|
||||||
controller->use_dma = 0;
|
|
||||||
} else {
|
|
||||||
- mode = QUP_IO_M_MODE_DMOV;
|
|
||||||
+ controller->mode = QUP_IO_M_MODE_DMOV;
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
|
||||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
|
||||||
controller->use_dma = 1;
|
|
||||||
@@ -638,8 +732,8 @@ static int spi_qup_io_config(struct spi_
|
|
||||||
else
|
|
||||||
iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
|
|
||||||
|
|
||||||
- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
|
|
||||||
- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
|
||||||
+ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
|
|
||||||
+ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
|
||||||
|
|
||||||
writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
|
|
||||||
|
|
||||||
@@ -724,7 +818,8 @@ static int spi_qup_transfer_one(struct s
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
- spi_qup_fifo_write(controller, xfer);
|
|
||||||
+ if (controller->mode == QUP_IO_M_MODE_FIFO)
|
|
||||||
+ spi_qup_fifo_write(controller, xfer);
|
|
||||||
|
|
||||||
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
|
||||||
dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
|
||||||
@@ -741,6 +836,7 @@ exit:
|
|
||||||
if (!ret)
|
|
||||||
ret = controller->error;
|
|
||||||
spin_unlock_irqrestore(&controller->lock, flags);
|
|
||||||
+
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Andy Gross <agross@codeaurora.org>
|
|
||||||
Date: Thu, 15 Jan 2015 17:56:02 -0800
|
|
||||||
Subject: [PATCH] spi: qup: Ensure done detection
|
|
||||||
|
|
||||||
This patch fixes an issue where a SPI transaction has completed, but the done
|
|
||||||
condition is missed. This occurs because at the time of interrupt the
|
|
||||||
MAX_INPUT_DONE_FLAG is not asserted. However, in the process of reading blocks
|
|
||||||
of data from the FIFO, the last portion of data comes in.
|
|
||||||
|
|
||||||
The opflags read at the beginning of the irq handler no longer matches the
|
|
||||||
current opflag state. To get around this condition, the block read function
|
|
||||||
should update the opflags so that done detection is correct after the return.
|
|
||||||
|
|
||||||
Change-Id: If109e0eeb432f96000d765c4b34dbb2269f8093f
|
|
||||||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/spi/spi-qup.c | 12 +++++++-----
|
|
||||||
1 file changed, 7 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/spi/spi-qup.c
|
|
||||||
+++ b/drivers/spi/spi-qup.c
|
|
||||||
@@ -298,7 +298,7 @@ static void spi_qup_fifo_write(struct sp
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spi_qup_block_read(struct spi_qup *controller,
|
|
||||||
- struct spi_transfer *xfer)
|
|
||||||
+ struct spi_transfer *xfer, u32 *opflags)
|
|
||||||
{
|
|
||||||
u32 data;
|
|
||||||
u32 reads_per_blk = controller->in_blk_sz >> 2;
|
|
||||||
@@ -327,10 +327,12 @@ static void spi_qup_block_read(struct sp
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
|
|
||||||
- * reads, it has to be cleared again at the very end
|
|
||||||
+ * reads, it has to be cleared again at the very end. However, be sure
|
|
||||||
+ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
|
|
||||||
+ * present and this is used to determine if transaction is complete
|
|
||||||
*/
|
|
||||||
- if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
|
|
||||||
- QUP_OP_MAX_INPUT_DONE_FLAG)
|
|
||||||
+ *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
|
||||||
+ if (*opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
|
|
||||||
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
|
||||||
controller->base + QUP_OPERATIONAL);
|
|
||||||
|
|
||||||
@@ -633,7 +635,7 @@ static irqreturn_t spi_qup_qup_irq(int i
|
|
||||||
if (!controller->use_dma) {
|
|
||||||
if (opflags & QUP_OP_IN_SERVICE_FLAG) {
|
|
||||||
if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
|
|
||||||
- spi_qup_block_read(controller, xfer);
|
|
||||||
+ spi_qup_block_read(controller, xfer, &opflags);
|
|
||||||
else
|
|
||||||
spi_qup_fifo_read(controller, xfer);
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Thu, 19 Feb 2015 20:19:30 -0800
|
|
||||||
Subject: [PATCH 1/3] watchdog: qcom: use timer devicetree binding
|
|
||||||
|
|
||||||
MSM watchdog configuration happens in the same register block as the
|
|
||||||
timer, so we'll use the same binding as the existing timer.
|
|
||||||
|
|
||||||
The qcom-wdt will now be probed when devicetree has an entry compatible
|
|
||||||
with "qcom,kpss-timer" or "qcom-scss-timer".
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------
|
|
||||||
1 file changed, 15 insertions(+), 6 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/watchdog/qcom-wdt.c
|
|
||||||
+++ b/drivers/watchdog/qcom-wdt.c
|
|
||||||
@@ -20,9 +20,9 @@
|
|
||||||
#include <linux/reboot.h>
|
|
||||||
#include <linux/watchdog.h>
|
|
||||||
|
|
||||||
-#define WDT_RST 0x0
|
|
||||||
-#define WDT_EN 0x8
|
|
||||||
-#define WDT_BITE_TIME 0x24
|
|
||||||
+#define WDT_RST 0x38
|
|
||||||
+#define WDT_EN 0x40
|
|
||||||
+#define WDT_BITE_TIME 0x5C
|
|
||||||
|
|
||||||
struct qcom_wdt {
|
|
||||||
struct watchdog_device wdd;
|
|
||||||
@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platfor
|
|
||||||
{
|
|
||||||
struct qcom_wdt *wdt;
|
|
||||||
struct resource *res;
|
|
||||||
+ struct device_node *np = pdev->dev.of_node;
|
|
||||||
+ u32 percpu_offset;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
|
|
||||||
@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platfor
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+
|
|
||||||
+ /* We use CPU0's DGT for the watchdog */
|
|
||||||
+ if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
|
|
||||||
+ percpu_offset = 0;
|
|
||||||
+
|
|
||||||
+ res->start += percpu_offset;
|
|
||||||
+ res->end += percpu_offset;
|
|
||||||
+
|
|
||||||
wdt->base = devm_ioremap_resource(&pdev->dev, res);
|
|
||||||
if (IS_ERR(wdt->base))
|
|
||||||
return PTR_ERR(wdt->base);
|
|
||||||
@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platfo
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id qcom_wdt_of_table[] = {
|
|
||||||
- { .compatible = "qcom,kpss-wdt-msm8960", },
|
|
||||||
- { .compatible = "qcom,kpss-wdt-apq8064", },
|
|
||||||
- { .compatible = "qcom,kpss-wdt-ipq8064", },
|
|
||||||
+ { .compatible = "qcom,kpss-timer" },
|
|
||||||
+ { .compatible = "qcom,scss-timer" },
|
|
||||||
{ },
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
|
|
|
@ -1,48 +0,0 @@
|
||||||
From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Thu, 19 Feb 2015 20:27:39 -0800
|
|
||||||
Subject: [PATCH 2/3] ARM: qcom: add description of KPSS WDT for IPQ8064
|
|
||||||
|
|
||||||
Add the watchdog related entries to the Krait Processor Sub-system
|
|
||||||
(KPSS) timer IPQ8064 devicetree section. Also, add a fixed-clock
|
|
||||||
description of SLEEP_CLK, which will do for now.
|
|
||||||
|
|
||||||
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++-
|
|
||||||
1 file changed, 13 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -60,6 +60,14 @@
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
+ clocks {
|
|
||||||
+ sleep_clk: sleep_clk {
|
|
||||||
+ compatible = "fixed-clock";
|
|
||||||
+ clock-frequency = <32768>;
|
|
||||||
+ #clock-cells = <0>;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
soc: soc {
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <1>;
|
|
||||||
@@ -89,10 +97,14 @@
|
|
||||||
compatible = "qcom,kpss-timer", "qcom,msm-timer";
|
|
||||||
interrupts = <1 1 0x301>,
|
|
||||||
<1 2 0x301>,
|
|
||||||
- <1 3 0x301>;
|
|
||||||
+ <1 3 0x301>,
|
|
||||||
+ <1 4 0x301>,
|
|
||||||
+ <1 5 0x301>;
|
|
||||||
reg = <0x0200a000 0x100>;
|
|
||||||
clock-frequency = <25000000>,
|
|
||||||
<32768>;
|
|
||||||
+ clocks = <&sleep_clk>;
|
|
||||||
+ clock-names = "sleep";
|
|
||||||
cpu-offset = <0x80000>;
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Thu, 19 Feb 2015 20:36:27 -0800
|
|
||||||
Subject: [PATCH 3/3] ARM: msm: add watchdog entries to DT timer binding doc
|
|
||||||
|
|
||||||
The watchdog has been reworked to use the same DT node as the timer.
|
|
||||||
This change is updating the device tree doc accordingly.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
Documentation/devicetree/bindings/arm/msm/timer.txt | 16 +++++++++++++---
|
|
||||||
1 file changed, 13 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
--- a/Documentation/devicetree/bindings/arm/msm/timer.txt
|
|
||||||
+++ b/Documentation/devicetree/bindings/arm/msm/timer.txt
|
|
||||||
@@ -9,11 +9,17 @@ Properties:
|
|
||||||
"qcom,scss-timer" - scorpion subsystem
|
|
||||||
|
|
||||||
- interrupts : Interrupts for the the debug timer, the first general purpose
|
|
||||||
- timer, and optionally a second general purpose timer in that
|
|
||||||
- order.
|
|
||||||
+ timer, and optionally a second general purpose timer, and
|
|
||||||
+ optionally as well, 2 watchdog interrupts, in that order.
|
|
||||||
|
|
||||||
- reg : Specifies the base address of the timer registers.
|
|
||||||
|
|
||||||
+- clocks: Reference to the parent clocks, one per output clock. The parents
|
|
||||||
+ must appear in the same order as the clock names.
|
|
||||||
+
|
|
||||||
+- clock-names: The name of the clocks as free-form strings. They should be in
|
|
||||||
+ the same order as the clocks.
|
|
||||||
+
|
|
||||||
- clock-frequency : The frequency of the debug timer and the general purpose
|
|
||||||
timer(s) in Hz in that order.
|
|
||||||
|
|
||||||
@@ -29,9 +35,13 @@ Example:
|
|
||||||
compatible = "qcom,scss-timer", "qcom,msm-timer";
|
|
||||||
interrupts = <1 1 0x301>,
|
|
||||||
<1 2 0x301>,
|
|
||||||
- <1 3 0x301>;
|
|
||||||
+ <1 3 0x301>,
|
|
||||||
+ <1 4 0x301>,
|
|
||||||
+ <1 5 0x301>;
|
|
||||||
reg = <0x0200a000 0x100>;
|
|
||||||
clock-frequency = <19200000>,
|
|
||||||
<32768>;
|
|
||||||
+ clocks = <&sleep_clk>;
|
|
||||||
+ clock-names = "sleep";
|
|
||||||
cpu-offset = <0x40000>;
|
|
||||||
};
|
|
|
@ -1,46 +0,0 @@
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
@@ -14,6 +14,14 @@
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
+ alias {
|
|
||||||
+ serial0 = &uart4;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ chosen {
|
|
||||||
+ linux,stdout-path = "serial0:115200n8";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
soc {
|
|
||||||
pinmux@800000 {
|
|
||||||
i2c4_pins: i2c4_pinmux {
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -140,7 +140,7 @@
|
|
||||||
ranges;
|
|
||||||
status = "disabled";
|
|
||||||
|
|
||||||
- serial@12490000 {
|
|
||||||
+ uart2: serial@12490000 {
|
|
||||||
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
|
|
||||||
reg = <0x12490000 0x1000>,
|
|
||||||
<0x12480000 0x1000>;
|
|
||||||
@@ -175,7 +175,7 @@
|
|
||||||
ranges;
|
|
||||||
status = "disabled";
|
|
||||||
|
|
||||||
- serial@16340000 {
|
|
||||||
+ uart4: serial@16340000 {
|
|
||||||
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
|
|
||||||
reg = <0x16340000 0x1000>,
|
|
||||||
<0x16300000 0x1000>;
|
|
||||||
@@ -209,7 +209,7 @@
|
|
||||||
ranges;
|
|
||||||
status = "disabled";
|
|
||||||
|
|
||||||
- serial@1a240000 {
|
|
||||||
+ uart5: serial@1a240000 {
|
|
||||||
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
|
|
||||||
reg = <0x1a240000 0x1000>,
|
|
||||||
<0x1a200000 0x1000>;
|
|
|
@ -1,35 +0,0 @@
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
@@ -78,13 +78,28 @@
|
|
||||||
reg = <0>;
|
|
||||||
|
|
||||||
partition@0 {
|
|
||||||
- label = "rootfs";
|
|
||||||
- reg = <0x0 0x1000000>;
|
|
||||||
+ label = "lowlevel_init";
|
|
||||||
+ reg = <0x0 0x1b0000>;
|
|
||||||
};
|
|
||||||
|
|
||||||
partition@1 {
|
|
||||||
- label = "scratch";
|
|
||||||
- reg = <0x1000000 0x1000000>;
|
|
||||||
+ label = "u-boot";
|
|
||||||
+ reg = <0x1b0000 0x80000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ partition@2 {
|
|
||||||
+ label = "u-boot-env";
|
|
||||||
+ reg = <0x230000 0x40000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ partition@3 {
|
|
||||||
+ label = "caldata";
|
|
||||||
+ reg = <0x270000 0x40000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ partition@4 {
|
|
||||||
+ label = "firmware";
|
|
||||||
+ reg = <0x2b0000 0x1d50000>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,160 +0,0 @@
|
||||||
From a32d6e7c8fca6371a2614924b89981bc912b6378 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Tue, 7 Apr 2015 19:58:58 -0700
|
|
||||||
Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree
|
|
||||||
|
|
||||||
Add basic DB149 (IPQ806x based platform) device-tree. It supports UART,
|
|
||||||
SATA, USB2, USB3 and NOR flash.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/boot/dts/Makefile | 1 +
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064-db149.dts | 257 +++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 258 insertions(+)
|
|
||||||
create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
|
|
||||||
--- a/arch/arm/boot/dts/Makefile
|
|
||||||
+++ b/arch/arm/boot/dts/Makefile
|
|
||||||
@@ -360,6 +360,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
|
|
||||||
qcom-apq8084-ifc6540.dtb \
|
|
||||||
qcom-apq8084-mtp.dtb \
|
|
||||||
qcom-ipq8064-ap148.dtb \
|
|
||||||
+ qcom-ipq8064-db149.dtb \
|
|
||||||
qcom-msm8660-surf.dtb \
|
|
||||||
qcom-msm8960-cdp.dtb \
|
|
||||||
qcom-msm8974-sony-xperia-honami.dtb
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
@@ -0,0 +1,132 @@
|
|
||||||
+#include "qcom-ipq8064-v1.0.dtsi"
|
|
||||||
+
|
|
||||||
+/ {
|
|
||||||
+ model = "Qualcomm IPQ8064/DB149";
|
|
||||||
+ compatible = "qcom,ipq8064-db149", "qcom,ipq8064";
|
|
||||||
+
|
|
||||||
+ reserved-memory {
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <1>;
|
|
||||||
+ ranges;
|
|
||||||
+ rsvd@41200000 {
|
|
||||||
+ reg = <0x41200000 0x300000>;
|
|
||||||
+ no-map;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ alias {
|
|
||||||
+ serial0 = &uart2;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ chosen {
|
|
||||||
+ linux,stdout-path = "serial0:115200n8";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ soc {
|
|
||||||
+ pinmux@800000 {
|
|
||||||
+ i2c4_pins: i2c4_pinmux {
|
|
||||||
+ pins = "gpio12", "gpio13";
|
|
||||||
+ function = "gsbi4";
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ spi_pins: spi_pins {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio18", "gpio19", "gpio21";
|
|
||||||
+ function = "gsbi5";
|
|
||||||
+ drive-strength = <10>;
|
|
||||||
+ bias-none;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gsbi2: gsbi@12480000 {
|
|
||||||
+ qcom,mode = <GSBI_PROT_I2C_UART>;
|
|
||||||
+ status = "ok";
|
|
||||||
+ uart2: serial@12490000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gsbi5: gsbi@1a200000 {
|
|
||||||
+ qcom,mode = <GSBI_PROT_SPI>;
|
|
||||||
+ status = "ok";
|
|
||||||
+
|
|
||||||
+ spi4: spi@1a280000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ spi-max-frequency = <50000000>;
|
|
||||||
+
|
|
||||||
+ pinctrl-0 = <&spi_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+
|
|
||||||
+ cs-gpios = <&qcom_pinmux 20 0>;
|
|
||||||
+
|
|
||||||
+ flash: m25p80@0 {
|
|
||||||
+ compatible = "s25fl256s1";
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <1>;
|
|
||||||
+ spi-max-frequency = <50000000>;
|
|
||||||
+ reg = <0>;
|
|
||||||
+ m25p,fast-read;
|
|
||||||
+
|
|
||||||
+ partition@0 {
|
|
||||||
+ label = "lowlevel_init";
|
|
||||||
+ reg = <0x0 0x1b0000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ partition@1 {
|
|
||||||
+ label = "u-boot";
|
|
||||||
+ reg = <0x1b0000 0x80000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ partition@2 {
|
|
||||||
+ label = "u-boot-env";
|
|
||||||
+ reg = <0x230000 0x40000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ partition@3 {
|
|
||||||
+ label = "caldata";
|
|
||||||
+ reg = <0x270000 0x40000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ partition@4 {
|
|
||||||
+ label = "firmware";
|
|
||||||
+ reg = <0x2b0000 0x1d50000>;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ sata-phy@1b400000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ sata@29000000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy@100f8800 { /* USB3 port 1 HS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy@100f8830 { /* USB3 port 1 SS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy@110f8800 { /* USB3 port 0 HS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy@110f8830 { /* USB3 port 0 SS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ usb30@0 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ usb30@1 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+};
|
|
|
@ -1,53 +0,0 @@
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
@@ -46,15 +46,12 @@
|
|
||||||
serial@16340000 {
|
|
||||||
status = "ok";
|
|
||||||
};
|
|
||||||
-
|
|
||||||
- i2c4: i2c@16380000 {
|
|
||||||
- status = "ok";
|
|
||||||
-
|
|
||||||
- clock-frequency = <200000>;
|
|
||||||
-
|
|
||||||
- pinctrl-0 = <&i2c4_pins>;
|
|
||||||
- pinctrl-names = "default";
|
|
||||||
- };
|
|
||||||
+ /*
|
|
||||||
+ * The i2c device on gsbi4 should not be enabled.
|
|
||||||
+ * On ipq806x designs gsbi4 i2c is meant for exclusive
|
|
||||||
+ * RPM usage. Turning this on in kernel manifests as
|
|
||||||
+ * i2c failure for the RPM.
|
|
||||||
+ */
|
|
||||||
};
|
|
||||||
|
|
||||||
gsbi5: gsbi@1a200000 {
|
|
||||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
|
||||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
|
||||||
@@ -794,7 +794,7 @@ static struct clk_rcg gsbi7_qup_src = {
|
|
||||||
.parent_names = gcc_pxo_pll8,
|
|
||||||
.num_parents = 2,
|
|
||||||
.ops = &clk_rcg_ops,
|
|
||||||
- .flags = CLK_SET_PARENT_GATE,
|
|
||||||
+ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -810,7 +810,7 @@ static struct clk_branch gsbi7_qup_clk =
|
|
||||||
.parent_names = (const char *[]){ "gsbi7_qup_src" },
|
|
||||||
.num_parents = 1,
|
|
||||||
.ops = &clk_branch_ops,
|
|
||||||
- .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -858,7 +858,7 @@ static struct clk_branch gsbi4_h_clk = {
|
|
||||||
.hw.init = &(struct clk_init_data){
|
|
||||||
.name = "gsbi4_h_clk",
|
|
||||||
.ops = &clk_branch_ops,
|
|
||||||
- .flags = CLK_IS_ROOT,
|
|
||||||
+ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,511 +0,0 @@
|
||||||
--- a/drivers/phy/Kconfig
|
|
||||||
+++ b/drivers/phy/Kconfig
|
|
||||||
@@ -256,4 +256,15 @@ config PHY_STIH41X_USB
|
|
||||||
Enable this to support the USB transceiver that is part of
|
|
||||||
STMicroelectronics STiH41x SoC series.
|
|
||||||
|
|
||||||
+config PHY_QCOM_DWC3
|
|
||||||
+ tristate "QCOM DWC3 USB PHY support"
|
|
||||||
+ depends on ARCH_QCOM
|
|
||||||
+ depends on HAS_IOMEM
|
|
||||||
+ depends on OF
|
|
||||||
+ select GENERIC_PHY
|
|
||||||
+ help
|
|
||||||
+ This option enables support for the Synopsis PHYs present inside the
|
|
||||||
+ Qualcomm USB3.0 DWC3 controller. This driver supports both HS and SS
|
|
||||||
+ PHY controllers.
|
|
||||||
+
|
|
||||||
endmenu
|
|
||||||
--- a/drivers/phy/Makefile
|
|
||||||
+++ b/drivers/phy/Makefile
|
|
||||||
@@ -31,3 +31,4 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) +=
|
|
||||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
|
||||||
obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
|
|
||||||
obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o
|
|
||||||
+obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/phy/phy-qcom-dwc3.c
|
|
||||||
@@ -0,0 +1,483 @@
|
|
||||||
+/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/of.h>
|
|
||||||
+#include <linux/phy/phy.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * USB QSCRATCH Hardware registers
|
|
||||||
+ */
|
|
||||||
+#define QSCRATCH_GENERAL_CFG (0x08)
|
|
||||||
+#define HSUSB_PHY_CTRL_REG (0x10)
|
|
||||||
+
|
|
||||||
+/* PHY_CTRL_REG */
|
|
||||||
+#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24)
|
|
||||||
+#define HSUSB_CTRL_USB2_SUSPEND BIT(23)
|
|
||||||
+#define HSUSB_CTRL_UTMI_CLK_EN BIT(21)
|
|
||||||
+#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20)
|
|
||||||
+#define HSUSB_CTRL_USE_CLKCORE BIT(18)
|
|
||||||
+#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17)
|
|
||||||
+#define HSUSB_CTRL_COMMONONN BIT(11)
|
|
||||||
+#define HSUSB_CTRL_ID_HV_CLAMP BIT(9)
|
|
||||||
+#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8)
|
|
||||||
+#define HSUSB_CTRL_CLAMP_EN BIT(7)
|
|
||||||
+#define HSUSB_CTRL_RETENABLEN BIT(1)
|
|
||||||
+#define HSUSB_CTRL_POR BIT(0)
|
|
||||||
+
|
|
||||||
+/* QSCRATCH_GENERAL_CFG */
|
|
||||||
+#define HSUSB_GCFG_XHCI_REV BIT(2)
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * USB QSCRATCH Hardware registers
|
|
||||||
+ */
|
|
||||||
+#define SSUSB_PHY_CTRL_REG (0x00)
|
|
||||||
+#define SSUSB_PHY_PARAM_CTRL_1 (0x04)
|
|
||||||
+#define SSUSB_PHY_PARAM_CTRL_2 (0x08)
|
|
||||||
+#define CR_PROTOCOL_DATA_IN_REG (0x0c)
|
|
||||||
+#define CR_PROTOCOL_DATA_OUT_REG (0x10)
|
|
||||||
+#define CR_PROTOCOL_CAP_ADDR_REG (0x14)
|
|
||||||
+#define CR_PROTOCOL_CAP_DATA_REG (0x18)
|
|
||||||
+#define CR_PROTOCOL_READ_REG (0x1c)
|
|
||||||
+#define CR_PROTOCOL_WRITE_REG (0x20)
|
|
||||||
+
|
|
||||||
+/* PHY_CTRL_REG */
|
|
||||||
+#define SSUSB_CTRL_REF_USE_PAD BIT(28)
|
|
||||||
+#define SSUSB_CTRL_TEST_POWERDOWN BIT(27)
|
|
||||||
+#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24)
|
|
||||||
+#define SSUSB_CTRL_SS_PHY_EN BIT(8)
|
|
||||||
+#define SSUSB_CTRL_SS_PHY_RESET BIT(7)
|
|
||||||
+
|
|
||||||
+/* SSPHY control registers */
|
|
||||||
+#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * lane)
|
|
||||||
+#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * lane)
|
|
||||||
+
|
|
||||||
+/* RX OVRD IN HI bits */
|
|
||||||
+#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13)
|
|
||||||
+#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12)
|
|
||||||
+#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11)
|
|
||||||
+#define RX_OVRD_IN_HI_RX_EQ_MASK 0x0700
|
|
||||||
+#define RX_OVRD_IN_HI_RX_EQ_SHIFT 8
|
|
||||||
+#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7)
|
|
||||||
+#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6)
|
|
||||||
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5)
|
|
||||||
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK 0x0018
|
|
||||||
+#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2)
|
|
||||||
+#define RX_OVRD_IN_HI_RX_RATE_MASK 0x0003
|
|
||||||
+
|
|
||||||
+/* TX OVRD DRV LO register bits */
|
|
||||||
+#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F
|
|
||||||
+#define TX_OVRD_DRV_LO_PREEMPH_MASK 0x3F80
|
|
||||||
+#define TX_OVRD_DRV_LO_PREEMPH_SHIFT 7
|
|
||||||
+#define TX_OVRD_DRV_LO_EN BIT(14)
|
|
||||||
+
|
|
||||||
+struct qcom_dwc3_usb_phy {
|
|
||||||
+ void __iomem *base;
|
|
||||||
+ struct device *dev;
|
|
||||||
+ struct phy *phy;
|
|
||||||
+
|
|
||||||
+ int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3);
|
|
||||||
+ int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3);
|
|
||||||
+
|
|
||||||
+ struct clk *xo_clk;
|
|
||||||
+ struct clk *ref_clk;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * Write register and read back masked value to confirm it is written
|
|
||||||
+ *
|
|
||||||
+ * @base - QCOM DWC3 PHY base virtual address.
|
|
||||||
+ * @offset - register offset.
|
|
||||||
+ * @mask - register bitmask specifying what should be updated
|
|
||||||
+ * @val - value to write.
|
|
||||||
+ */
|
|
||||||
+static inline void qcom_dwc3_phy_write_readback(
|
|
||||||
+ struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
|
|
||||||
+ const u32 mask, u32 val)
|
|
||||||
+{
|
|
||||||
+ u32 write_val, tmp = readl(phy_dwc3->base + offset);
|
|
||||||
+
|
|
||||||
+ tmp &= ~mask; /* retain other bits */
|
|
||||||
+ write_val = tmp | val;
|
|
||||||
+
|
|
||||||
+ writel(write_val, phy_dwc3->base + offset);
|
|
||||||
+
|
|
||||||
+ /* Read back to see if val was written */
|
|
||||||
+ tmp = readl(phy_dwc3->base + offset);
|
|
||||||
+ tmp &= mask; /* clear other bits */
|
|
||||||
+
|
|
||||||
+ if (tmp != val)
|
|
||||||
+ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
|
|
||||||
+ val, offset);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_for_latch(void __iomem *addr)
|
|
||||||
+{
|
|
||||||
+ u32 retry = 10;
|
|
||||||
+
|
|
||||||
+ while (true) {
|
|
||||||
+ if (!readl(addr))
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ if (--retry == 0)
|
|
||||||
+ return -ETIMEDOUT;
|
|
||||||
+
|
|
||||||
+ usleep_range(10, 20);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * Write SSPHY register
|
|
||||||
+ *
|
|
||||||
+ * @base - QCOM DWC3 PHY base virtual address.
|
|
||||||
+ * @addr - SSPHY address to write.
|
|
||||||
+ * @val - value to write.
|
|
||||||
+ */
|
|
||||||
+static int qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
|
|
||||||
+ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_wait;
|
|
||||||
+
|
|
||||||
+ writel(val, base + CR_PROTOCOL_DATA_IN_REG);
|
|
||||||
+ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_wait;
|
|
||||||
+
|
|
||||||
+ writel(0x1, base + CR_PROTOCOL_WRITE_REG);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG);
|
|
||||||
+
|
|
||||||
+err_wait:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * Read SSPHY register.
|
|
||||||
+ *
|
|
||||||
+ * @base - QCOM DWC3 PHY base virtual address.
|
|
||||||
+ * @addr - SSPHY address to read.
|
|
||||||
+ */
|
|
||||||
+static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ bool first_read = true;
|
|
||||||
+
|
|
||||||
+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
|
|
||||||
+ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_wait;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Due to hardware bug, first read of SSPHY register might be
|
|
||||||
+ * incorrect. Hence as workaround, SW should perform SSPHY register
|
|
||||||
+ * read twice, but use only second read and ignore first read.
|
|
||||||
+ */
|
|
||||||
+retry:
|
|
||||||
+ writel(0x1, base + CR_PROTOCOL_READ_REG);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_wait;
|
|
||||||
+
|
|
||||||
+ if (first_read) {
|
|
||||||
+ readl(base + CR_PROTOCOL_DATA_OUT_REG);
|
|
||||||
+ first_read = false;
|
|
||||||
+ goto retry;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
|
|
||||||
+
|
|
||||||
+err_wait:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_phy_power_on(struct phy *phy)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(phy_dwc3->xo_clk);
|
|
||||||
+ if (ret)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(phy_dwc3->ref_clk);
|
|
||||||
+ if (ret)
|
|
||||||
+ clk_disable_unprepare(phy_dwc3->xo_clk);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_phy_power_off(struct phy *phy)
|
|
||||||
+{
|
|
||||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
|
||||||
+
|
|
||||||
+ clk_disable_unprepare(phy_dwc3->ref_clk);
|
|
||||||
+ clk_disable_unprepare(phy_dwc3->xo_clk);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
|
|
||||||
+{
|
|
||||||
+ u32 val;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
|
|
||||||
+ * enable clamping, and disable RETENTION (power-on default is ENABLED)
|
|
||||||
+ */
|
|
||||||
+ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
|
|
||||||
+ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN |
|
|
||||||
+ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
|
|
||||||
+ HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
|
|
||||||
+ HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
|
|
||||||
+
|
|
||||||
+ /* use core clock if external reference is not present */
|
|
||||||
+ if (!phy_dwc3->xo_clk)
|
|
||||||
+ val |= HSUSB_CTRL_USE_CLKCORE;
|
|
||||||
+
|
|
||||||
+ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
|
|
||||||
+ usleep_range(2000, 2200);
|
|
||||||
+
|
|
||||||
+ /* Disable (bypass) VBUS and ID filters */
|
|
||||||
+ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ u32 data = 0;
|
|
||||||
+
|
|
||||||
+ /* reset phy */
|
|
||||||
+ data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
|
||||||
+ writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET,
|
|
||||||
+ phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
|
||||||
+ usleep_range(2000, 2200);
|
|
||||||
+ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
|
||||||
+
|
|
||||||
+ /* clear REF_PAD if we don't have XO clk */
|
|
||||||
+ if (!phy_dwc3->xo_clk)
|
|
||||||
+ data &= ~SSUSB_CTRL_REF_USE_PAD;
|
|
||||||
+ else
|
|
||||||
+ data |= SSUSB_CTRL_REF_USE_PAD;
|
|
||||||
+
|
|
||||||
+ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
|
||||||
+ msleep(30);
|
|
||||||
+
|
|
||||||
+ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
|
|
||||||
+ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Fix RX Equalization setting as follows
|
|
||||||
+ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
|
|
||||||
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
|
|
||||||
+ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
|
|
||||||
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
|
|
||||||
+ */
|
|
||||||
+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
|
|
||||||
+ SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_phy_trans;
|
|
||||||
+
|
|
||||||
+ data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
|
|
||||||
+ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
|
|
||||||
+ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
|
|
||||||
+ data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
|
|
||||||
+ data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
|
|
||||||
+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
|
|
||||||
+ SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_phy_trans;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Set EQ and TX launch amplitudes as follows
|
|
||||||
+ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
|
|
||||||
+ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
|
|
||||||
+ * LANE0.TX_OVRD_DRV_LO.EN set to 1.
|
|
||||||
+ */
|
|
||||||
+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
|
|
||||||
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_phy_trans;
|
|
||||||
+
|
|
||||||
+ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
|
|
||||||
+ data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
|
|
||||||
+ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
|
|
||||||
+ data |= 0x7f;
|
|
||||||
+ data |= TX_OVRD_DRV_LO_EN;
|
|
||||||
+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
|
|
||||||
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_phy_trans;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
|
|
||||||
+ * TX_FULL_SWING [26:20] amplitude to 127
|
|
||||||
+ * TX_DEEMPH_3_5DB [13:8] to 22
|
|
||||||
+ * LOS_BIAS [2:0] to 0x5
|
|
||||||
+ */
|
|
||||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
|
|
||||||
+ 0x07f03f07, 0x07f01605);
|
|
||||||
+
|
|
||||||
+err_phy_trans:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3)
|
|
||||||
+{
|
|
||||||
+ /* Sequence to put SSPHY in low power state:
|
|
||||||
+ * 1. Clear REF_PHY_EN in PHY_CTRL_REG
|
|
||||||
+ * 2. Clear REF_USE_PAD in PHY_CTRL_REG
|
|
||||||
+ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
|
|
||||||
+ * 4. Disable SSPHY ref clk
|
|
||||||
+ */
|
|
||||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
|
|
||||||
+ SSUSB_CTRL_SS_PHY_EN, 0x0);
|
|
||||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
|
|
||||||
+ SSUSB_CTRL_REF_USE_PAD, 0x0);
|
|
||||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
|
|
||||||
+ 0x0, SSUSB_CTRL_TEST_POWERDOWN);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_phy_init(struct phy *phy)
|
|
||||||
+{
|
|
||||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
|
||||||
+
|
|
||||||
+ if (phy_dwc3->phy_init)
|
|
||||||
+ return phy_dwc3->phy_init(phy_dwc3);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_phy_exit(struct phy *phy)
|
|
||||||
+{
|
|
||||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
|
||||||
+
|
|
||||||
+ if (phy_dwc3->phy_exit)
|
|
||||||
+ return qcom_dwc3_ss_phy_exit(phy_dwc3);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct phy_ops qcom_dwc3_phy_ops = {
|
|
||||||
+ .init = qcom_dwc3_phy_init,
|
|
||||||
+ .exit = qcom_dwc3_phy_exit,
|
|
||||||
+ .power_on = qcom_dwc3_phy_power_on,
|
|
||||||
+ .power_off = qcom_dwc3_phy_power_off,
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct of_device_id qcom_dwc3_phy_table[] = {
|
|
||||||
+ { .compatible = "qcom,dwc3-hs-usb-phy", },
|
|
||||||
+ { .compatible = "qcom,dwc3-ss-usb-phy", },
|
|
||||||
+ { /* Sentinel */ }
|
|
||||||
+};
|
|
||||||
+MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
|
|
||||||
+
|
|
||||||
+static int qcom_dwc3_phy_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct qcom_dwc3_usb_phy *phy_dwc3;
|
|
||||||
+ struct phy_provider *phy_provider;
|
|
||||||
+ struct resource *res;
|
|
||||||
+
|
|
||||||
+ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
|
|
||||||
+ if (!phy_dwc3)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, phy_dwc3);
|
|
||||||
+
|
|
||||||
+ phy_dwc3->dev = &pdev->dev;
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
|
|
||||||
+ if (IS_ERR(phy_dwc3->base))
|
|
||||||
+ return PTR_ERR(phy_dwc3->base);
|
|
||||||
+
|
|
||||||
+ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
|
|
||||||
+ if (IS_ERR(phy_dwc3->ref_clk)) {
|
|
||||||
+ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
|
|
||||||
+ return PTR_ERR(phy_dwc3->ref_clk);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (of_device_is_compatible(pdev->dev.of_node,
|
|
||||||
+ "qcom,dwc3-hs-usb-phy")) {
|
|
||||||
+ clk_set_rate(phy_dwc3->ref_clk, 60000000);
|
|
||||||
+ phy_dwc3->phy_init = qcom_dwc3_hs_phy_init;
|
|
||||||
+ } else if (of_device_is_compatible(pdev->dev.of_node,
|
|
||||||
+ "qcom,dwc3-ss-usb-phy")) {
|
|
||||||
+ phy_dwc3->phy_init = qcom_dwc3_ss_phy_init;
|
|
||||||
+ phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit;
|
|
||||||
+ clk_set_rate(phy_dwc3->ref_clk, 125000000);
|
|
||||||
+ } else {
|
|
||||||
+ dev_err(phy_dwc3->dev, "Unknown phy\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
|
|
||||||
+ if (IS_ERR(phy_dwc3->xo_clk)) {
|
|
||||||
+ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
|
|
||||||
+ phy_dwc3->xo_clk = NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops,
|
|
||||||
+ NULL);
|
|
||||||
+
|
|
||||||
+ if (IS_ERR(phy_dwc3->phy))
|
|
||||||
+ return PTR_ERR(phy_dwc3->phy);
|
|
||||||
+
|
|
||||||
+ phy_set_drvdata(phy_dwc3->phy, phy_dwc3);
|
|
||||||
+
|
|
||||||
+ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
|
|
||||||
+ of_phy_simple_xlate);
|
|
||||||
+
|
|
||||||
+ if (IS_ERR(phy_provider))
|
|
||||||
+ return PTR_ERR(phy_provider);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver qcom_dwc3_phy_driver = {
|
|
||||||
+ .probe = qcom_dwc3_phy_probe,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "qcom-dwc3-usb-phy",
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .of_match_table = qcom_dwc3_phy_table,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+module_platform_driver(qcom_dwc3_phy_driver);
|
|
||||||
+
|
|
||||||
+MODULE_ALIAS("platform:phy-qcom-dwc3");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
+MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
|
|
||||||
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
|
|
||||||
+MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
|
|
|
@ -1,125 +0,0 @@
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
@@ -109,5 +109,29 @@
|
|
||||||
sata@29000000 {
|
|
||||||
status = "ok";
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ phy@100f8800 { /* USB3 port 1 HS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy@100f8830 { /* USB3 port 1 SS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy@110f8800 { /* USB3 port 0 HS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy@110f8830 { /* USB3 port 0 SS phy */
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ usb30@0 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ usb30@1 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -291,5 +291,90 @@
|
|
||||||
#clock-cells = <1>;
|
|
||||||
#reset-cells = <1>;
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ hs_phy_1: phy@100f8800 {
|
|
||||||
+ compatible = "qcom,dwc3-hs-usb-phy";
|
|
||||||
+ reg = <0x100f8800 0x30>;
|
|
||||||
+ clocks = <&gcc USB30_1_UTMI_CLK>;
|
|
||||||
+ clock-names = "ref";
|
|
||||||
+ #phy-cells = <0>;
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ ss_phy_1: phy@100f8830 {
|
|
||||||
+ compatible = "qcom,dwc3-ss-usb-phy";
|
|
||||||
+ reg = <0x100f8830 0x30>;
|
|
||||||
+ clocks = <&gcc USB30_1_MASTER_CLK>;
|
|
||||||
+ clock-names = "ref";
|
|
||||||
+ #phy-cells = <0>;
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ hs_phy_0: phy@110f8800 {
|
|
||||||
+ compatible = "qcom,dwc3-hs-usb-phy";
|
|
||||||
+ reg = <0x110f8800 0x30>;
|
|
||||||
+ clocks = <&gcc USB30_0_UTMI_CLK>;
|
|
||||||
+ clock-names = "ref";
|
|
||||||
+ #phy-cells = <0>;
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ ss_phy_0: phy@110f8830 {
|
|
||||||
+ compatible = "qcom,dwc3-ss-usb-phy";
|
|
||||||
+ reg = <0x110f8830 0x30>;
|
|
||||||
+ clocks = <&gcc USB30_0_MASTER_CLK>;
|
|
||||||
+ clock-names = "ref";
|
|
||||||
+ #phy-cells = <0>;
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ usb3_0: usb30@0 {
|
|
||||||
+ compatible = "qcom,dwc3";
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <1>;
|
|
||||||
+ clocks = <&gcc USB30_0_MASTER_CLK>;
|
|
||||||
+ clock-names = "core";
|
|
||||||
+
|
|
||||||
+ ranges;
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+
|
|
||||||
+ dwc3@11000000 {
|
|
||||||
+ compatible = "snps,dwc3";
|
|
||||||
+ reg = <0x11000000 0xcd00>;
|
|
||||||
+ interrupts = <0 110 0x4>;
|
|
||||||
+ phys = <&hs_phy_0>, <&ss_phy_0>;
|
|
||||||
+ phy-names = "usb2-phy", "usb3-phy";
|
|
||||||
+ tx-fifo-resize;
|
|
||||||
+ dr_mode = "host";
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ usb3_1: usb30@1 {
|
|
||||||
+ compatible = "qcom,dwc3";
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <1>;
|
|
||||||
+ clocks = <&gcc USB30_1_MASTER_CLK>;
|
|
||||||
+ clock-names = "core";
|
|
||||||
+
|
|
||||||
+ ranges;
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+
|
|
||||||
+ dwc3@10000000 {
|
|
||||||
+ compatible = "snps,dwc3";
|
|
||||||
+ reg = <0x10000000 0xcd00>;
|
|
||||||
+ interrupts = <0 205 0x4>;
|
|
||||||
+ phys = <&hs_phy_1>, <&ss_phy_1>;
|
|
||||||
+ phy-names = "usb2-phy", "usb3-phy";
|
|
||||||
+ tx-fifo-resize;
|
|
||||||
+ dr_mode = "host";
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,249 +0,0 @@
|
||||||
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
|
|
||||||
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt
|
|
||||||
@@ -6,7 +6,8 @@ configuration settings. The mode settin
|
|
||||||
the 4 GSBI IOs.
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
-- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064
|
|
||||||
+- compatible: Should contain "qcom,gsbi-v1.0.0"
|
|
||||||
+- cell-index: Should contain the GSBI index
|
|
||||||
- reg: Address range for GSBI registers
|
|
||||||
- clocks: required clock
|
|
||||||
- clock-names: must contain "iface" entry
|
|
||||||
@@ -16,6 +17,8 @@ Required properties:
|
|
||||||
Optional properties:
|
|
||||||
- qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference
|
|
||||||
dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values.
|
|
||||||
+- syscon-tcsr: indicates phandle of TCSR syscon node. Required if child uses
|
|
||||||
+ dma.
|
|
||||||
|
|
||||||
Required properties if child node exists:
|
|
||||||
- #address-cells: Must be 1
|
|
||||||
@@ -39,6 +42,7 @@ Example for APQ8064:
|
|
||||||
|
|
||||||
gsbi4@16300000 {
|
|
||||||
compatible = "qcom,gsbi-v1.0.0";
|
|
||||||
+ cell-index = <4>;
|
|
||||||
reg = <0x16300000 0x100>;
|
|
||||||
clocks = <&gcc GSBI4_H_CLK>;
|
|
||||||
clock-names = "iface";
|
|
||||||
@@ -48,6 +52,8 @@ Example for APQ8064:
|
|
||||||
qcom,mode = <GSBI_PROT_I2C_UART>;
|
|
||||||
qcom,crci = <GSBI_CRCI_QUP>;
|
|
||||||
|
|
||||||
+ syscon-tcsr = <&tcsr>;
|
|
||||||
+
|
|
||||||
/* child nodes go under here */
|
|
||||||
|
|
||||||
i2c_qup4: i2c@16380000 {
|
|
||||||
@@ -76,3 +82,9 @@ Example for APQ8064:
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
+ tcsr: syscon@1a400000 {
|
|
||||||
+ compatible = "qcom,apq8064-tcsr", "syscon";
|
|
||||||
+ reg = <0x1a400000 0x100>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+
|
|
||||||
--- a/drivers/soc/qcom/Kconfig
|
|
||||||
+++ b/drivers/soc/qcom/Kconfig
|
|
||||||
@@ -4,6 +4,7 @@
|
|
||||||
config QCOM_GSBI
|
|
||||||
tristate "QCOM General Serial Bus Interface"
|
|
||||||
depends on ARCH_QCOM
|
|
||||||
+ select MFD_SYSCON
|
|
||||||
help
|
|
||||||
Say y here to enable GSBI support. The GSBI provides control
|
|
||||||
functions for connecting the underlying serial UART, SPI, and I2C
|
|
||||||
--- a/drivers/soc/qcom/qcom_gsbi.c
|
|
||||||
+++ b/drivers/soc/qcom/qcom_gsbi.c
|
|
||||||
@@ -18,22 +18,129 @@
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/of_platform.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
+#include <linux/regmap.h>
|
|
||||||
+#include <linux/mfd/syscon.h>
|
|
||||||
+#include <dt-bindings/soc/qcom,gsbi.h>
|
|
||||||
|
|
||||||
#define GSBI_CTRL_REG 0x0000
|
|
||||||
#define GSBI_PROTOCOL_SHIFT 4
|
|
||||||
+#define MAX_GSBI 12
|
|
||||||
+
|
|
||||||
+#define TCSR_ADM_CRCI_BASE 0x70
|
|
||||||
+
|
|
||||||
+struct crci_config {
|
|
||||||
+ u32 num_rows;
|
|
||||||
+ const u32 (*array)[MAX_GSBI];
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const u32 crci_ipq8064[][MAX_GSBI] = {
|
|
||||||
+ {
|
|
||||||
+ 0x000003, 0x00000c, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000c00, 0x003000, 0x00c000,
|
|
||||||
+ 0x030000, 0x0c0000, 0x300000, 0xc00000
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ 0x000003, 0x00000c, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000c00, 0x003000, 0x00c000,
|
|
||||||
+ 0x030000, 0x0c0000, 0x300000, 0xc00000
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct crci_config config_ipq8064 = {
|
|
||||||
+ .num_rows = ARRAY_SIZE(crci_ipq8064),
|
|
||||||
+ .array = crci_ipq8064,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const unsigned int crci_apq8064[][MAX_GSBI] = {
|
|
||||||
+ {
|
|
||||||
+ 0x001800, 0x006000, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000400, 0x000000, 0x000000,
|
|
||||||
+ 0x000000, 0x000000, 0x000000, 0x000000
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ 0x000000, 0x000000, 0x000000, 0x000000,
|
|
||||||
+ 0x000000, 0x000020, 0x0000c0, 0x000000,
|
|
||||||
+ 0x000000, 0x000000, 0x000000, 0x000000
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct crci_config config_apq8064 = {
|
|
||||||
+ .num_rows = ARRAY_SIZE(crci_apq8064),
|
|
||||||
+ .array = crci_apq8064,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const unsigned int crci_msm8960[][MAX_GSBI] = {
|
|
||||||
+ {
|
|
||||||
+ 0x000003, 0x00000c, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000400, 0x000000, 0x000000,
|
|
||||||
+ 0x000000, 0x000000, 0x000000, 0x000000
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ 0x000000, 0x000000, 0x000000, 0x000000,
|
|
||||||
+ 0x000000, 0x000020, 0x0000c0, 0x000300,
|
|
||||||
+ 0x001800, 0x006000, 0x000000, 0x000000
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct crci_config config_msm8960 = {
|
|
||||||
+ .num_rows = ARRAY_SIZE(crci_msm8960),
|
|
||||||
+ .array = crci_msm8960,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const unsigned int crci_msm8660[][MAX_GSBI] = {
|
|
||||||
+ { /* ADM 0 - B */
|
|
||||||
+ 0x000003, 0x00000c, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000c00, 0x003000, 0x00c000,
|
|
||||||
+ 0x030000, 0x0c0000, 0x300000, 0xc00000
|
|
||||||
+ },
|
|
||||||
+ { /* ADM 0 - B */
|
|
||||||
+ 0x000003, 0x00000c, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000c00, 0x003000, 0x00c000,
|
|
||||||
+ 0x030000, 0x0c0000, 0x300000, 0xc00000
|
|
||||||
+ },
|
|
||||||
+ { /* ADM 1 - A */
|
|
||||||
+ 0x000003, 0x00000c, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000c00, 0x003000, 0x00c000,
|
|
||||||
+ 0x030000, 0x0c0000, 0x300000, 0xc00000
|
|
||||||
+ },
|
|
||||||
+ { /* ADM 1 - B */
|
|
||||||
+ 0x000003, 0x00000c, 0x000030, 0x0000c0,
|
|
||||||
+ 0x000300, 0x000c00, 0x003000, 0x00c000,
|
|
||||||
+ 0x030000, 0x0c0000, 0x300000, 0xc00000
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct crci_config config_msm8660 = {
|
|
||||||
+ .num_rows = ARRAY_SIZE(crci_msm8660),
|
|
||||||
+ .array = crci_msm8660,
|
|
||||||
+};
|
|
||||||
|
|
||||||
struct gsbi_info {
|
|
||||||
struct clk *hclk;
|
|
||||||
u32 mode;
|
|
||||||
u32 crci;
|
|
||||||
+ struct regmap *tcsr;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct of_device_id tcsr_dt_match[] = {
|
|
||||||
+ { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
|
|
||||||
+ { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
|
|
||||||
+ { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
|
|
||||||
+ { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
|
|
||||||
+ { },
|
|
||||||
};
|
|
||||||
|
|
||||||
static int gsbi_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct device_node *node = pdev->dev.of_node;
|
|
||||||
+ struct device_node *tcsr_node;
|
|
||||||
+ const struct of_device_id *match;
|
|
||||||
struct resource *res;
|
|
||||||
void __iomem *base;
|
|
||||||
struct gsbi_info *gsbi;
|
|
||||||
+ int i;
|
|
||||||
+ u32 mask, gsbi_num;
|
|
||||||
+ const struct crci_config *config = NULL;
|
|
||||||
|
|
||||||
gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
|
|
||||||
|
|
||||||
@@ -45,6 +152,32 @@ static int gsbi_probe(struct platform_de
|
|
||||||
if (IS_ERR(base))
|
|
||||||
return PTR_ERR(base);
|
|
||||||
|
|
||||||
+ /* get the tcsr node and setup the config and regmap */
|
|
||||||
+ gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
|
|
||||||
+
|
|
||||||
+ if (!IS_ERR(gsbi->tcsr)) {
|
|
||||||
+ tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
|
|
||||||
+ if (tcsr_node) {
|
|
||||||
+ match = of_match_node(tcsr_dt_match, tcsr_node);
|
|
||||||
+ if (match)
|
|
||||||
+ config = match->data;
|
|
||||||
+ else
|
|
||||||
+ dev_warn(&pdev->dev, "no matching TCSR\n");
|
|
||||||
+
|
|
||||||
+ of_node_put(tcsr_node);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
|
|
||||||
+ dev_err(&pdev->dev, "missing cell-index\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
|
|
||||||
+ dev_err(&pdev->dev, "invalid cell-index\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
|
|
||||||
dev_err(&pdev->dev, "missing mode configuration\n");
|
|
||||||
return -EINVAL;
|
|
||||||
@@ -64,6 +197,25 @@ static int gsbi_probe(struct platform_de
|
|
||||||
writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
|
|
||||||
base + GSBI_CTRL_REG);
|
|
||||||
|
|
||||||
+ /*
|
|
||||||
+ * modify tcsr to reflect mode and ADM CRCI mux
|
|
||||||
+ * Each gsbi contains a pair of bits, one for RX and one for TX
|
|
||||||
+ * SPI mode requires both bits cleared, otherwise they are set
|
|
||||||
+ */
|
|
||||||
+ if (config) {
|
|
||||||
+ for (i = 0; i < config->num_rows; i++) {
|
|
||||||
+ mask = config->array[i][gsbi_num - 1];
|
|
||||||
+
|
|
||||||
+ if (gsbi->mode == GSBI_PROT_SPI)
|
|
||||||
+ regmap_update_bits(gsbi->tcsr,
|
|
||||||
+ TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
|
|
||||||
+ else
|
|
||||||
+ regmap_update_bits(gsbi->tcsr,
|
|
||||||
+ TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
|
|
||||||
+
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
/* make sure the gsbi control write is not reordered */
|
|
||||||
wmb();
|
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -132,6 +132,7 @@
|
|
||||||
|
|
||||||
gsbi2: gsbi@12480000 {
|
|
||||||
compatible = "qcom,gsbi-v1.0.0";
|
|
||||||
+ cell-index = <2>;
|
|
||||||
reg = <0x12480000 0x100>;
|
|
||||||
clocks = <&gcc GSBI2_H_CLK>;
|
|
||||||
clock-names = "iface";
|
|
||||||
@@ -140,6 +141,8 @@
|
|
||||||
ranges;
|
|
||||||
status = "disabled";
|
|
||||||
|
|
||||||
+ syscon-tcsr = <&tcsr>;
|
|
||||||
+
|
|
||||||
uart2: serial@12490000 {
|
|
||||||
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
|
|
||||||
reg = <0x12490000 0x1000>,
|
|
||||||
@@ -167,6 +170,7 @@
|
|
||||||
|
|
||||||
gsbi4: gsbi@16300000 {
|
|
||||||
compatible = "qcom,gsbi-v1.0.0";
|
|
||||||
+ cell-index = <4>;
|
|
||||||
reg = <0x16300000 0x100>;
|
|
||||||
clocks = <&gcc GSBI4_H_CLK>;
|
|
||||||
clock-names = "iface";
|
|
||||||
@@ -175,6 +179,8 @@
|
|
||||||
ranges;
|
|
||||||
status = "disabled";
|
|
||||||
|
|
||||||
+ syscon-tcsr = <&tcsr>;
|
|
||||||
+
|
|
||||||
uart4: serial@16340000 {
|
|
||||||
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
|
|
||||||
reg = <0x16340000 0x1000>,
|
|
||||||
@@ -201,6 +207,7 @@
|
|
||||||
|
|
||||||
gsbi5: gsbi@1a200000 {
|
|
||||||
compatible = "qcom,gsbi-v1.0.0";
|
|
||||||
+ cell-index = <5>;
|
|
||||||
reg = <0x1a200000 0x100>;
|
|
||||||
clocks = <&gcc GSBI5_H_CLK>;
|
|
||||||
clock-names = "iface";
|
|
||||||
@@ -209,6 +216,8 @@
|
|
||||||
ranges;
|
|
||||||
status = "disabled";
|
|
||||||
|
|
||||||
+ syscon-tcsr = <&tcsr>;
|
|
||||||
+
|
|
||||||
uart5: serial@1a240000 {
|
|
||||||
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
|
|
||||||
reg = <0x1a240000 0x1000>,
|
|
||||||
@@ -279,6 +288,11 @@
|
|
||||||
status = "disabled";
|
|
||||||
};
|
|
||||||
|
|
||||||
+ tcsr: syscon@1a400000 {
|
|
||||||
+ compatible = "qcom,tcsr-ipq8064", "syscon";
|
|
||||||
+ reg = <0x1a400000 0x100>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
qcom,ssbi@500000 {
|
|
||||||
compatible = "qcom,ssbi";
|
|
||||||
reg = <0x00500000 0x1000>;
|
|
|
@ -1,263 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings
|
|
||||||
From: Stanimir Varbanov <svarbanov@mm-sol.com>
|
|
||||||
X-Patchwork-Id: 6326181
|
|
||||||
Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com>
|
|
||||||
To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
|
|
||||||
Mark Rutland <mark.rutland@arm.com>,
|
|
||||||
Grant Likely <grant.likely@linaro.org>,
|
|
||||||
Bjorn Helgaas <bhelgaas@google.com>,
|
|
||||||
Kishon Vijay Abraham I <kishon@ti.com>,
|
|
||||||
Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
|
|
||||||
Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
|
|
||||||
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
|
|
||||||
linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
|
|
||||||
Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
|
|
||||||
Stanimir Varbanov <svarbanov@mm-sol.com>
|
|
||||||
Date: Mon, 4 May 2015 15:42:16 +0300
|
|
||||||
|
|
||||||
Document Qualcomm PCIe driver devicetree bindings.
|
|
||||||
|
|
||||||
Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
.../devicetree/bindings/pci/qcom,pcie.txt | 231 ++++++++++++++++++++
|
|
||||||
1 files changed, 231 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
|
|
||||||
@@ -0,0 +1,231 @@
|
|
||||||
+* Qualcomm PCI express root complex
|
|
||||||
+
|
|
||||||
+- compatible:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <stringlist>
|
|
||||||
+ Definition: Value shall include
|
|
||||||
+ - "qcom,pcie-v0" for apq/ipq8064
|
|
||||||
+ - "qcom,pcie-v1" for apq8084
|
|
||||||
+
|
|
||||||
+- reg:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: Register ranges as listed in the reg-names property
|
|
||||||
+
|
|
||||||
+- reg-names:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <stringlist>
|
|
||||||
+ Definition: Must include the following entries
|
|
||||||
+ - "parf" Qualcomm specific registers
|
|
||||||
+ - "dbi" Designware PCIe registers
|
|
||||||
+ - "elbi" External local bus interface registers
|
|
||||||
+ - "config" PCIe configuration space
|
|
||||||
+
|
|
||||||
+- device_type:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: Should be "pci". As specified in designware-pcie.txt
|
|
||||||
+
|
|
||||||
+- #address-cells:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <u32>
|
|
||||||
+ Definition: Should be set to 3. As specified in designware-pcie.txt
|
|
||||||
+
|
|
||||||
+- #size-cells:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <u32>
|
|
||||||
+ Definition: Should be set 2. As specified in designware-pcie.txt
|
|
||||||
+
|
|
||||||
+- ranges:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: As specified in designware-pcie.txt
|
|
||||||
+
|
|
||||||
+- interrupts:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: MSI interrupt
|
|
||||||
+
|
|
||||||
+- interrupt-names:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <stringlist>
|
|
||||||
+ Definition: Should contain "msi"
|
|
||||||
+
|
|
||||||
+- #interrupt-cells:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <u32>
|
|
||||||
+ Definition: Should be 1. As specified in designware-pcie.txt
|
|
||||||
+
|
|
||||||
+- interrupt-map-mask:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: As specified in designware-pcie.txt
|
|
||||||
+
|
|
||||||
+- interrupt-map:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: As specified in designware-pcie.txt
|
|
||||||
+
|
|
||||||
+- clocks:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: List of phandle and clock specifier pairs as listed
|
|
||||||
+ in clock-names property
|
|
||||||
+
|
|
||||||
+- clock-names:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <stringlist>
|
|
||||||
+ Definition: Should contain the following entries
|
|
||||||
+ * should be populated for v0 and v1
|
|
||||||
+ - "iface" Configuration AHB clock
|
|
||||||
+
|
|
||||||
+ * should be populated for v0
|
|
||||||
+ - "core" Clocks the pcie hw block
|
|
||||||
+ - "phy" Clocks the pcie PHY block
|
|
||||||
+
|
|
||||||
+ * should be populated for v1
|
|
||||||
+ - "aux" Auxiliary (AUX) clock
|
|
||||||
+ - "bus_master" Master AXI clock
|
|
||||||
+ - "bus_slave" Slave AXI clock
|
|
||||||
+
|
|
||||||
+- resets:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: List of phandle and reset specifier pairs as listed
|
|
||||||
+ in reset-names property
|
|
||||||
+
|
|
||||||
+- reset-names:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <stringlist>
|
|
||||||
+ Definition: Should contain the following entries
|
|
||||||
+ * should be populated for v0
|
|
||||||
+ - "axi" AXI reset
|
|
||||||
+ - "ahb" AHB reset
|
|
||||||
+ - "por" POR reset
|
|
||||||
+ - "pci" PCI reset
|
|
||||||
+ - "phy" PHY reset
|
|
||||||
+
|
|
||||||
+ * should be populated for v1
|
|
||||||
+ - "core" Core reset
|
|
||||||
+
|
|
||||||
+- power-domains:
|
|
||||||
+ Usage: required (for v1 only)
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: A phandle and power domain specifier pair to the
|
|
||||||
+ power domain which is responsible for collapsing
|
|
||||||
+ and restoring power to the peripheral
|
|
||||||
+
|
|
||||||
+- <name>-supply:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <phandle>
|
|
||||||
+ Definition: List of phandles to the power supply regulator(s)
|
|
||||||
+ * should be populated for v0 and v1
|
|
||||||
+ - "vdda" core analog power supply
|
|
||||||
+
|
|
||||||
+ * should be populated for v0
|
|
||||||
+ - "vdda_phy" analog power supply for PHY
|
|
||||||
+ - "vdda_refclk" analog power supply for IC which generate
|
|
||||||
+ reference clock
|
|
||||||
+
|
|
||||||
+- phys:
|
|
||||||
+ Usage: required (for v1 only)
|
|
||||||
+ Value type: <phandle>
|
|
||||||
+ Definition: List of phandle(s) as listed in phy-names property
|
|
||||||
+
|
|
||||||
+- phy-names:
|
|
||||||
+ Usage: required (for v1 only)
|
|
||||||
+ Value type: <stringlist>
|
|
||||||
+ Definition: Should contain "pciephy"
|
|
||||||
+
|
|
||||||
+- <name>-gpio:
|
|
||||||
+ Usage: optional
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: List of phandle and gpio specifier pairs. Should contain
|
|
||||||
+ - "perst" PCIe endpoint reset signal line
|
|
||||||
+ - "pewake" PCIe endpoint wake signal line
|
|
||||||
+
|
|
||||||
+- pinctrl-0:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <phandle>
|
|
||||||
+ Definition: List of phandles pointing at a pin(s) configuration
|
|
||||||
+
|
|
||||||
+- pinctrl-names
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <stringlist>
|
|
||||||
+ Definition: List of names of pinctrl-0 state
|
|
||||||
+
|
|
||||||
+* Example for v0
|
|
||||||
+ pcie0: pci@1b500000 {
|
|
||||||
+ compatible = "qcom,pcie-v0";
|
|
||||||
+ reg = <0x1b500000 0x1000
|
|
||||||
+ 0x1b502000 0x80
|
|
||||||
+ 0x1b600000 0x100
|
|
||||||
+ 0x0ff00000 0x100000>;
|
|
||||||
+ reg-names = "dbi", "elbi", "parf", "config";
|
|
||||||
+ device_type = "pci";
|
|
||||||
+ linux,pci-domain = <0>;
|
|
||||||
+ bus-range = <0x00 0xff>;
|
|
||||||
+ num-lanes = <1>;
|
|
||||||
+ #address-cells = <3>;
|
|
||||||
+ #size-cells = <2>;
|
|
||||||
+ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */
|
|
||||||
+ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */
|
|
||||||
+ interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>;
|
|
||||||
+ interrupt-names = "msi";
|
|
||||||
+ #interrupt-cells = <1>;
|
|
||||||
+ interrupt-map-mask = <0 0 0 0x7>;
|
|
||||||
+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
|
|
||||||
+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
|
|
||||||
+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
|
|
||||||
+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
|
|
||||||
+ clocks = <&gcc PCIE_A_CLK>,
|
|
||||||
+ <&gcc PCIE_H_CLK>,
|
|
||||||
+ <&gcc PCIE_PHY_CLK>;
|
|
||||||
+ clock-names = "core", "iface", "phy";
|
|
||||||
+ resets = <&gcc PCIE_ACLK_RESET>,
|
|
||||||
+ <&gcc PCIE_HCLK_RESET>,
|
|
||||||
+ <&gcc PCIE_POR_RESET>,
|
|
||||||
+ <&gcc PCIE_PCI_RESET>,
|
|
||||||
+ <&gcc PCIE_PHY_RESET>;
|
|
||||||
+ reset-names = "axi", "ahb", "por", "pci", "phy";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+* Example for v1
|
|
||||||
+ pcie0@fc520000 {
|
|
||||||
+ compatible = "qcom,pcie-v1";
|
|
||||||
+ reg = <0xfc520000 0x2000>,
|
|
||||||
+ <0xff000000 0x1000>,
|
|
||||||
+ <0xff001000 0x1000>,
|
|
||||||
+ <0xff002000 0x2000>;
|
|
||||||
+ reg-names = "parf", "dbi", "elbi", "config";
|
|
||||||
+ device_type = "pci";
|
|
||||||
+ linux,pci-domain = <0>;
|
|
||||||
+ bus-range = <0x00 0xff>;
|
|
||||||
+ num-lanes = <1>;
|
|
||||||
+ #address-cells = <3>;
|
|
||||||
+ #size-cells = <2>;
|
|
||||||
+ ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */
|
|
||||||
+ 0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */
|
|
||||||
+ interrupts = <GIC_SPI 243 IRQ_TYPE_NONE>;
|
|
||||||
+ interrupt-names = "msi";
|
|
||||||
+ #interrupt-cells = <1>;
|
|
||||||
+ interrupt-map-mask = <0 0 0 0x7>;
|
|
||||||
+ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
|
|
||||||
+ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
|
|
||||||
+ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
|
|
||||||
+ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
|
|
||||||
+ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
|
|
||||||
+ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
|
|
||||||
+ <&gcc GCC_PCIE_0_SLV_AXI_CLK>,
|
|
||||||
+ <&gcc GCC_PCIE_0_AUX_CLK>;
|
|
||||||
+ clock-names = "iface", "master_bus", "slave_bus", "aux";
|
|
||||||
+ resets = <&gcc GCC_PCIE_0_BCR>;
|
|
||||||
+ reset-names = "core";
|
|
||||||
+ power-domains = <&gcc PCIE0_GDSC>;
|
|
||||||
+ vdda-supply = <&pma8084_l3>;
|
|
||||||
+ phys = <&pciephy0>;
|
|
||||||
+ phy-names = "pciephy";
|
|
||||||
+ perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>;
|
|
||||||
+ pinctrl-0 = <&pcie0_pins_default>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
|
@ -1,753 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver
|
|
||||||
From: Stanimir Varbanov <svarbanov@mm-sol.com>
|
|
||||||
X-Patchwork-Id: 6326161
|
|
||||||
Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com>
|
|
||||||
To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>,
|
|
||||||
Mark Rutland <mark.rutland@arm.com>,
|
|
||||||
Grant Likely <grant.likely@linaro.org>,
|
|
||||||
Bjorn Helgaas <bhelgaas@google.com>,
|
|
||||||
Kishon Vijay Abraham I <kishon@ti.com>,
|
|
||||||
Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de>
|
|
||||||
Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
|
|
||||||
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
|
|
||||||
linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>,
|
|
||||||
Srinivas Kandagatla <srinivas.kandagatla@linaro.org>,
|
|
||||||
Stanimir Varbanov <svarbanov@mm-sol.com>
|
|
||||||
Date: Mon, 4 May 2015 15:42:17 +0300
|
|
||||||
|
|
||||||
The PCIe driver reuse the Designware common code for host
|
|
||||||
and MSI initialization, and also program the Qualcomm
|
|
||||||
application specific registers.
|
|
||||||
|
|
||||||
Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
MAINTAINERS | 7 +
|
|
||||||
drivers/pci/host/Kconfig | 9 +
|
|
||||||
drivers/pci/host/Makefile | 1 +
|
|
||||||
drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
4 files changed, 694 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/pci/host/pcie-qcom.c
|
|
||||||
|
|
||||||
--- a/MAINTAINERS
|
|
||||||
+++ b/MAINTAINERS
|
|
||||||
@@ -7127,6 +7127,13 @@ L: linux-pci@vger.kernel.org
|
|
||||||
S: Maintained
|
|
||||||
F: drivers/pci/host/*spear*
|
|
||||||
|
|
||||||
+PCIE DRIVER FOR QUALCOMM MSM
|
|
||||||
+M: Stanimir Varbanov <svarbanov@mm-sol.com>
|
|
||||||
+L: linux-pci@vger.kernel.org
|
|
||||||
+L: linux-arm-msm@vger.kernel.org
|
|
||||||
+S: Maintained
|
|
||||||
+F: drivers/pci/host/*qcom*
|
|
||||||
+
|
|
||||||
PCMCIA SUBSYSTEM
|
|
||||||
P: Linux PCMCIA Team
|
|
||||||
L: linux-pcmcia@lists.infradead.org
|
|
||||||
--- a/drivers/pci/host/Kconfig
|
|
||||||
+++ b/drivers/pci/host/Kconfig
|
|
||||||
@@ -91,4 +91,13 @@ config PCI_XGENE
|
|
||||||
There are 5 internal PCIe ports available. Each port is GEN3 capable
|
|
||||||
and have varied lanes from x1 to x8.
|
|
||||||
|
|
||||||
+config PCIE_QCOM
|
|
||||||
+ bool "Qualcomm PCIe controller"
|
|
||||||
+ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST)
|
|
||||||
+ select PCIE_DW
|
|
||||||
+ select PCIEPORTBUS
|
|
||||||
+ help
|
|
||||||
+ Say Y here to enable PCIe controller support on Qualcomm SoCs. The
|
|
||||||
+ PCIe controller use Designware core plus Qualcomm specific hardware
|
|
||||||
+ wrappers.
|
|
||||||
endmenu
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/pci/host/pcie-qcom.c
|
|
||||||
@@ -0,0 +1,677 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
+#include <linux/gpio.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/of_gpio.h>
|
|
||||||
+#include <linux/pci.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/phy/phy.h>
|
|
||||||
+#include <linux/regulator/consumer.h>
|
|
||||||
+#include <linux/reset.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+
|
|
||||||
+#include "pcie-designware.h"
|
|
||||||
+
|
|
||||||
+#define PCIE20_PARF_PHY_CTRL 0x40
|
|
||||||
+#define PCIE20_PARF_PHY_REFCLK 0x4C
|
|
||||||
+#define PCIE20_PARF_DBI_BASE_ADDR 0x168
|
|
||||||
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
|
|
||||||
+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
|
|
||||||
+
|
|
||||||
+#define PCIE20_ELBI_SYS_CTRL 0x04
|
|
||||||
+#define PCIE20_ELBI_SYS_STTS 0x08
|
|
||||||
+#define XMLH_LINK_UP BIT(10)
|
|
||||||
+
|
|
||||||
+#define PCIE20_CAP 0x70
|
|
||||||
+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
|
|
||||||
+
|
|
||||||
+#define PERST_DELAY_MIN_US 1000
|
|
||||||
+#define PERST_DELAY_MAX_US 1005
|
|
||||||
+
|
|
||||||
+#define LINKUP_DELAY_MIN_US 5000
|
|
||||||
+#define LINKUP_DELAY_MAX_US 5100
|
|
||||||
+#define LINKUP_RETRIES_COUNT 20
|
|
||||||
+
|
|
||||||
+#define PCIE_V0 0 /* apq8064 */
|
|
||||||
+#define PCIE_V1 1 /* apq8084 */
|
|
||||||
+
|
|
||||||
+struct qcom_pcie_resources_v0 {
|
|
||||||
+ struct clk *iface_clk;
|
|
||||||
+ struct clk *core_clk;
|
|
||||||
+ struct clk *phy_clk;
|
|
||||||
+ struct reset_control *pci_reset;
|
|
||||||
+ struct reset_control *axi_reset;
|
|
||||||
+ struct reset_control *ahb_reset;
|
|
||||||
+ struct reset_control *por_reset;
|
|
||||||
+ struct reset_control *phy_reset;
|
|
||||||
+ struct regulator *vdda;
|
|
||||||
+ struct regulator *vdda_phy;
|
|
||||||
+ struct regulator *vdda_refclk;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct qcom_pcie_resources_v1 {
|
|
||||||
+ struct clk *iface;
|
|
||||||
+ struct clk *aux;
|
|
||||||
+ struct clk *master_bus;
|
|
||||||
+ struct clk *slave_bus;
|
|
||||||
+ struct reset_control *core;
|
|
||||||
+ struct regulator *vdda;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+union pcie_resources {
|
|
||||||
+ struct qcom_pcie_resources_v0 v0;
|
|
||||||
+ struct qcom_pcie_resources_v1 v1;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct qcom_pcie {
|
|
||||||
+ struct pcie_port pp;
|
|
||||||
+ struct device *dev;
|
|
||||||
+ union pcie_resources res;
|
|
||||||
+ void __iomem *parf;
|
|
||||||
+ void __iomem *dbi;
|
|
||||||
+ void __iomem *elbi;
|
|
||||||
+ struct phy *phy;
|
|
||||||
+ struct gpio_desc *reset;
|
|
||||||
+ unsigned int version;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp)
|
|
||||||
+
|
|
||||||
+static inline void
|
|
||||||
+writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask)
|
|
||||||
+{
|
|
||||||
+ u32 val = readl(addr);
|
|
||||||
+
|
|
||||||
+ val &= ~clear_mask;
|
|
||||||
+ val |= set_mask;
|
|
||||||
+ writel(val, addr);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert)
|
|
||||||
+{
|
|
||||||
+ int val, active_low;
|
|
||||||
+
|
|
||||||
+ if (IS_ERR_OR_NULL(pcie->reset))
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ active_low = gpiod_is_active_low(pcie->reset);
|
|
||||||
+
|
|
||||||
+ if (assert)
|
|
||||||
+ val = !!active_low;
|
|
||||||
+ else
|
|
||||||
+ val = !active_low;
|
|
||||||
+
|
|
||||||
+ gpiod_set_value(pcie->reset, val);
|
|
||||||
+
|
|
||||||
+ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ qcom_ep_reset_assert_deassert(pcie, 1);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ qcom_ep_reset_assert_deassert(pcie, 0);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
|
|
||||||
+{
|
|
||||||
+ struct pcie_port *pp = arg;
|
|
||||||
+
|
|
||||||
+ return dw_handle_msi_irq(pp);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_link_up(struct pcie_port *pp)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
|
|
||||||
+ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS);
|
|
||||||
+
|
|
||||||
+ return val & BIT(29) ? 1 : 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
|
|
||||||
+
|
|
||||||
+ reset_control_assert(res->pci_reset);
|
|
||||||
+ reset_control_assert(res->axi_reset);
|
|
||||||
+ reset_control_assert(res->ahb_reset);
|
|
||||||
+ reset_control_assert(res->por_reset);
|
|
||||||
+ reset_control_assert(res->pci_reset);
|
|
||||||
+ clk_disable_unprepare(res->iface_clk);
|
|
||||||
+ clk_disable_unprepare(res->core_clk);
|
|
||||||
+ clk_disable_unprepare(res->phy_clk);
|
|
||||||
+ regulator_disable(res->vdda);
|
|
||||||
+ regulator_disable(res->vdda_phy);
|
|
||||||
+ regulator_disable(res->vdda_refclk);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
|
|
||||||
+
|
|
||||||
+ reset_control_assert(res->core);
|
|
||||||
+ clk_disable_unprepare(res->slave_bus);
|
|
||||||
+ clk_disable_unprepare(res->master_bus);
|
|
||||||
+ clk_disable_unprepare(res->iface);
|
|
||||||
+ clk_disable_unprepare(res->aux);
|
|
||||||
+ regulator_disable(res->vdda);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
|
|
||||||
+ struct device *dev = pcie->dev;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ ret = regulator_enable(res->vdda);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot enable vdda regulator\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = regulator_enable(res->vdda_refclk);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot enable vdda_refclk regulator\n");
|
|
||||||
+ goto err_refclk;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = regulator_enable(res->vdda_phy);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot enable vdda_phy regulator\n");
|
|
||||||
+ goto err_vdda_phy;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(res->iface_clk);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot prepare/enable iface clock\n");
|
|
||||||
+ goto err_iface;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(res->core_clk);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot prepare/enable core clock\n");
|
|
||||||
+ goto err_clk_core;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(res->phy_clk);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot prepare/enable phy clock\n");
|
|
||||||
+ goto err_clk_phy;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = reset_control_deassert(res->ahb_reset);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot deassert ahb reset\n");
|
|
||||||
+ goto err_reset_ahb;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_reset_ahb:
|
|
||||||
+ clk_disable_unprepare(res->phy_clk);
|
|
||||||
+err_clk_phy:
|
|
||||||
+ clk_disable_unprepare(res->core_clk);
|
|
||||||
+err_clk_core:
|
|
||||||
+ clk_disable_unprepare(res->iface_clk);
|
|
||||||
+err_iface:
|
|
||||||
+ regulator_disable(res->vdda_phy);
|
|
||||||
+err_vdda_phy:
|
|
||||||
+ regulator_disable(res->vdda_refclk);
|
|
||||||
+err_refclk:
|
|
||||||
+ regulator_disable(res->vdda);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
|
|
||||||
+ struct device *dev = pcie->dev;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ ret = reset_control_deassert(res->core);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot deassert core reset\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(res->aux);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot prepare/enable aux clock\n");
|
|
||||||
+ goto err_res;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(res->iface);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot prepare/enable iface clock\n");
|
|
||||||
+ goto err_aux;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(res->master_bus);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot prepare/enable master_bus clock\n");
|
|
||||||
+ goto err_iface;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(res->slave_bus);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot prepare/enable slave_bus clock\n");
|
|
||||||
+ goto err_master;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = regulator_enable(res->vdda);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot enable vdda regulator\n");
|
|
||||||
+ goto err_slave;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_slave:
|
|
||||||
+ clk_disable_unprepare(res->slave_bus);
|
|
||||||
+err_master:
|
|
||||||
+ clk_disable_unprepare(res->master_bus);
|
|
||||||
+err_iface:
|
|
||||||
+ clk_disable_unprepare(res->iface);
|
|
||||||
+err_aux:
|
|
||||||
+ clk_disable_unprepare(res->aux);
|
|
||||||
+err_res:
|
|
||||||
+ reset_control_assert(res->core);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
|
|
||||||
+ struct device *dev = pcie->dev;
|
|
||||||
+
|
|
||||||
+ res->vdda = devm_regulator_get(dev, "vdda");
|
|
||||||
+ if (IS_ERR(res->vdda))
|
|
||||||
+ return PTR_ERR(res->vdda);
|
|
||||||
+
|
|
||||||
+ res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
|
|
||||||
+ if (IS_ERR(res->vdda_phy))
|
|
||||||
+ return PTR_ERR(res->vdda_phy);
|
|
||||||
+
|
|
||||||
+ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
|
|
||||||
+ if (IS_ERR(res->vdda_refclk))
|
|
||||||
+ return PTR_ERR(res->vdda_refclk);
|
|
||||||
+
|
|
||||||
+ res->iface_clk = devm_clk_get(dev, "iface");
|
|
||||||
+ if (IS_ERR(res->iface_clk))
|
|
||||||
+ return PTR_ERR(res->iface_clk);
|
|
||||||
+
|
|
||||||
+ res->core_clk = devm_clk_get(dev, "core");
|
|
||||||
+ if (IS_ERR(res->core_clk))
|
|
||||||
+ return PTR_ERR(res->core_clk);
|
|
||||||
+
|
|
||||||
+ res->phy_clk = devm_clk_get(dev, "phy");
|
|
||||||
+ if (IS_ERR(res->phy_clk))
|
|
||||||
+ return PTR_ERR(res->phy_clk);
|
|
||||||
+
|
|
||||||
+ res->pci_reset = devm_reset_control_get(dev, "pci");
|
|
||||||
+ if (IS_ERR(res->pci_reset))
|
|
||||||
+ return PTR_ERR(res->pci_reset);
|
|
||||||
+
|
|
||||||
+ res->axi_reset = devm_reset_control_get(dev, "axi");
|
|
||||||
+ if (IS_ERR(res->axi_reset))
|
|
||||||
+ return PTR_ERR(res->axi_reset);
|
|
||||||
+
|
|
||||||
+ res->ahb_reset = devm_reset_control_get(dev, "ahb");
|
|
||||||
+ if (IS_ERR(res->ahb_reset))
|
|
||||||
+ return PTR_ERR(res->ahb_reset);
|
|
||||||
+
|
|
||||||
+ res->por_reset = devm_reset_control_get(dev, "por");
|
|
||||||
+ if (IS_ERR(res->por_reset))
|
|
||||||
+ return PTR_ERR(res->por_reset);
|
|
||||||
+
|
|
||||||
+ res->phy_reset = devm_reset_control_get(dev, "phy");
|
|
||||||
+ if (IS_ERR(res->phy_reset))
|
|
||||||
+ return PTR_ERR(res->phy_reset);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
|
|
||||||
+ struct device *dev = pcie->dev;
|
|
||||||
+
|
|
||||||
+ res->vdda = devm_regulator_get(dev, "vdda");
|
|
||||||
+ if (IS_ERR(res->vdda))
|
|
||||||
+ return PTR_ERR(res->vdda);
|
|
||||||
+
|
|
||||||
+ res->iface = devm_clk_get(dev, "iface");
|
|
||||||
+ if (IS_ERR(res->iface))
|
|
||||||
+ return PTR_ERR(res->iface);
|
|
||||||
+
|
|
||||||
+ res->aux = devm_clk_get(dev, "aux");
|
|
||||||
+ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER)
|
|
||||||
+ return -EPROBE_DEFER;
|
|
||||||
+ else if (IS_ERR(res->aux))
|
|
||||||
+ res->aux = NULL;
|
|
||||||
+
|
|
||||||
+ res->master_bus = devm_clk_get(dev, "master_bus");
|
|
||||||
+ if (IS_ERR(res->master_bus))
|
|
||||||
+ return PTR_ERR(res->master_bus);
|
|
||||||
+
|
|
||||||
+ res->slave_bus = devm_clk_get(dev, "slave_bus");
|
|
||||||
+ if (IS_ERR(res->slave_bus))
|
|
||||||
+ return PTR_ERR(res->slave_bus);
|
|
||||||
+
|
|
||||||
+ res->core = devm_reset_control_get(dev, "core");
|
|
||||||
+ if (IS_ERR(res->core))
|
|
||||||
+ return PTR_ERR(res->core);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_enable_link_training(struct pcie_port *pp)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
|
|
||||||
+ struct device *dev = pp->dev;
|
|
||||||
+ int retries;
|
|
||||||
+ u32 val;
|
|
||||||
+
|
|
||||||
+ /* enable link training */
|
|
||||||
+ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
|
|
||||||
+
|
|
||||||
+ /* wait for up to 100ms for the link to come up */
|
|
||||||
+ retries = LINKUP_RETRIES_COUNT;
|
|
||||||
+ do {
|
|
||||||
+ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS);
|
|
||||||
+ if (val & XMLH_LINK_UP)
|
|
||||||
+ break;
|
|
||||||
+ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US);
|
|
||||||
+ } while (retries--);
|
|
||||||
+
|
|
||||||
+ if (retries < 0 || !dw_pcie_link_up(pp)) {
|
|
||||||
+ dev_err(dev, "link initialization failed\n");
|
|
||||||
+ return -ENXIO;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_pcie_host_init_v1(struct pcie_port *pp)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ qcom_ep_reset_assert(pcie);
|
|
||||||
+
|
|
||||||
+ ret = qcom_pcie_enable_resources_v1(pcie);
|
|
||||||
+ if (ret)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ /* change DBI base address */
|
|
||||||
+ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
|
|
||||||
+
|
|
||||||
+ if (IS_ENABLED(CONFIG_PCI_MSI))
|
|
||||||
+ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
|
|
||||||
+ 0, BIT(31));
|
|
||||||
+
|
|
||||||
+ ret = phy_init(pcie->phy);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_res;
|
|
||||||
+
|
|
||||||
+ ret = phy_power_on(pcie->phy);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_phy;
|
|
||||||
+
|
|
||||||
+ dw_pcie_setup_rc(pp);
|
|
||||||
+
|
|
||||||
+ if (IS_ENABLED(CONFIG_PCI_MSI))
|
|
||||||
+ dw_pcie_msi_init(pp);
|
|
||||||
+
|
|
||||||
+ qcom_ep_reset_deassert(pcie);
|
|
||||||
+
|
|
||||||
+ ret = qcom_pcie_enable_link_training(pp);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+err:
|
|
||||||
+ qcom_ep_reset_assert(pcie);
|
|
||||||
+ phy_power_off(pcie->phy);
|
|
||||||
+err_phy:
|
|
||||||
+ phy_exit(pcie->phy);
|
|
||||||
+err_res:
|
|
||||||
+ qcom_pcie_disable_resources_v1(pcie);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_pcie_host_init_v0(struct pcie_port *pp)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
|
|
||||||
+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
|
|
||||||
+ struct device *dev = pcie->dev;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ qcom_ep_reset_assert(pcie);
|
|
||||||
+
|
|
||||||
+ ret = qcom_pcie_enable_resources_v0(pcie);
|
|
||||||
+ if (ret)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
|
|
||||||
+
|
|
||||||
+ /* enable external reference clock */
|
|
||||||
+ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
|
|
||||||
+
|
|
||||||
+ ret = reset_control_deassert(res->phy_reset);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot deassert phy reset\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = reset_control_deassert(res->pci_reset);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot deassert pci reset\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = reset_control_deassert(res->por_reset);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot deassert por reset\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = reset_control_deassert(res->axi_reset);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot deassert axi reset\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* wait 150ms for clock acquisition */
|
|
||||||
+ usleep_range(10000, 15000);
|
|
||||||
+
|
|
||||||
+ dw_pcie_setup_rc(pp);
|
|
||||||
+
|
|
||||||
+ if (IS_ENABLED(CONFIG_PCI_MSI))
|
|
||||||
+ dw_pcie_msi_init(pp);
|
|
||||||
+
|
|
||||||
+ qcom_ep_reset_deassert(pcie);
|
|
||||||
+
|
|
||||||
+ ret = qcom_pcie_enable_link_training(pp);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ return;
|
|
||||||
+err:
|
|
||||||
+ qcom_ep_reset_assert(pcie);
|
|
||||||
+ qcom_pcie_disable_resources_v0(pcie);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qcom_pcie_host_init(struct pcie_port *pp)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
|
|
||||||
+
|
|
||||||
+ if (pcie->version == PCIE_V0)
|
|
||||||
+ return qcom_pcie_host_init_v0(pp);
|
|
||||||
+ else
|
|
||||||
+ return qcom_pcie_host_init_v1(pp);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int
|
|
||||||
+qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val)
|
|
||||||
+{
|
|
||||||
+ /* the device class is not reported correctly from the register */
|
|
||||||
+ if (where == PCI_CLASS_REVISION && size == 4) {
|
|
||||||
+ *val = readl(pp->dbi_base + PCI_CLASS_REVISION);
|
|
||||||
+ *val &= ~(0xffff << 16);
|
|
||||||
+ *val |= PCI_CLASS_BRIDGE_PCI << 16;
|
|
||||||
+ return PCIBIOS_SUCCESSFUL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where,
|
|
||||||
+ size, val);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct pcie_host_ops qcom_pcie_ops = {
|
|
||||||
+ .link_up = qcom_pcie_link_up,
|
|
||||||
+ .host_init = qcom_pcie_host_init,
|
|
||||||
+ .rd_own_conf = qcom_pcie_rd_own_conf,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct of_device_id qcom_pcie_match[] = {
|
|
||||||
+ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 },
|
|
||||||
+ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 },
|
|
||||||
+ { }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &pdev->dev;
|
|
||||||
+ const struct of_device_id *match;
|
|
||||||
+ struct resource *res;
|
|
||||||
+ struct qcom_pcie *pcie;
|
|
||||||
+ struct pcie_port *pp;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ match = of_match_node(qcom_pcie_match, dev->of_node);
|
|
||||||
+ if (!match)
|
|
||||||
+ return -ENXIO;
|
|
||||||
+
|
|
||||||
+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
|
||||||
+ if (!pcie)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ pcie->version = (unsigned int)match->data;
|
|
||||||
+
|
|
||||||
+ pcie->reset = devm_gpiod_get_optional(dev, "perst");
|
|
||||||
+ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER)
|
|
||||||
+ return PTR_ERR(pcie->reset);
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
|
|
||||||
+ pcie->parf = devm_ioremap_resource(dev, res);
|
|
||||||
+ if (IS_ERR(pcie->parf))
|
|
||||||
+ return PTR_ERR(pcie->parf);
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
|
|
||||||
+ pcie->dbi = devm_ioremap_resource(dev, res);
|
|
||||||
+ if (IS_ERR(pcie->dbi))
|
|
||||||
+ return PTR_ERR(pcie->dbi);
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
|
|
||||||
+ pcie->elbi = devm_ioremap_resource(dev, res);
|
|
||||||
+ if (IS_ERR(pcie->elbi))
|
|
||||||
+ return PTR_ERR(pcie->elbi);
|
|
||||||
+
|
|
||||||
+ pcie->phy = devm_phy_optional_get(dev, "pciephy");
|
|
||||||
+ if (IS_ERR(pcie->phy))
|
|
||||||
+ return PTR_ERR(pcie->phy);
|
|
||||||
+
|
|
||||||
+ pcie->dev = dev;
|
|
||||||
+
|
|
||||||
+ if (pcie->version == PCIE_V0)
|
|
||||||
+ ret = qcom_pcie_get_resources_v0(pcie);
|
|
||||||
+ else
|
|
||||||
+ ret = qcom_pcie_get_resources_v1(pcie);
|
|
||||||
+
|
|
||||||
+ if (ret)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ pp = &pcie->pp;
|
|
||||||
+ pp->dev = dev;
|
|
||||||
+ pp->dbi_base = pcie->dbi;
|
|
||||||
+ pp->root_bus_nr = -1;
|
|
||||||
+ pp->ops = &qcom_pcie_ops;
|
|
||||||
+
|
|
||||||
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
|
||||||
+ pp->msi_irq = platform_get_irq_byname(pdev, "msi");
|
|
||||||
+ if (pp->msi_irq < 0) {
|
|
||||||
+ dev_err(dev, "cannot get msi irq\n");
|
|
||||||
+ return pp->msi_irq;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = devm_request_irq(dev, pp->msi_irq,
|
|
||||||
+ qcom_pcie_msi_irq_handler,
|
|
||||||
+ IRQF_SHARED, "qcom-pcie-msi", pp);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot request msi irq\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = dw_pcie_host_init(pp);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "cannot initialize host\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, pcie);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_pcie_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct qcom_pcie *pcie = platform_get_drvdata(pdev);
|
|
||||||
+
|
|
||||||
+ qcom_ep_reset_assert(pcie);
|
|
||||||
+ phy_power_off(pcie->phy);
|
|
||||||
+ phy_exit(pcie->phy);
|
|
||||||
+ if (pcie->version == PCIE_V0)
|
|
||||||
+ qcom_pcie_disable_resources_v0(pcie);
|
|
||||||
+ else
|
|
||||||
+ qcom_pcie_disable_resources_v1(pcie);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver qcom_pcie_driver = {
|
|
||||||
+ .probe = qcom_pcie_probe,
|
|
||||||
+ .remove = qcom_pcie_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "qcom-pcie",
|
|
||||||
+ .of_match_table = qcom_pcie_match,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+module_platform_driver(qcom_pcie_driver);
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
|
|
||||||
+MODULE_DESCRIPTION("Qualcomm PCIe root complex driver");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
+MODULE_ALIAS("platform:qcom-pcie");
|
|
||||||
--- a/drivers/pci/host/Makefile
|
|
||||||
+++ b/drivers/pci/host/Makefile
|
|
||||||
@@ -11,3 +11,4 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spe
|
|
||||||
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
|
|
||||||
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
|
|
||||||
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
|
|
||||||
+obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
|
|
|
@ -1,268 +0,0 @@
|
||||||
From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Tue, 21 Apr 2015 19:01:42 -0700
|
|
||||||
Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms
|
|
||||||
|
|
||||||
qcom-pcie driver now supports version 0 of the controller. This change
|
|
||||||
adds the corresponding entries to the IPQ806x dtsi file and
|
|
||||||
corresponding platform (AP148).
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 30 ++++++++
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064.dtsi | 124 +++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 154 insertions(+)
|
|
||||||
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
@@ -30,6 +30,22 @@
|
|
||||||
bias-disable;
|
|
||||||
};
|
|
||||||
|
|
||||||
+ pcie1_pins: pcie1_pinmux {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio3";
|
|
||||||
+ drive-strength = <2>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie2_pins: pcie2_pinmux {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio48";
|
|
||||||
+ drive-strength = <2>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
spi_pins: spi_pins {
|
|
||||||
mux {
|
|
||||||
pins = "gpio18", "gpio19", "gpio21";
|
|
||||||
@@ -133,5 +149,19 @@
|
|
||||||
usb30@1 {
|
|
||||||
status = "ok";
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ pcie0: pci@1b500000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ reset-gpio = <&qcom_pinmux 3 0>;
|
|
||||||
+ pinctrl-0 = <&pcie1_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie1: pci@1b700000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ reset-gpio = <&qcom_pinmux 48 0>;
|
|
||||||
+ pinctrl-0 = <&pcie2_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
@@ -30,6 +30,30 @@
|
|
||||||
bias-disable;
|
|
||||||
};
|
|
||||||
|
|
||||||
+ pcie1_pins: pcie1_pinmux {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio3";
|
|
||||||
+ drive-strength = <2>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie2_pins: pcie2_pinmux {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio48";
|
|
||||||
+ drive-strength = <2>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie3_pins: pcie3_pinmux {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio63";
|
|
||||||
+ drive-strength = <2>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
spi_pins: spi_pins {
|
|
||||||
mux {
|
|
||||||
pins = "gpio18", "gpio19", "gpio21";
|
|
||||||
@@ -128,5 +152,26 @@
|
|
||||||
usb30@1 {
|
|
||||||
status = "ok";
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ pcie0: pci@1b500000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ reset-gpio = <&qcom_pinmux 3 0>;
|
|
||||||
+ pinctrl-0 = <&pcie1_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie1: pci@1b700000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ reset-gpio = <&qcom_pinmux 48 0>;
|
|
||||||
+ pinctrl-0 = <&pcie2_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie2: pci@1b900000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ reset-gpio = <&qcom_pinmux 63 0>;
|
|
||||||
+ pinctrl-0 = <&pcie3_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -3,6 +3,8 @@
|
|
||||||
#include "skeleton.dtsi"
|
|
||||||
#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
|
|
||||||
#include <dt-bindings/soc/qcom,gsbi.h>
|
|
||||||
+#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
|
|
||||||
+#include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
||||||
|
|
||||||
/ {
|
|
||||||
model = "Qualcomm IPQ8064";
|
|
||||||
@@ -306,6 +308,129 @@
|
|
||||||
#reset-cells = <1>;
|
|
||||||
};
|
|
||||||
|
|
||||||
+ pcie0: pci@1b500000 {
|
|
||||||
+ compatible = "qcom,pcie-v0";
|
|
||||||
+ reg = <0x1b500000 0x1000
|
|
||||||
+ 0x1b502000 0x80
|
|
||||||
+ 0x1b600000 0x100
|
|
||||||
+ 0x0ff00000 0x100000>;
|
|
||||||
+ reg-names = "dbi", "elbi", "parf", "config";
|
|
||||||
+ device_type = "pci";
|
|
||||||
+ linux,pci-domain = <0>;
|
|
||||||
+ bus-range = <0x00 0xff>;
|
|
||||||
+ num-lanes = <1>;
|
|
||||||
+ #address-cells = <3>;
|
|
||||||
+ #size-cells = <2>;
|
|
||||||
+
|
|
||||||
+ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */
|
|
||||||
+ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
|
|
||||||
+
|
|
||||||
+ interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>;
|
|
||||||
+ interrupt-names = "msi";
|
|
||||||
+ #interrupt-cells = <1>;
|
|
||||||
+ interrupt-map-mask = <0 0 0 0x7>;
|
|
||||||
+ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
|
|
||||||
+ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
|
|
||||||
+ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
|
|
||||||
+ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc PCIE_A_CLK>,
|
|
||||||
+ <&gcc PCIE_H_CLK>,
|
|
||||||
+ <&gcc PCIE_PHY_CLK>;
|
|
||||||
+ clock-names = "core", "iface", "phy";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc PCIE_ACLK_RESET>,
|
|
||||||
+ <&gcc PCIE_HCLK_RESET>,
|
|
||||||
+ <&gcc PCIE_POR_RESET>,
|
|
||||||
+ <&gcc PCIE_PCI_RESET>,
|
|
||||||
+ <&gcc PCIE_PHY_RESET>;
|
|
||||||
+ reset-names = "axi", "ahb", "por", "pci", "phy";
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie1: pci@1b700000 {
|
|
||||||
+ compatible = "qcom,pcie-v0";
|
|
||||||
+ reg = <0x1b700000 0x1000
|
|
||||||
+ 0x1b702000 0x80
|
|
||||||
+ 0x1b800000 0x100
|
|
||||||
+ 0x31f00000 0x100000>;
|
|
||||||
+ reg-names = "dbi", "elbi", "parf", "config";
|
|
||||||
+ device_type = "pci";
|
|
||||||
+ linux,pci-domain = <1>;
|
|
||||||
+ bus-range = <0x00 0xff>;
|
|
||||||
+ num-lanes = <1>;
|
|
||||||
+ #address-cells = <3>;
|
|
||||||
+ #size-cells = <2>;
|
|
||||||
+
|
|
||||||
+ ranges = <0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */
|
|
||||||
+ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */
|
|
||||||
+
|
|
||||||
+ interrupts = <GIC_SPI 57 IRQ_TYPE_NONE>;
|
|
||||||
+ interrupt-names = "msi";
|
|
||||||
+ #interrupt-cells = <1>;
|
|
||||||
+ interrupt-map-mask = <0 0 0 0x7>;
|
|
||||||
+ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
|
|
||||||
+ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
|
|
||||||
+ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
|
|
||||||
+ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc PCIE_1_A_CLK>,
|
|
||||||
+ <&gcc PCIE_1_H_CLK>,
|
|
||||||
+ <&gcc PCIE_1_PHY_CLK>;
|
|
||||||
+ clock-names = "core", "iface", "phy";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc PCIE_1_ACLK_RESET>,
|
|
||||||
+ <&gcc PCIE_1_HCLK_RESET>,
|
|
||||||
+ <&gcc PCIE_1_POR_RESET>,
|
|
||||||
+ <&gcc PCIE_1_PCI_RESET>,
|
|
||||||
+ <&gcc PCIE_1_PHY_RESET>;
|
|
||||||
+ reset-names = "axi", "ahb", "por", "pci", "phy";
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ pcie2: pci@1b900000 {
|
|
||||||
+ compatible = "qcom,pcie-v0";
|
|
||||||
+ reg = <0x1b900000 0x1000
|
|
||||||
+ 0x1b902000 0x80
|
|
||||||
+ 0x1ba00000 0x100
|
|
||||||
+ 0x35f00000 0x100000>;
|
|
||||||
+ reg-names = "dbi", "elbi", "parf", "config";
|
|
||||||
+ device_type = "pci";
|
|
||||||
+ linux,pci-domain = <2>;
|
|
||||||
+ bus-range = <0x00 0xff>;
|
|
||||||
+ num-lanes = <1>;
|
|
||||||
+ #address-cells = <3>;
|
|
||||||
+ #size-cells = <2>;
|
|
||||||
+
|
|
||||||
+ ranges = <0x81000000 0 0 0x35e00000 0 0x00100000 /* downstream I/O */
|
|
||||||
+ 0x82000000 0 0x00000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */
|
|
||||||
+
|
|
||||||
+ interrupts = <GIC_SPI 71 IRQ_TYPE_NONE>;
|
|
||||||
+ interrupt-names = "msi";
|
|
||||||
+ #interrupt-cells = <1>;
|
|
||||||
+ interrupt-map-mask = <0 0 0 0x7>;
|
|
||||||
+ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
|
|
||||||
+ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
|
|
||||||
+ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
|
|
||||||
+ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc PCIE_2_A_CLK>,
|
|
||||||
+ <&gcc PCIE_2_H_CLK>,
|
|
||||||
+ <&gcc PCIE_2_PHY_CLK>;
|
|
||||||
+ clock-names = "core", "iface", "phy";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc PCIE_2_ACLK_RESET>,
|
|
||||||
+ <&gcc PCIE_2_HCLK_RESET>,
|
|
||||||
+ <&gcc PCIE_2_POR_RESET>,
|
|
||||||
+ <&gcc PCIE_2_PCI_RESET>,
|
|
||||||
+ <&gcc PCIE_2_PHY_RESET>;
|
|
||||||
+ reset-names = "axi", "ahb", "por", "pci", "phy";
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
hs_phy_1: phy@100f8800 {
|
|
||||||
compatible = "qcom,dwc3-hs-usb-phy";
|
|
||||||
reg = <0x100f8800 0x30>;
|
|
||||||
@@ -389,6 +514,5 @@
|
|
||||||
dr_mode = "host";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
-
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,29 +0,0 @@
|
||||||
From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Tue, 21 Apr 2015 19:09:07 -0700
|
|
||||||
Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is
|
|
||||||
enabled
|
|
||||||
|
|
||||||
If multiple PCIe devices are present in the system, the kernel will
|
|
||||||
panic at boot time when trying to scan the PCI buses. This happens on
|
|
||||||
IPQ806x based platforms, which has 3 PCIe ports.
|
|
||||||
|
|
||||||
Enabling this option allows the kernel to assign the pci-domains
|
|
||||||
according to the device-tree content. This allows multiple PCIe
|
|
||||||
controllers to coexist in the system.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/mach-qcom/Kconfig | 1 +
|
|
||||||
1 file changed, 1 insertion(+)
|
|
||||||
|
|
||||||
--- a/arch/arm/mach-qcom/Kconfig
|
|
||||||
+++ b/arch/arm/mach-qcom/Kconfig
|
|
||||||
@@ -6,6 +6,7 @@ menuconfig ARCH_QCOM
|
|
||||||
select CLKSRC_OF
|
|
||||||
select PINCTRL
|
|
||||||
select QCOM_SCM if SMP
|
|
||||||
+ select PCI_DOMAINS if PCI
|
|
||||||
help
|
|
||||||
Support for Qualcomm's devicetree based systems.
|
|
||||||
|
|
|
@ -1,654 +0,0 @@
|
||||||
From 58e214382bdd1eb48c5a3519182bddcb26edabad Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
Date: Wed, 26 Nov 2014 13:51:00 -0800
|
|
||||||
Subject: [PATCH] mfd: qcom-rpm: Driver for the Qualcomm RPM
|
|
||||||
|
|
||||||
Driver for the Resource Power Manager (RPM) found in Qualcomm 8660, 8960
|
|
||||||
and 8064 based devices. The driver exposes resources that child drivers
|
|
||||||
can operate on; to implementing regulator, clock and bus frequency
|
|
||||||
drivers.
|
|
||||||
|
|
||||||
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
Signed-off-by: Lee Jones <lee.jones@linaro.org>
|
|
||||||
---
|
|
||||||
drivers/mfd/Kconfig | 14 ++
|
|
||||||
drivers/mfd/Makefile | 1 +
|
|
||||||
drivers/mfd/qcom_rpm.c | 581 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
include/linux/mfd/qcom_rpm.h | 13 +
|
|
||||||
4 files changed, 609 insertions(+)
|
|
||||||
create mode 100644 drivers/mfd/qcom_rpm.c
|
|
||||||
create mode 100644 include/linux/mfd/qcom_rpm.h
|
|
||||||
|
|
||||||
--- a/drivers/mfd/Kconfig
|
|
||||||
+++ b/drivers/mfd/Kconfig
|
|
||||||
@@ -567,6 +567,20 @@ config MFD_PM8921_CORE
|
|
||||||
Say M here if you want to include support for PM8921 chip as a module.
|
|
||||||
This will build a module called "pm8921-core".
|
|
||||||
|
|
||||||
+config MFD_QCOM_RPM
|
|
||||||
+ tristate "Qualcomm Resource Power Manager (RPM)"
|
|
||||||
+ depends on ARCH_QCOM && OF
|
|
||||||
+ help
|
|
||||||
+ If you say yes to this option, support will be included for the
|
|
||||||
+ Resource Power Manager system found in the Qualcomm 8660, 8960 and
|
|
||||||
+ 8064 based devices.
|
|
||||||
+
|
|
||||||
+ This is required to access many regulators, clocks and bus
|
|
||||||
+ frequencies controlled by the RPM on these devices.
|
|
||||||
+
|
|
||||||
+ Say M here if you want to include support for the Qualcomm RPM as a
|
|
||||||
+ module. This will build a module called "qcom_rpm".
|
|
||||||
+
|
|
||||||
config MFD_SPMI_PMIC
|
|
||||||
tristate "Qualcomm SPMI PMICs"
|
|
||||||
depends on ARCH_QCOM || COMPILE_TEST
|
|
||||||
--- a/drivers/mfd/Makefile
|
|
||||||
+++ b/drivers/mfd/Makefile
|
|
||||||
@@ -153,6 +153,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-
|
|
||||||
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
|
|
||||||
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
|
|
||||||
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
|
|
||||||
+obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
|
|
||||||
obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
|
|
||||||
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
|
|
||||||
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/mfd/qcom_rpm.c
|
|
||||||
@@ -0,0 +1,581 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2014, Sony Mobile Communications AB.
|
|
||||||
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
|
||||||
+ * Author: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 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/module.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/of_platform.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <linux/mfd/qcom_rpm.h>
|
|
||||||
+#include <linux/mfd/syscon.h>
|
|
||||||
+#include <linux/regmap.h>
|
|
||||||
+
|
|
||||||
+#include <dt-bindings/mfd/qcom-rpm.h>
|
|
||||||
+
|
|
||||||
+struct qcom_rpm_resource {
|
|
||||||
+ unsigned target_id;
|
|
||||||
+ unsigned status_id;
|
|
||||||
+ unsigned select_id;
|
|
||||||
+ unsigned size;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct qcom_rpm_data {
|
|
||||||
+ u32 version;
|
|
||||||
+ const struct qcom_rpm_resource *resource_table;
|
|
||||||
+ unsigned n_resources;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct qcom_rpm {
|
|
||||||
+ struct device *dev;
|
|
||||||
+ struct regmap *ipc_regmap;
|
|
||||||
+ unsigned ipc_offset;
|
|
||||||
+ unsigned ipc_bit;
|
|
||||||
+
|
|
||||||
+ struct completion ack;
|
|
||||||
+ struct mutex lock;
|
|
||||||
+
|
|
||||||
+ void __iomem *status_regs;
|
|
||||||
+ void __iomem *ctrl_regs;
|
|
||||||
+ void __iomem *req_regs;
|
|
||||||
+
|
|
||||||
+ u32 ack_status;
|
|
||||||
+
|
|
||||||
+ const struct qcom_rpm_data *data;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4)
|
|
||||||
+#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4)
|
|
||||||
+#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4)
|
|
||||||
+
|
|
||||||
+#define RPM_REQUEST_TIMEOUT (5 * HZ)
|
|
||||||
+
|
|
||||||
+#define RPM_REQUEST_CONTEXT 3
|
|
||||||
+#define RPM_REQ_SELECT 11
|
|
||||||
+#define RPM_ACK_CONTEXT 15
|
|
||||||
+#define RPM_ACK_SELECTOR 23
|
|
||||||
+#define RPM_SELECT_SIZE 7
|
|
||||||
+
|
|
||||||
+#define RPM_NOTIFICATION BIT(30)
|
|
||||||
+#define RPM_REJECTED BIT(31)
|
|
||||||
+
|
|
||||||
+#define RPM_SIGNAL BIT(2)
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = {
|
|
||||||
+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
|
|
||||||
+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 },
|
|
||||||
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
|
|
||||||
+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
|
|
||||||
+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
|
|
||||||
+ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 },
|
|
||||||
+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 21 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS1] = { 116, 31, 30, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS2] = { 118, 33, 31, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS3] = { 120, 35, 32, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS4] = { 122, 37, 33, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS5] = { 124, 39, 34, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS6] = { 126, 41, 35, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS7] = { 128, 43, 36, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS8] = { 130, 45, 37, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO1] = { 132, 47, 38, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO2] = { 134, 49, 39, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO3] = { 136, 51, 40, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO4] = { 138, 53, 41, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO5] = { 140, 55, 42, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO6] = { 142, 57, 43, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO7] = { 144, 59, 44, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO8] = { 146, 61, 45, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO9] = { 148, 63, 46, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO10] = { 150, 65, 47, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO11] = { 152, 67, 48, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO12] = { 154, 69, 49, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO13] = { 156, 71, 50, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO14] = { 158, 73, 51, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO15] = { 160, 75, 52, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO16] = { 162, 77, 53, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO17] = { 164, 79, 54, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO18] = { 166, 81, 55, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO19] = { 168, 83, 56, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO20] = { 170, 85, 57, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO21] = { 172, 87, 58, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO22] = { 174, 89, 59, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO23] = { 176, 91, 60, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO24] = { 178, 93, 61, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO25] = { 180, 95, 62, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO26] = { 182, 97, 63, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO27] = { 184, 99, 64, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO28] = { 186, 101, 65, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO29] = { 188, 103, 66, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_CLK1] = { 190, 105, 67, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_CLK2] = { 192, 107, 68, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS1] = { 194, 109, 69, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS2] = { 195, 110, 70, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS3] = { 196, 111, 71, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS4] = { 197, 112, 72, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS5] = { 198, 113, 73, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS6] = { 199, 114, 74, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS7] = { 200, 115, 75, 1 },
|
|
||||||
+ [QCOM_RPM_PM8821_SMPS1] = { 201, 116, 76, 2 },
|
|
||||||
+ [QCOM_RPM_PM8821_SMPS2] = { 203, 118, 77, 2 },
|
|
||||||
+ [QCOM_RPM_PM8821_LDO1] = { 205, 120, 78, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_NCP] = { 207, 122, 80, 2 },
|
|
||||||
+ [QCOM_RPM_CXO_BUFFERS] = { 209, 124, 81, 1 },
|
|
||||||
+ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 125, 82, 1 },
|
|
||||||
+ [QCOM_RPM_HDMI_SWITCH] = { 211, 126, 83, 1 },
|
|
||||||
+ [QCOM_RPM_DDR_DMM] = { 212, 127, 84, 2 },
|
|
||||||
+ [QCOM_RPM_VDDMIN_GPIO] = { 215, 131, 89, 1 },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_data apq8064_template = {
|
|
||||||
+ .version = 3,
|
|
||||||
+ .resource_table = apq8064_rpm_resource_table,
|
|
||||||
+ .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = {
|
|
||||||
+ [QCOM_RPM_CXO_CLK] = { 32, 12, 5, 1 },
|
|
||||||
+ [QCOM_RPM_PXO_CLK] = { 33, 13, 6, 1 },
|
|
||||||
+ [QCOM_RPM_PLL_4] = { 34, 14, 7, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 35, 15, 8, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 36, 16, 9, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_CLK] = { 37, 17, 10, 1 },
|
|
||||||
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 38, 18, 11, 1 },
|
|
||||||
+ [QCOM_RPM_SFPB_CLK] = { 39, 19, 12, 1 },
|
|
||||||
+ [QCOM_RPM_CFPB_CLK] = { 40, 20, 13, 1 },
|
|
||||||
+ [QCOM_RPM_MMFPB_CLK] = { 41, 21, 14, 1 },
|
|
||||||
+ [QCOM_RPM_SMI_CLK] = { 42, 22, 15, 1 },
|
|
||||||
+ [QCOM_RPM_EBI1_CLK] = { 43, 23, 16, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_L2_CACHE_CTL] = { 44, 24, 17, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 45, 25, 18, 2 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 47, 26, 19, 3 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 51, 28, 21, 6 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 63, 29, 22, 2 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 65, 30, 23, 3 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 69, 32, 25, 22 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_HALT] = { 105, 33, 26, 2 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_MODE] = { 107, 34, 27, 3 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_ARB] = { 111, 36, 29, 23 },
|
|
||||||
+ [QCOM_RPM_PM8901_SMPS0] = { 134, 37, 30, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_SMPS1] = { 136, 39, 31, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_SMPS2] = { 138, 41, 32, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_SMPS3] = { 140, 43, 33, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_SMPS4] = { 142, 45, 34, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LDO0] = { 144, 47, 35, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LDO1] = { 146, 49, 36, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LDO2] = { 148, 51, 37, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LDO3] = { 150, 53, 38, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LDO4] = { 152, 55, 39, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LDO5] = { 154, 57, 40, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LDO6] = { 156, 59, 41, 2 },
|
|
||||||
+ [QCOM_RPM_PM8901_LVS0] = { 158, 61, 42, 1 },
|
|
||||||
+ [QCOM_RPM_PM8901_LVS1] = { 159, 62, 43, 1 },
|
|
||||||
+ [QCOM_RPM_PM8901_LVS2] = { 160, 63, 44, 1 },
|
|
||||||
+ [QCOM_RPM_PM8901_LVS3] = { 161, 64, 45, 1 },
|
|
||||||
+ [QCOM_RPM_PM8901_MVS] = { 162, 65, 46, 1 },
|
|
||||||
+ [QCOM_RPM_PM8058_SMPS0] = { 163, 66, 47, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_SMPS1] = { 165, 68, 48, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_SMPS2] = { 167, 70, 49, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_SMPS3] = { 169, 72, 50, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_SMPS4] = { 171, 74, 51, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO0] = { 173, 76, 52, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO1] = { 175, 78, 53, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO2] = { 177, 80, 54, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO3] = { 179, 82, 55, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO4] = { 181, 84, 56, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO5] = { 183, 86, 57, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO6] = { 185, 88, 58, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO7] = { 187, 90, 59, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO8] = { 189, 92, 60, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO9] = { 191, 94, 61, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO10] = { 193, 96, 62, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO11] = { 195, 98, 63, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO12] = { 197, 100, 64, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO13] = { 199, 102, 65, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO14] = { 201, 104, 66, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO15] = { 203, 106, 67, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO16] = { 205, 108, 68, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO17] = { 207, 110, 69, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO18] = { 209, 112, 70, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO19] = { 211, 114, 71, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO20] = { 213, 116, 72, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO21] = { 215, 118, 73, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO22] = { 217, 120, 74, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO23] = { 219, 122, 75, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO24] = { 221, 124, 76, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LDO25] = { 223, 126, 77, 2 },
|
|
||||||
+ [QCOM_RPM_PM8058_LVS0] = { 225, 128, 78, 1 },
|
|
||||||
+ [QCOM_RPM_PM8058_LVS1] = { 226, 129, 79, 1 },
|
|
||||||
+ [QCOM_RPM_PM8058_NCP] = { 227, 130, 80, 2 },
|
|
||||||
+ [QCOM_RPM_CXO_BUFFERS] = { 229, 132, 81, 1 },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_data msm8660_template = {
|
|
||||||
+ .version = 2,
|
|
||||||
+ .resource_table = msm8660_rpm_resource_table,
|
|
||||||
+ .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = {
|
|
||||||
+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
|
|
||||||
+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 },
|
|
||||||
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
|
|
||||||
+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
|
|
||||||
+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
|
|
||||||
+ [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 },
|
|
||||||
+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 29 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_HALT] = { 88, 27, 26, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_MODE] = { 90, 28, 27, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 93, 29, 28, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_ARB] = { 94, 30, 29, 23 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS1] = { 117, 31, 30, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS2] = { 119, 33, 31, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS3] = { 121, 35, 32, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS4] = { 123, 37, 33, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS5] = { 125, 39, 34, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS6] = { 127, 41, 35, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS7] = { 129, 43, 36, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_SMPS8] = { 131, 45, 37, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO1] = { 133, 47, 38, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO2] = { 135, 49, 39, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO3] = { 137, 51, 40, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO4] = { 139, 53, 41, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO5] = { 141, 55, 42, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO6] = { 143, 57, 43, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO7] = { 145, 59, 44, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO8] = { 147, 61, 45, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO9] = { 149, 63, 46, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO10] = { 151, 65, 47, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO11] = { 153, 67, 48, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO12] = { 155, 69, 49, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO13] = { 157, 71, 50, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO14] = { 159, 73, 51, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO15] = { 161, 75, 52, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO16] = { 163, 77, 53, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO17] = { 165, 79, 54, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO18] = { 167, 81, 55, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO19] = { 169, 83, 56, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO20] = { 171, 85, 57, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO21] = { 173, 87, 58, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO22] = { 175, 89, 59, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO23] = { 177, 91, 60, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO24] = { 179, 93, 61, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO25] = { 181, 95, 62, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO26] = { 183, 97, 63, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO27] = { 185, 99, 64, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO28] = { 187, 101, 65, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LDO29] = { 189, 103, 66, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_CLK1] = { 191, 105, 67, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_CLK2] = { 193, 107, 68, 2 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS1] = { 195, 109, 69, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS2] = { 196, 110, 70, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS3] = { 197, 111, 71, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS4] = { 198, 112, 72, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS5] = { 199, 113, 73, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS6] = { 200, 114, 74, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_LVS7] = { 201, 115, 75, 1 },
|
|
||||||
+ [QCOM_RPM_PM8921_NCP] = { 202, 116, 80, 2 },
|
|
||||||
+ [QCOM_RPM_CXO_BUFFERS] = { 204, 118, 81, 1 },
|
|
||||||
+ [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 },
|
|
||||||
+ [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 },
|
|
||||||
+ [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_data msm8960_template = {
|
|
||||||
+ .version = 3,
|
|
||||||
+ .resource_table = msm8960_rpm_resource_table,
|
|
||||||
+ .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct of_device_id qcom_rpm_of_match[] = {
|
|
||||||
+ { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
|
|
||||||
+ { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
|
|
||||||
+ { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
|
|
||||||
+ { }
|
|
||||||
+};
|
|
||||||
+MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
|
|
||||||
+
|
|
||||||
+int qcom_rpm_write(struct qcom_rpm *rpm,
|
|
||||||
+ int state,
|
|
||||||
+ int resource,
|
|
||||||
+ u32 *buf, size_t count)
|
|
||||||
+{
|
|
||||||
+ const struct qcom_rpm_resource *res;
|
|
||||||
+ const struct qcom_rpm_data *data = rpm->data;
|
|
||||||
+ u32 sel_mask[RPM_SELECT_SIZE] = { 0 };
|
|
||||||
+ int left;
|
|
||||||
+ int ret = 0;
|
|
||||||
+ int i;
|
|
||||||
+
|
|
||||||
+ if (WARN_ON(resource < 0 || resource >= data->n_resources))
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ res = &data->resource_table[resource];
|
|
||||||
+ if (WARN_ON(res->size != count))
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&rpm->lock);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < res->size; i++)
|
|
||||||
+ writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i));
|
|
||||||
+
|
|
||||||
+ bitmap_set((unsigned long *)sel_mask, res->select_id, 1);
|
|
||||||
+ for (i = 0; i < ARRAY_SIZE(sel_mask); i++) {
|
|
||||||
+ writel_relaxed(sel_mask[i],
|
|
||||||
+ RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i));
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ writel_relaxed(BIT(state), RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT));
|
|
||||||
+
|
|
||||||
+ reinit_completion(&rpm->ack);
|
|
||||||
+ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
|
|
||||||
+
|
|
||||||
+ left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT);
|
|
||||||
+ if (!left)
|
|
||||||
+ ret = -ETIMEDOUT;
|
|
||||||
+ else if (rpm->ack_status & RPM_REJECTED)
|
|
||||||
+ ret = -EIO;
|
|
||||||
+
|
|
||||||
+ mutex_unlock(&rpm->lock);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qcom_rpm_write);
|
|
||||||
+
|
|
||||||
+static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev)
|
|
||||||
+{
|
|
||||||
+ struct qcom_rpm *rpm = dev;
|
|
||||||
+ u32 ack;
|
|
||||||
+ int i;
|
|
||||||
+
|
|
||||||
+ ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
|
|
||||||
+ for (i = 0; i < RPM_SELECT_SIZE; i++)
|
|
||||||
+ writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i));
|
|
||||||
+ writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT));
|
|
||||||
+
|
|
||||||
+ if (ack & RPM_NOTIFICATION) {
|
|
||||||
+ dev_warn(rpm->dev, "ignoring notification!\n");
|
|
||||||
+ } else {
|
|
||||||
+ rpm->ack_status = ack;
|
|
||||||
+ complete(&rpm->ack);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev)
|
|
||||||
+{
|
|
||||||
+ struct qcom_rpm *rpm = dev;
|
|
||||||
+
|
|
||||||
+ regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit));
|
|
||||||
+ dev_err(rpm->dev, "RPM triggered fatal error\n");
|
|
||||||
+
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev)
|
|
||||||
+{
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_rpm_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ const struct of_device_id *match;
|
|
||||||
+ struct device_node *syscon_np;
|
|
||||||
+ struct resource *res;
|
|
||||||
+ struct qcom_rpm *rpm;
|
|
||||||
+ u32 fw_version[3];
|
|
||||||
+ int irq_wakeup;
|
|
||||||
+ int irq_ack;
|
|
||||||
+ int irq_err;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL);
|
|
||||||
+ if (!rpm)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ rpm->dev = &pdev->dev;
|
|
||||||
+ mutex_init(&rpm->lock);
|
|
||||||
+ init_completion(&rpm->ack);
|
|
||||||
+
|
|
||||||
+ irq_ack = platform_get_irq_byname(pdev, "ack");
|
|
||||||
+ if (irq_ack < 0) {
|
|
||||||
+ dev_err(&pdev->dev, "required ack interrupt missing\n");
|
|
||||||
+ return irq_ack;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ irq_err = platform_get_irq_byname(pdev, "err");
|
|
||||||
+ if (irq_err < 0) {
|
|
||||||
+ dev_err(&pdev->dev, "required err interrupt missing\n");
|
|
||||||
+ return irq_err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ irq_wakeup = platform_get_irq_byname(pdev, "wakeup");
|
|
||||||
+ if (irq_wakeup < 0) {
|
|
||||||
+ dev_err(&pdev->dev, "required wakeup interrupt missing\n");
|
|
||||||
+ return irq_wakeup;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ match = of_match_device(qcom_rpm_of_match, &pdev->dev);
|
|
||||||
+ rpm->data = match->data;
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ rpm->status_regs = devm_ioremap_resource(&pdev->dev, res);
|
|
||||||
+ if (IS_ERR(rpm->status_regs))
|
|
||||||
+ return PTR_ERR(rpm->status_regs);
|
|
||||||
+ rpm->ctrl_regs = rpm->status_regs + 0x400;
|
|
||||||
+ rpm->req_regs = rpm->status_regs + 0x600;
|
|
||||||
+
|
|
||||||
+ syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0);
|
|
||||||
+ if (!syscon_np) {
|
|
||||||
+ dev_err(&pdev->dev, "no qcom,ipc node\n");
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ rpm->ipc_regmap = syscon_node_to_regmap(syscon_np);
|
|
||||||
+ if (IS_ERR(rpm->ipc_regmap))
|
|
||||||
+ return PTR_ERR(rpm->ipc_regmap);
|
|
||||||
+
|
|
||||||
+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1,
|
|
||||||
+ &rpm->ipc_offset);
|
|
||||||
+ if (ret < 0) {
|
|
||||||
+ dev_err(&pdev->dev, "no offset in qcom,ipc\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2,
|
|
||||||
+ &rpm->ipc_bit);
|
|
||||||
+ if (ret < 0) {
|
|
||||||
+ dev_err(&pdev->dev, "no bit in qcom,ipc\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ dev_set_drvdata(&pdev->dev, rpm);
|
|
||||||
+
|
|
||||||
+ fw_version[0] = readl(RPM_STATUS_REG(rpm, 0));
|
|
||||||
+ fw_version[1] = readl(RPM_STATUS_REG(rpm, 1));
|
|
||||||
+ fw_version[2] = readl(RPM_STATUS_REG(rpm, 2));
|
|
||||||
+ if (fw_version[0] != rpm->data->version) {
|
|
||||||
+ dev_err(&pdev->dev,
|
|
||||||
+ "RPM version %u.%u.%u incompatible with driver version %u",
|
|
||||||
+ fw_version[0],
|
|
||||||
+ fw_version[1],
|
|
||||||
+ fw_version[2],
|
|
||||||
+ rpm->data->version);
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0],
|
|
||||||
+ fw_version[1],
|
|
||||||
+ fw_version[2]);
|
|
||||||
+
|
|
||||||
+ ret = devm_request_irq(&pdev->dev,
|
|
||||||
+ irq_ack,
|
|
||||||
+ qcom_rpm_ack_interrupt,
|
|
||||||
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
|
|
||||||
+ "qcom_rpm_ack",
|
|
||||||
+ rpm);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(&pdev->dev, "failed to request ack interrupt\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = irq_set_irq_wake(irq_ack, 1);
|
|
||||||
+ if (ret)
|
|
||||||
+ dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n");
|
|
||||||
+
|
|
||||||
+ ret = devm_request_irq(&pdev->dev,
|
|
||||||
+ irq_err,
|
|
||||||
+ qcom_rpm_err_interrupt,
|
|
||||||
+ IRQF_TRIGGER_RISING,
|
|
||||||
+ "qcom_rpm_err",
|
|
||||||
+ rpm);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(&pdev->dev, "failed to request err interrupt\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = devm_request_irq(&pdev->dev,
|
|
||||||
+ irq_wakeup,
|
|
||||||
+ qcom_rpm_wakeup_interrupt,
|
|
||||||
+ IRQF_TRIGGER_RISING,
|
|
||||||
+ "qcom_rpm_wakeup",
|
|
||||||
+ rpm);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(&pdev->dev, "failed to request wakeup interrupt\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = irq_set_irq_wake(irq_wakeup, 1);
|
|
||||||
+ if (ret)
|
|
||||||
+ dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n");
|
|
||||||
+
|
|
||||||
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qcom_rpm_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ of_platform_depopulate(&pdev->dev);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver qcom_rpm_driver = {
|
|
||||||
+ .probe = qcom_rpm_probe,
|
|
||||||
+ .remove = qcom_rpm_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "qcom_rpm",
|
|
||||||
+ .of_match_table = qcom_rpm_of_match,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init qcom_rpm_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&qcom_rpm_driver);
|
|
||||||
+}
|
|
||||||
+arch_initcall(qcom_rpm_init);
|
|
||||||
+
|
|
||||||
+static void __exit qcom_rpm_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&qcom_rpm_driver);
|
|
||||||
+}
|
|
||||||
+module_exit(qcom_rpm_exit)
|
|
||||||
+
|
|
||||||
+MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
+MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/linux/mfd/qcom_rpm.h
|
|
||||||
@@ -0,0 +1,13 @@
|
|
||||||
+#ifndef __QCOM_RPM_H__
|
|
||||||
+#define __QCOM_RPM_H__
|
|
||||||
+
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+
|
|
||||||
+struct qcom_rpm;
|
|
||||||
+
|
|
||||||
+#define QCOM_RPM_ACTIVE_STATE 0
|
|
||||||
+#define QCOM_RPM_SLEEP_STATE 1
|
|
||||||
+
|
|
||||||
+int qcom_rpm_write(struct qcom_rpm *rpm, int state, int resource, u32 *buf, size_t count);
|
|
||||||
+
|
|
||||||
+#endif
|
|
|
@ -1,71 +0,0 @@
|
||||||
From 4d54b0adfa67476e6509bc8646b9dbac642e8a29 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Date: Thu, 26 Mar 2015 11:29:26 -0700
|
|
||||||
Subject: [PATCH] mfd: qcom_rpm: Add support for IPQ8064
|
|
||||||
|
|
||||||
The IPQ8064 also includes an RPM following the same message structure as
|
|
||||||
other chips. In addition, it supports a few new resource types to
|
|
||||||
support the NSS fabric clocks and the SMB208/SMB209 regulators found on
|
|
||||||
the reference boards.
|
|
||||||
|
|
||||||
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Signed-off-by: Lee Jones <lee.jones@linaro.org>
|
|
||||||
---
|
|
||||||
drivers/mfd/qcom_rpm.c | 41 +++++++++++++++++++++++++++++++++++++++++
|
|
||||||
1 file changed, 41 insertions(+)
|
|
||||||
|
|
||||||
--- a/drivers/mfd/qcom_rpm.c
|
|
||||||
+++ b/drivers/mfd/qcom_rpm.c
|
|
||||||
@@ -323,10 +323,51 @@ static const struct qcom_rpm_data msm896
|
|
||||||
.n_resources = ARRAY_SIZE(msm8960_rpm_resource_table),
|
|
||||||
};
|
|
||||||
|
|
||||||
+static const struct qcom_rpm_resource ipq806x_rpm_resource_table[] = {
|
|
||||||
+ [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 },
|
|
||||||
+ [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 },
|
|
||||||
+ [QCOM_RPM_NSS_FABRIC_0_CLK] = { 29, 13, 10, 1 },
|
|
||||||
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 },
|
|
||||||
+ [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 },
|
|
||||||
+ [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 },
|
|
||||||
+ [QCOM_RPM_NSS_FABRIC_1_CLK] = { 33, 17, 14, 1 },
|
|
||||||
+ [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 2 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 3 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 },
|
|
||||||
+ [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 2 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 3 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 },
|
|
||||||
+ [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 2 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 3 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 },
|
|
||||||
+ [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 2 },
|
|
||||||
+ [QCOM_RPM_CXO_BUFFERS] = { 209, 33, 31, 1 },
|
|
||||||
+ [QCOM_RPM_USB_OTG_SWITCH] = { 210, 34, 32, 1 },
|
|
||||||
+ [QCOM_RPM_HDMI_SWITCH] = { 211, 35, 33, 1 },
|
|
||||||
+ [QCOM_RPM_DDR_DMM] = { 212, 36, 34, 2 },
|
|
||||||
+ [QCOM_RPM_VDDMIN_GPIO] = { 215, 40, 39, 1 },
|
|
||||||
+ [QCOM_RPM_SMB208_S1a] = { 216, 41, 90, 2 },
|
|
||||||
+ [QCOM_RPM_SMB208_S1b] = { 218, 43, 91, 2 },
|
|
||||||
+ [QCOM_RPM_SMB208_S2a] = { 220, 45, 92, 2 },
|
|
||||||
+ [QCOM_RPM_SMB208_S2b] = { 222, 47, 93, 2 },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct qcom_rpm_data ipq806x_template = {
|
|
||||||
+ .version = 3,
|
|
||||||
+ .resource_table = ipq806x_rpm_resource_table,
|
|
||||||
+ .n_resources = ARRAY_SIZE(ipq806x_rpm_resource_table),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static const struct of_device_id qcom_rpm_of_match[] = {
|
|
||||||
{ .compatible = "qcom,rpm-apq8064", .data = &apq8064_template },
|
|
||||||
{ .compatible = "qcom,rpm-msm8660", .data = &msm8660_template },
|
|
||||||
{ .compatible = "qcom,rpm-msm8960", .data = &msm8960_template },
|
|
||||||
+ { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
|
|
|
@ -1,247 +0,0 @@
|
||||||
From aa0c4b815045420ea54d5ae5362f5a0190609d46 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
Date: Wed, 26 Nov 2014 13:50:59 -0800
|
|
||||||
Subject: [PATCH] mfd: devicetree: bindings: Add Qualcomm RPM DT binding
|
|
||||||
|
|
||||||
Add binding for the Qualcomm Resource Power Manager (RPM) found in 8660,
|
|
||||||
8960 and 8064 based devices.
|
|
||||||
|
|
||||||
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
Signed-off-by: Lee Jones <lee.jones@linaro.org>
|
|
||||||
---
|
|
||||||
Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 70 ++++++++++
|
|
||||||
include/dt-bindings/mfd/qcom-rpm.h | 154 +++++++++++++++++++++
|
|
||||||
2 files changed, 224 insertions(+)
|
|
||||||
create mode 100644 Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
|
||||||
create mode 100644 include/dt-bindings/mfd/qcom-rpm.h
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
|
||||||
@@ -0,0 +1,70 @@
|
|
||||||
+Qualcomm Resource Power Manager (RPM)
|
|
||||||
+
|
|
||||||
+This driver is used to interface with the Resource Power Manager (RPM) found in
|
|
||||||
+various Qualcomm platforms. The RPM allows each component in the system to vote
|
|
||||||
+for state of the system resources, such as clocks, regulators and bus
|
|
||||||
+frequencies.
|
|
||||||
+
|
|
||||||
+- compatible:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: must be one of:
|
|
||||||
+ "qcom,rpm-apq8064"
|
|
||||||
+ "qcom,rpm-msm8660"
|
|
||||||
+ "qcom,rpm-msm8960"
|
|
||||||
+
|
|
||||||
+- reg:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: base address and size of the RPM's message ram
|
|
||||||
+
|
|
||||||
+- interrupts:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: three entries specifying the RPM's:
|
|
||||||
+ 1. acknowledgement interrupt
|
|
||||||
+ 2. error interrupt
|
|
||||||
+ 3. wakeup interrupt
|
|
||||||
+
|
|
||||||
+- interrupt-names:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string-array>
|
|
||||||
+ Definition: must be the three strings "ack", "err" and "wakeup", in order
|
|
||||||
+
|
|
||||||
+- #address-cells:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <u32>
|
|
||||||
+ Definition: must be 1
|
|
||||||
+
|
|
||||||
+- #size-cells:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <u32>
|
|
||||||
+ Definition: must be 0
|
|
||||||
+
|
|
||||||
+- qcom,ipc:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+
|
|
||||||
+ Definition: three entries specifying the outgoing ipc bit used for
|
|
||||||
+ signaling the RPM:
|
|
||||||
+ - phandle to a syscon node representing the apcs registers
|
|
||||||
+ - u32 representing offset to the register within the syscon
|
|
||||||
+ - u32 representing the ipc bit within the register
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+= EXAMPLE
|
|
||||||
+
|
|
||||||
+ #include <dt-bindings/mfd/qcom-rpm.h>
|
|
||||||
+
|
|
||||||
+ rpm@108000 {
|
|
||||||
+ compatible = "qcom,rpm-msm8960";
|
|
||||||
+ reg = <0x108000 0x1000>;
|
|
||||||
+ qcom,ipc = <&apcs 0x8 2>;
|
|
||||||
+
|
|
||||||
+ interrupts = <0 19 0>, <0 21 0>, <0 22 0>;
|
|
||||||
+ interrupt-names = "ack", "err", "wakeup";
|
|
||||||
+
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <0>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/dt-bindings/mfd/qcom-rpm.h
|
|
||||||
@@ -0,0 +1,154 @@
|
|
||||||
+/*
|
|
||||||
+ * This header provides constants for the Qualcomm RPM bindings.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef _DT_BINDINGS_MFD_QCOM_RPM_H
|
|
||||||
+#define _DT_BINDINGS_MFD_QCOM_RPM_H
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Constants use to identify individual resources in the RPM.
|
|
||||||
+ */
|
|
||||||
+#define QCOM_RPM_APPS_FABRIC_ARB 1
|
|
||||||
+#define QCOM_RPM_APPS_FABRIC_CLK 2
|
|
||||||
+#define QCOM_RPM_APPS_FABRIC_HALT 3
|
|
||||||
+#define QCOM_RPM_APPS_FABRIC_IOCTL 4
|
|
||||||
+#define QCOM_RPM_APPS_FABRIC_MODE 5
|
|
||||||
+#define QCOM_RPM_APPS_L2_CACHE_CTL 6
|
|
||||||
+#define QCOM_RPM_CFPB_CLK 7
|
|
||||||
+#define QCOM_RPM_CXO_BUFFERS 8
|
|
||||||
+#define QCOM_RPM_CXO_CLK 9
|
|
||||||
+#define QCOM_RPM_DAYTONA_FABRIC_CLK 10
|
|
||||||
+#define QCOM_RPM_DDR_DMM 11
|
|
||||||
+#define QCOM_RPM_EBI1_CLK 12
|
|
||||||
+#define QCOM_RPM_HDMI_SWITCH 13
|
|
||||||
+#define QCOM_RPM_MMFPB_CLK 14
|
|
||||||
+#define QCOM_RPM_MM_FABRIC_ARB 15
|
|
||||||
+#define QCOM_RPM_MM_FABRIC_CLK 16
|
|
||||||
+#define QCOM_RPM_MM_FABRIC_HALT 17
|
|
||||||
+#define QCOM_RPM_MM_FABRIC_IOCTL 18
|
|
||||||
+#define QCOM_RPM_MM_FABRIC_MODE 19
|
|
||||||
+#define QCOM_RPM_PLL_4 20
|
|
||||||
+#define QCOM_RPM_PM8058_LDO0 21
|
|
||||||
+#define QCOM_RPM_PM8058_LDO1 22
|
|
||||||
+#define QCOM_RPM_PM8058_LDO2 23
|
|
||||||
+#define QCOM_RPM_PM8058_LDO3 24
|
|
||||||
+#define QCOM_RPM_PM8058_LDO4 25
|
|
||||||
+#define QCOM_RPM_PM8058_LDO5 26
|
|
||||||
+#define QCOM_RPM_PM8058_LDO6 27
|
|
||||||
+#define QCOM_RPM_PM8058_LDO7 28
|
|
||||||
+#define QCOM_RPM_PM8058_LDO8 29
|
|
||||||
+#define QCOM_RPM_PM8058_LDO9 30
|
|
||||||
+#define QCOM_RPM_PM8058_LDO10 31
|
|
||||||
+#define QCOM_RPM_PM8058_LDO11 32
|
|
||||||
+#define QCOM_RPM_PM8058_LDO12 33
|
|
||||||
+#define QCOM_RPM_PM8058_LDO13 34
|
|
||||||
+#define QCOM_RPM_PM8058_LDO14 35
|
|
||||||
+#define QCOM_RPM_PM8058_LDO15 36
|
|
||||||
+#define QCOM_RPM_PM8058_LDO16 37
|
|
||||||
+#define QCOM_RPM_PM8058_LDO17 38
|
|
||||||
+#define QCOM_RPM_PM8058_LDO18 39
|
|
||||||
+#define QCOM_RPM_PM8058_LDO19 40
|
|
||||||
+#define QCOM_RPM_PM8058_LDO20 41
|
|
||||||
+#define QCOM_RPM_PM8058_LDO21 42
|
|
||||||
+#define QCOM_RPM_PM8058_LDO22 43
|
|
||||||
+#define QCOM_RPM_PM8058_LDO23 44
|
|
||||||
+#define QCOM_RPM_PM8058_LDO24 45
|
|
||||||
+#define QCOM_RPM_PM8058_LDO25 46
|
|
||||||
+#define QCOM_RPM_PM8058_LVS0 47
|
|
||||||
+#define QCOM_RPM_PM8058_LVS1 48
|
|
||||||
+#define QCOM_RPM_PM8058_NCP 49
|
|
||||||
+#define QCOM_RPM_PM8058_SMPS0 50
|
|
||||||
+#define QCOM_RPM_PM8058_SMPS1 51
|
|
||||||
+#define QCOM_RPM_PM8058_SMPS2 52
|
|
||||||
+#define QCOM_RPM_PM8058_SMPS3 53
|
|
||||||
+#define QCOM_RPM_PM8058_SMPS4 54
|
|
||||||
+#define QCOM_RPM_PM8821_LDO1 55
|
|
||||||
+#define QCOM_RPM_PM8821_SMPS1 56
|
|
||||||
+#define QCOM_RPM_PM8821_SMPS2 57
|
|
||||||
+#define QCOM_RPM_PM8901_LDO0 58
|
|
||||||
+#define QCOM_RPM_PM8901_LDO1 59
|
|
||||||
+#define QCOM_RPM_PM8901_LDO2 60
|
|
||||||
+#define QCOM_RPM_PM8901_LDO3 61
|
|
||||||
+#define QCOM_RPM_PM8901_LDO4 62
|
|
||||||
+#define QCOM_RPM_PM8901_LDO5 63
|
|
||||||
+#define QCOM_RPM_PM8901_LDO6 64
|
|
||||||
+#define QCOM_RPM_PM8901_LVS0 65
|
|
||||||
+#define QCOM_RPM_PM8901_LVS1 66
|
|
||||||
+#define QCOM_RPM_PM8901_LVS2 67
|
|
||||||
+#define QCOM_RPM_PM8901_LVS3 68
|
|
||||||
+#define QCOM_RPM_PM8901_MVS 69
|
|
||||||
+#define QCOM_RPM_PM8901_SMPS0 70
|
|
||||||
+#define QCOM_RPM_PM8901_SMPS1 71
|
|
||||||
+#define QCOM_RPM_PM8901_SMPS2 72
|
|
||||||
+#define QCOM_RPM_PM8901_SMPS3 73
|
|
||||||
+#define QCOM_RPM_PM8901_SMPS4 74
|
|
||||||
+#define QCOM_RPM_PM8921_CLK1 75
|
|
||||||
+#define QCOM_RPM_PM8921_CLK2 76
|
|
||||||
+#define QCOM_RPM_PM8921_LDO1 77
|
|
||||||
+#define QCOM_RPM_PM8921_LDO2 78
|
|
||||||
+#define QCOM_RPM_PM8921_LDO3 79
|
|
||||||
+#define QCOM_RPM_PM8921_LDO4 80
|
|
||||||
+#define QCOM_RPM_PM8921_LDO5 81
|
|
||||||
+#define QCOM_RPM_PM8921_LDO6 82
|
|
||||||
+#define QCOM_RPM_PM8921_LDO7 83
|
|
||||||
+#define QCOM_RPM_PM8921_LDO8 84
|
|
||||||
+#define QCOM_RPM_PM8921_LDO9 85
|
|
||||||
+#define QCOM_RPM_PM8921_LDO10 86
|
|
||||||
+#define QCOM_RPM_PM8921_LDO11 87
|
|
||||||
+#define QCOM_RPM_PM8921_LDO12 88
|
|
||||||
+#define QCOM_RPM_PM8921_LDO13 89
|
|
||||||
+#define QCOM_RPM_PM8921_LDO14 90
|
|
||||||
+#define QCOM_RPM_PM8921_LDO15 91
|
|
||||||
+#define QCOM_RPM_PM8921_LDO16 92
|
|
||||||
+#define QCOM_RPM_PM8921_LDO17 93
|
|
||||||
+#define QCOM_RPM_PM8921_LDO18 94
|
|
||||||
+#define QCOM_RPM_PM8921_LDO19 95
|
|
||||||
+#define QCOM_RPM_PM8921_LDO20 96
|
|
||||||
+#define QCOM_RPM_PM8921_LDO21 97
|
|
||||||
+#define QCOM_RPM_PM8921_LDO22 98
|
|
||||||
+#define QCOM_RPM_PM8921_LDO23 99
|
|
||||||
+#define QCOM_RPM_PM8921_LDO24 100
|
|
||||||
+#define QCOM_RPM_PM8921_LDO25 101
|
|
||||||
+#define QCOM_RPM_PM8921_LDO26 102
|
|
||||||
+#define QCOM_RPM_PM8921_LDO27 103
|
|
||||||
+#define QCOM_RPM_PM8921_LDO28 104
|
|
||||||
+#define QCOM_RPM_PM8921_LDO29 105
|
|
||||||
+#define QCOM_RPM_PM8921_LVS1 106
|
|
||||||
+#define QCOM_RPM_PM8921_LVS2 107
|
|
||||||
+#define QCOM_RPM_PM8921_LVS3 108
|
|
||||||
+#define QCOM_RPM_PM8921_LVS4 109
|
|
||||||
+#define QCOM_RPM_PM8921_LVS5 110
|
|
||||||
+#define QCOM_RPM_PM8921_LVS6 111
|
|
||||||
+#define QCOM_RPM_PM8921_LVS7 112
|
|
||||||
+#define QCOM_RPM_PM8921_MVS 113
|
|
||||||
+#define QCOM_RPM_PM8921_NCP 114
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS1 115
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS2 116
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS3 117
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS4 118
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS5 119
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS6 120
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS7 121
|
|
||||||
+#define QCOM_RPM_PM8921_SMPS8 122
|
|
||||||
+#define QCOM_RPM_PXO_CLK 123
|
|
||||||
+#define QCOM_RPM_QDSS_CLK 124
|
|
||||||
+#define QCOM_RPM_SFPB_CLK 125
|
|
||||||
+#define QCOM_RPM_SMI_CLK 126
|
|
||||||
+#define QCOM_RPM_SYS_FABRIC_ARB 127
|
|
||||||
+#define QCOM_RPM_SYS_FABRIC_CLK 128
|
|
||||||
+#define QCOM_RPM_SYS_FABRIC_HALT 129
|
|
||||||
+#define QCOM_RPM_SYS_FABRIC_IOCTL 130
|
|
||||||
+#define QCOM_RPM_SYS_FABRIC_MODE 131
|
|
||||||
+#define QCOM_RPM_USB_OTG_SWITCH 132
|
|
||||||
+#define QCOM_RPM_VDDMIN_GPIO 133
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Constants used to select force mode for regulators.
|
|
||||||
+ */
|
|
||||||
+#define QCOM_RPM_FORCE_MODE_NONE 0
|
|
||||||
+#define QCOM_RPM_FORCE_MODE_LPM 1
|
|
||||||
+#define QCOM_RPM_FORCE_MODE_HPM 2
|
|
||||||
+#define QCOM_RPM_FORCE_MODE_AUTO 3
|
|
||||||
+#define QCOM_RPM_FORCE_MODE_BYPASS 4
|
|
||||||
+
|
|
||||||
+#endif
|
|
|
@ -1,42 +0,0 @@
|
||||||
From 30bc3aa5c4ed3072bdff7d915772df1b91307ed4 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Date: Thu, 26 Mar 2015 11:29:25 -0700
|
|
||||||
Subject: [PATCH] mfd: devicetree: qcom_rpm: Document IPQ8064 resources
|
|
||||||
|
|
||||||
The IPQ8064 SoC has several RPM-controlled resources, an NSS fabrick
|
|
||||||
clock and four regulator resources. Provide definitions for them.
|
|
||||||
|
|
||||||
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
[sboyd@codeaurora.org: Drop regulator part of binding]
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Signed-off-by: Lee Jones <lee.jones@linaro.org>
|
|
||||||
---
|
|
||||||
Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 1 +
|
|
||||||
include/dt-bindings/mfd/qcom-rpm.h | 6 ++++++
|
|
||||||
2 files changed, 7 insertions(+)
|
|
||||||
|
|
||||||
--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
|
||||||
+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
|
||||||
@@ -12,6 +12,7 @@ frequencies.
|
|
||||||
"qcom,rpm-apq8064"
|
|
||||||
"qcom,rpm-msm8660"
|
|
||||||
"qcom,rpm-msm8960"
|
|
||||||
+ "qcom,rpm-ipq8064"
|
|
||||||
|
|
||||||
- reg:
|
|
||||||
Usage: required
|
|
||||||
--- a/include/dt-bindings/mfd/qcom-rpm.h
|
|
||||||
+++ b/include/dt-bindings/mfd/qcom-rpm.h
|
|
||||||
@@ -141,6 +141,12 @@
|
|
||||||
#define QCOM_RPM_SYS_FABRIC_MODE 131
|
|
||||||
#define QCOM_RPM_USB_OTG_SWITCH 132
|
|
||||||
#define QCOM_RPM_VDDMIN_GPIO 133
|
|
||||||
+#define QCOM_RPM_NSS_FABRIC_0_CLK 134
|
|
||||||
+#define QCOM_RPM_NSS_FABRIC_1_CLK 135
|
|
||||||
+#define QCOM_RPM_SMB208_S1a 136
|
|
||||||
+#define QCOM_RPM_SMB208_S1b 137
|
|
||||||
+#define QCOM_RPM_SMB208_S2a 138
|
|
||||||
+#define QCOM_RPM_SMB208_S2b 139
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constants used to select force mode for regulators.
|
|
|
@ -1,58 +0,0 @@
|
||||||
From 0f5bb5b5de3b18877373f746bdb85d8ea0efeedf Mon Sep 17 00:00:00 2001
|
|
||||||
From: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Date: Thu, 20 Nov 2014 13:41:25 -0600
|
|
||||||
Subject: [PATCH] regulator: rpm: add support for RPM-controller SMB208
|
|
||||||
|
|
||||||
The IPQ8064 reference boards make use of SMB208 regulators which are
|
|
||||||
controlled by RPM. Implement support for these regulators in the RPM
|
|
||||||
regulator driver.
|
|
||||||
|
|
||||||
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
|
|
||||||
Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
|
||||||
---
|
|
||||||
drivers/regulator/qcom_rpm-regulator.c | 19 +++++++++++++++++++
|
|
||||||
1 file changed, 19 insertions(+)
|
|
||||||
|
|
||||||
--- a/drivers/regulator/qcom_rpm-regulator.c
|
|
||||||
+++ b/drivers/regulator/qcom_rpm-regulator.c
|
|
||||||
@@ -183,6 +183,13 @@ static const struct regulator_linear_ran
|
|
||||||
REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000),
|
|
||||||
};
|
|
||||||
|
|
||||||
+static const struct regulator_linear_range smb208_ranges[] = {
|
|
||||||
+ REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500),
|
|
||||||
+ REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500),
|
|
||||||
+ REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000),
|
|
||||||
+ REGULATOR_LINEAR_RANGE(3100000, 154, 234, 25000),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static const struct regulator_linear_range ncp_ranges[] = {
|
|
||||||
REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000),
|
|
||||||
};
|
|
||||||
@@ -559,6 +566,16 @@ static const struct qcom_rpm_reg pm8921_
|
|
||||||
.parts = &rpm8960_switch_parts,
|
|
||||||
};
|
|
||||||
|
|
||||||
+static const struct qcom_rpm_reg smb208_smps = {
|
|
||||||
+ .desc.linear_ranges = smb208_ranges,
|
|
||||||
+ .desc.n_linear_ranges = ARRAY_SIZE(smb208_ranges),
|
|
||||||
+ .desc.n_voltages = 235,
|
|
||||||
+ .desc.ops = &uV_ops,
|
|
||||||
+ .parts = &rpm8960_smps_parts,
|
|
||||||
+ .supports_force_mode_auto = false,
|
|
||||||
+ .supports_force_mode_bypass = false,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static const struct of_device_id rpm_of_match[] = {
|
|
||||||
{ .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo },
|
|
||||||
{ .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo },
|
|
||||||
@@ -578,6 +595,8 @@ static const struct of_device_id rpm_of_
|
|
||||||
{ .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps },
|
|
||||||
{ .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp },
|
|
||||||
{ .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch },
|
|
||||||
+
|
|
||||||
+ { .compatible = "qcom,rpm-smb208", .data = &smb208_smps },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, rpm_of_match);
|
|
|
@ -1,25 +0,0 @@
|
||||||
From 803926825fa4db007437f76654e3e63bafb9b906 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
Date: Wed, 26 Nov 2014 13:51:01 -0800
|
|
||||||
Subject: [PATCH] regulator: qcom-rpm: Add missing state flag in call to RPM
|
|
||||||
|
|
||||||
This adds the missing state parameter to the call down to the RPM. This
|
|
||||||
is currently hard coded to the active state, as that's all we're
|
|
||||||
supporting at this moment.
|
|
||||||
|
|
||||||
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
|
|
||||||
Signed-off-by: Lee Jones <lee.jones@linaro.org>
|
|
||||||
---
|
|
||||||
drivers/regulator/qcom_rpm-regulator.c | 1 +
|
|
||||||
1 file changed, 1 insertion(+)
|
|
||||||
|
|
||||||
--- a/drivers/regulator/qcom_rpm-regulator.c
|
|
||||||
+++ b/drivers/regulator/qcom_rpm-regulator.c
|
|
||||||
@@ -205,6 +205,7 @@ static int rpm_reg_write(struct qcom_rpm
|
|
||||||
vreg->val[req->word] |= value << req->shift;
|
|
||||||
|
|
||||||
return qcom_rpm_write(vreg->rpm,
|
|
||||||
+ QCOM_RPM_ACTIVE_STATE,
|
|
||||||
vreg->resource,
|
|
||||||
vreg->val,
|
|
||||||
vreg->parts->request_len);
|
|
|
@ -1,87 +0,0 @@
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -2,6 +2,7 @@
|
|
||||||
|
|
||||||
#include "skeleton.dtsi"
|
|
||||||
#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
|
|
||||||
+#include <dt-bindings/mfd/qcom-rpm.h>
|
|
||||||
#include <dt-bindings/soc/qcom,gsbi.h>
|
|
||||||
#include <dt-bindings/reset/qcom,gcc-ipq806x.h>
|
|
||||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
||||||
@@ -76,6 +77,63 @@
|
|
||||||
ranges;
|
|
||||||
compatible = "simple-bus";
|
|
||||||
|
|
||||||
+ rpm@108000 {
|
|
||||||
+ compatible = "qcom,rpm-ipq8064";
|
|
||||||
+ reg = <0x108000 0x1000>;
|
|
||||||
+ qcom,ipc = <&l2cc 0x8 2>;
|
|
||||||
+
|
|
||||||
+ interrupts = <0 19 0>,
|
|
||||||
+ <0 21 0>,
|
|
||||||
+ <0 22 0>;
|
|
||||||
+ interrupt-names = "ack",
|
|
||||||
+ "err",
|
|
||||||
+ "wakeup";
|
|
||||||
+
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <0>;
|
|
||||||
+
|
|
||||||
+ smb208_s1a: smb208-s1a {
|
|
||||||
+ compatible = "qcom,rpm-smb208";
|
|
||||||
+ reg = <QCOM_RPM_SMB208_S1a>;
|
|
||||||
+
|
|
||||||
+ regulator-min-microvolt = <1050000>;
|
|
||||||
+ regulator-max-microvolt = <1150000>;
|
|
||||||
+
|
|
||||||
+ qcom,switch-mode-frequency = <1200000>;
|
|
||||||
+
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ smb208_s1b: smb208-s1b {
|
|
||||||
+ compatible = "qcom,rpm-smb208";
|
|
||||||
+ reg = <QCOM_RPM_SMB208_S1b>;
|
|
||||||
+
|
|
||||||
+ regulator-min-microvolt = <1050000>;
|
|
||||||
+ regulator-max-microvolt = <1150000>;
|
|
||||||
+
|
|
||||||
+ qcom,switch-mode-frequency = <1200000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ smb208_s2a: smb208-s2a {
|
|
||||||
+ compatible = "qcom,rpm-smb208";
|
|
||||||
+ reg = <QCOM_RPM_SMB208_S2a>;
|
|
||||||
+
|
|
||||||
+ regulator-min-microvolt = < 800000>;
|
|
||||||
+ regulator-max-microvolt = <1250000>;
|
|
||||||
+
|
|
||||||
+ qcom,switch-mode-frequency = <1200000>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ smb208_s2b: smb208-s2b {
|
|
||||||
+ compatible = "qcom,rpm-smb208";
|
|
||||||
+ reg = <QCOM_RPM_SMB208_S2b>;
|
|
||||||
+
|
|
||||||
+ regulator-min-microvolt = < 800000>;
|
|
||||||
+ regulator-max-microvolt = <1250000>;
|
|
||||||
+
|
|
||||||
+ qcom,switch-mode-frequency = <1200000>;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
qcom_pinmux: pinmux@800000 {
|
|
||||||
compatible = "qcom,ipq8064-pinctrl";
|
|
||||||
reg = <0x800000 0x4000>;
|
|
||||||
@@ -120,6 +178,12 @@
|
|
||||||
reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
|
|
||||||
};
|
|
||||||
|
|
||||||
+ l2cc: clock-controller@2011000 {
|
|
||||||
+ compatible = "qcom,kpss-gcc", "syscon";
|
|
||||||
+ reg = <0x2011000 0x1000>;
|
|
||||||
+ clock-output-names = "acpu_l2_aux";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
saw0: regulator@2089000 {
|
|
||||||
compatible = "qcom,saw2";
|
|
||||||
reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
|
|
|
@ -1,55 +0,0 @@
|
||||||
From 6793b3cd5da817c4be218bd8632f07cf4d2b0d26 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Hans de Goede <hdegoede@redhat.com>
|
|
||||||
Date: Wed, 19 Nov 2014 14:48:59 +0100
|
|
||||||
Subject: [PATCH] clk_mux: Fix set_parent doing the wrong thing when INDEX_BIT
|
|
||||||
&& index >= 3
|
|
||||||
|
|
||||||
If CLK_MUX_INDEX_BIT is set, then each bit turns on / off a single parent,
|
|
||||||
so theoretically multiple parents could be enabled at the same time, but in
|
|
||||||
practice only one bit should ever be 1. So to select parent 0, set
|
|
||||||
the register (*) to 0x01, to select parent 1 set it 0x02, parent 2, 0x04,
|
|
||||||
parent 3, 0x08, etc.
|
|
||||||
|
|
||||||
But the current code does:
|
|
||||||
|
|
||||||
if (mux->flags & CLK_MUX_INDEX_BIT)
|
|
||||||
index = (1 << ffs(index));
|
|
||||||
|
|
||||||
Which means that:
|
|
||||||
|
|
||||||
For an input index of 0, ffs returns 0, so we set the register
|
|
||||||
to 0x01, ok.
|
|
||||||
|
|
||||||
For an input index of 1, ffs returns 1, so we set the register
|
|
||||||
to 0x02, ok.
|
|
||||||
|
|
||||||
For an input index of 2, ffs returns 2, so we set the register
|
|
||||||
to 0x04, ok.
|
|
||||||
|
|
||||||
For an input index of 3, ffs returns 1, so we set the register
|
|
||||||
to 0x02, not good!
|
|
||||||
|
|
||||||
The code should simply be:
|
|
||||||
|
|
||||||
if (mux->flags & CLK_MUX_INDEX_BIT)
|
|
||||||
index = 1 << index;
|
|
||||||
|
|
||||||
Which always does the right thing, this commit fixes this.
|
|
||||||
|
|
||||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
||||||
Signed-off-by: Michael Turquette <mturquette@linaro.org>
|
|
||||||
---
|
|
||||||
drivers/clk/clk-mux.c | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
--- a/drivers/clk/clk-mux.c
|
|
||||||
+++ b/drivers/clk/clk-mux.c
|
|
||||||
@@ -77,7 +77,7 @@ static int clk_mux_set_parent(struct clk
|
|
||||||
|
|
||||||
else {
|
|
||||||
if (mux->flags & CLK_MUX_INDEX_BIT)
|
|
||||||
- index = (1 << ffs(index));
|
|
||||||
+ index = 1 << index;
|
|
||||||
|
|
||||||
if (mux->flags & CLK_MUX_INDEX_ONE)
|
|
||||||
index++;
|
|
|
@ -1,120 +0,0 @@
|
||||||
From 15a02c1f6dd7c2bb150c61d00ffb33f584ff2288 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Date: Mon, 19 Jan 2015 18:05:28 -0800
|
|
||||||
Subject: [PATCH] clk: Add __clk_mux_determine_rate_closest
|
|
||||||
|
|
||||||
Some clock drivers want to find the closest rate on the input of
|
|
||||||
a mux instead of a rate that's less than or equal to the desired
|
|
||||||
rate. Add a generic mux function to support this.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Tested-by: Kenneth Westfield <kwestfie@codeaurora.org>
|
|
||||||
Signed-off-by: Michael Turquette <mturquette@linaro.org>
|
|
||||||
---
|
|
||||||
drivers/clk/clk.c | 47 +++++++++++++++++++++++++++++++++++---------
|
|
||||||
include/linux/clk-provider.h | 8 +++++++-
|
|
||||||
2 files changed, 45 insertions(+), 10 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/clk/clk.c
|
|
||||||
+++ b/drivers/clk/clk.c
|
|
||||||
@@ -695,14 +695,20 @@ struct clk *__clk_lookup(const char *nam
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
-/*
|
|
||||||
- * Helper for finding best parent to provide a given frequency. This can be used
|
|
||||||
- * directly as a determine_rate callback (e.g. for a mux), or from a more
|
|
||||||
- * complex clock that may combine a mux with other operations.
|
|
||||||
- */
|
|
||||||
-long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
- unsigned long *best_parent_rate,
|
|
||||||
- struct clk **best_parent_p)
|
|
||||||
+static bool mux_is_better_rate(unsigned long rate, unsigned long now,
|
|
||||||
+ unsigned long best, unsigned long flags)
|
|
||||||
+{
|
|
||||||
+ if (flags & CLK_MUX_ROUND_CLOSEST)
|
|
||||||
+ return abs(now - rate) < abs(best - rate);
|
|
||||||
+
|
|
||||||
+ return now <= rate && now > best;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static long
|
|
||||||
+clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long *best_parent_rate,
|
|
||||||
+ struct clk **best_parent_p,
|
|
||||||
+ unsigned long flags)
|
|
||||||
{
|
|
||||||
struct clk *clk = hw->clk, *parent, *best_parent = NULL;
|
|
||||||
int i, num_parents;
|
|
||||||
@@ -730,7 +736,7 @@ long __clk_mux_determine_rate(struct clk
|
|
||||||
parent_rate = __clk_round_rate(parent, rate);
|
|
||||||
else
|
|
||||||
parent_rate = __clk_get_rate(parent);
|
|
||||||
- if (parent_rate <= rate && parent_rate > best) {
|
|
||||||
+ if (mux_is_better_rate(rate, parent_rate, best, flags)) {
|
|
||||||
best_parent = parent;
|
|
||||||
best = parent_rate;
|
|
||||||
}
|
|
||||||
@@ -743,8 +749,31 @@ out:
|
|
||||||
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Helper for finding best parent to provide a given frequency. This can be used
|
|
||||||
+ * directly as a determine_rate callback (e.g. for a mux), or from a more
|
|
||||||
+ * complex clock that may combine a mux with other operations.
|
|
||||||
+ */
|
|
||||||
+long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long *best_parent_rate,
|
|
||||||
+ struct clk **best_parent_p)
|
|
||||||
+{
|
|
||||||
+ return clk_mux_determine_rate_flags(hw, rate, best_parent_rate,
|
|
||||||
+ best_parent_p, 0);
|
|
||||||
+}
|
|
||||||
EXPORT_SYMBOL_GPL(__clk_mux_determine_rate);
|
|
||||||
|
|
||||||
+long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long *best_parent_rate,
|
|
||||||
+ struct clk **best_parent_p)
|
|
||||||
+{
|
|
||||||
+ return clk_mux_determine_rate_flags(hw, rate, best_parent_rate,
|
|
||||||
+ best_parent_p,
|
|
||||||
+ CLK_MUX_ROUND_CLOSEST);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
|
|
||||||
+
|
|
||||||
/*** clk api ***/
|
|
||||||
|
|
||||||
void __clk_unprepare(struct clk *clk)
|
|
||||||
--- a/include/linux/clk-provider.h
|
|
||||||
+++ b/include/linux/clk-provider.h
|
|
||||||
@@ -382,6 +382,8 @@ struct clk *clk_register_divider_table(s
|
|
||||||
* register, and mask of mux bits are in higher 16-bit of this register.
|
|
||||||
* While setting the mux bits, higher 16-bit should also be updated to
|
|
||||||
* indicate changing mux bits.
|
|
||||||
+ * CLK_MUX_ROUND_CLOSEST - Use the parent rate that is closest to the desired
|
|
||||||
+ * frequency.
|
|
||||||
*/
|
|
||||||
struct clk_mux {
|
|
||||||
struct clk_hw hw;
|
|
||||||
@@ -396,7 +398,8 @@ struct clk_mux {
|
|
||||||
#define CLK_MUX_INDEX_ONE BIT(0)
|
|
||||||
#define CLK_MUX_INDEX_BIT BIT(1)
|
|
||||||
#define CLK_MUX_HIWORD_MASK BIT(2)
|
|
||||||
-#define CLK_MUX_READ_ONLY BIT(3) /* mux setting cannot be changed */
|
|
||||||
+#define CLK_MUX_READ_ONLY BIT(3) /* mux can't be changed */
|
|
||||||
+#define CLK_MUX_ROUND_CLOSEST BIT(4)
|
|
||||||
|
|
||||||
extern const struct clk_ops clk_mux_ops;
|
|
||||||
extern const struct clk_ops clk_mux_ro_ops;
|
|
||||||
@@ -554,6 +557,9 @@ struct clk *__clk_lookup(const char *nam
|
|
||||||
long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
unsigned long *best_parent_rate,
|
|
||||||
struct clk **best_parent_p);
|
|
||||||
+long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long *best_parent_rate,
|
|
||||||
+ struct clk **best_parent_p);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME clock api without lock protection
|
|
|
@ -1,115 +0,0 @@
|
||||||
From 4e3c021fb995bcbb5d1f814d00584cb80eb904a8 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
|
||||||
Date: Mon, 5 Jan 2015 10:52:40 +0100
|
|
||||||
Subject: [PATCH] clk: Add clk_unregister_{divider, gate, mux} to close memory
|
|
||||||
leak
|
|
||||||
|
|
||||||
The common clk_register_{divider,gate,mux} functions allocated memory
|
|
||||||
for internal data which wasn't freed anywhere. Drivers using these
|
|
||||||
helpers could only unregister clocks but the memory would still leak.
|
|
||||||
|
|
||||||
Add corresponding unregister functions which will release all resources.
|
|
||||||
|
|
||||||
Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
|
||||||
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Signed-off-by: Michael Turquette <mturquette@linaro.org>
|
|
||||||
---
|
|
||||||
drivers/clk/clk-divider.c | 16 ++++++++++++++++
|
|
||||||
drivers/clk/clk-gate.c | 16 ++++++++++++++++
|
|
||||||
drivers/clk/clk-mux.c | 16 ++++++++++++++++
|
|
||||||
include/linux/clk-provider.h | 4 ++++
|
|
||||||
4 files changed, 52 insertions(+)
|
|
||||||
|
|
||||||
--- a/drivers/clk/clk-divider.c
|
|
||||||
+++ b/drivers/clk/clk-divider.c
|
|
||||||
@@ -461,3 +461,19 @@ struct clk *clk_register_divider_table(s
|
|
||||||
width, clk_divider_flags, table, lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(clk_register_divider_table);
|
|
||||||
+
|
|
||||||
+void clk_unregister_divider(struct clk *clk)
|
|
||||||
+{
|
|
||||||
+ struct clk_divider *div;
|
|
||||||
+ struct clk_hw *hw;
|
|
||||||
+
|
|
||||||
+ hw = __clk_get_hw(clk);
|
|
||||||
+ if (!hw)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ div = to_clk_divider(hw);
|
|
||||||
+
|
|
||||||
+ clk_unregister(clk);
|
|
||||||
+ kfree(div);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL_GPL(clk_unregister_divider);
|
|
||||||
--- a/drivers/clk/clk-gate.c
|
|
||||||
+++ b/drivers/clk/clk-gate.c
|
|
||||||
@@ -162,3 +162,19 @@ struct clk *clk_register_gate(struct dev
|
|
||||||
return clk;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(clk_register_gate);
|
|
||||||
+
|
|
||||||
+void clk_unregister_gate(struct clk *clk)
|
|
||||||
+{
|
|
||||||
+ struct clk_gate *gate;
|
|
||||||
+ struct clk_hw *hw;
|
|
||||||
+
|
|
||||||
+ hw = __clk_get_hw(clk);
|
|
||||||
+ if (!hw)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ gate = to_clk_gate(hw);
|
|
||||||
+
|
|
||||||
+ clk_unregister(clk);
|
|
||||||
+ kfree(gate);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL_GPL(clk_unregister_gate);
|
|
||||||
--- a/drivers/clk/clk-mux.c
|
|
||||||
+++ b/drivers/clk/clk-mux.c
|
|
||||||
@@ -177,3 +177,19 @@ struct clk *clk_register_mux(struct devi
|
|
||||||
NULL, lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(clk_register_mux);
|
|
||||||
+
|
|
||||||
+void clk_unregister_mux(struct clk *clk)
|
|
||||||
+{
|
|
||||||
+ struct clk_mux *mux;
|
|
||||||
+ struct clk_hw *hw;
|
|
||||||
+
|
|
||||||
+ hw = __clk_get_hw(clk);
|
|
||||||
+ if (!hw)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ mux = to_clk_mux(hw);
|
|
||||||
+
|
|
||||||
+ clk_unregister(clk);
|
|
||||||
+ kfree(mux);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL_GPL(clk_unregister_mux);
|
|
||||||
--- a/include/linux/clk-provider.h
|
|
||||||
+++ b/include/linux/clk-provider.h
|
|
||||||
@@ -294,6 +294,7 @@ struct clk *clk_register_gate(struct dev
|
|
||||||
const char *parent_name, unsigned long flags,
|
|
||||||
void __iomem *reg, u8 bit_idx,
|
|
||||||
u8 clk_gate_flags, spinlock_t *lock);
|
|
||||||
+void clk_unregister_gate(struct clk *clk);
|
|
||||||
|
|
||||||
struct clk_div_table {
|
|
||||||
unsigned int val;
|
|
||||||
@@ -361,6 +362,7 @@ struct clk *clk_register_divider_table(s
|
|
||||||
void __iomem *reg, u8 shift, u8 width,
|
|
||||||
u8 clk_divider_flags, const struct clk_div_table *table,
|
|
||||||
spinlock_t *lock);
|
|
||||||
+void clk_unregister_divider(struct clk *clk);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct clk_mux - multiplexer clock
|
|
||||||
@@ -414,6 +416,8 @@ struct clk *clk_register_mux_table(struc
|
|
||||||
void __iomem *reg, u8 shift, u32 mask,
|
|
||||||
u8 clk_mux_flags, u32 *table, spinlock_t *lock);
|
|
||||||
|
|
||||||
+void clk_unregister_mux(struct clk *clk);
|
|
||||||
+
|
|
||||||
void of_fixed_factor_clk_setup(struct device_node *node);
|
|
||||||
|
|
||||||
/**
|
|
|
@ -1,144 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063051
|
|
||||||
Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>,
|
|
||||||
Mark Rutland <mark.rutland@arm.com>, Russell King <linux@arm.linux.org.uk>,
|
|
||||||
Courtney Cavin <courtney.cavin@sonymobile.com>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:20 -0700
|
|
||||||
|
|
||||||
Krait CPUs have a handful of L2 cache controller registers that
|
|
||||||
live behind a cp15 based indirection register. First you program
|
|
||||||
the indirection register (l2cpselr) to point the L2 'window'
|
|
||||||
register (l2cpdr) at what you want to read/write. Then you
|
|
||||||
read/write the 'window' register to do what you want. The
|
|
||||||
l2cpselr register is not banked per-cpu so we must lock around
|
|
||||||
accesses to it to prevent other CPUs from re-pointing l2cpdr
|
|
||||||
underneath us.
|
|
||||||
|
|
||||||
Cc: Mark Rutland <mark.rutland@arm.com>
|
|
||||||
Cc: Russell King <linux@arm.linux.org.uk>
|
|
||||||
Cc: Courtney Cavin <courtney.cavin@sonymobile.com>
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
arch/arm/common/Kconfig | 3 ++
|
|
||||||
arch/arm/common/Makefile | 1 +
|
|
||||||
arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++
|
|
||||||
arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
|
|
||||||
4 files changed, 82 insertions(+)
|
|
||||||
create mode 100644 arch/arm/common/krait-l2-accessors.c
|
|
||||||
create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
|
|
||||||
|
|
||||||
--- a/arch/arm/common/Kconfig
|
|
||||||
+++ b/arch/arm/common/Kconfig
|
|
||||||
@@ -9,6 +9,9 @@ config DMABOUNCE
|
|
||||||
bool
|
|
||||||
select ZONE_DMA
|
|
||||||
|
|
||||||
+config KRAIT_L2_ACCESSORS
|
|
||||||
+ bool
|
|
||||||
+
|
|
||||||
config SHARP_LOCOMO
|
|
||||||
bool
|
|
||||||
|
|
||||||
--- a/arch/arm/common/Makefile
|
|
||||||
+++ b/arch/arm/common/Makefile
|
|
||||||
@@ -7,6 +7,7 @@ obj-y += firmware.o
|
|
||||||
obj-$(CONFIG_ICST) += icst.o
|
|
||||||
obj-$(CONFIG_SA1111) += sa1111.o
|
|
||||||
obj-$(CONFIG_DMABOUNCE) += dmabounce.o
|
|
||||||
+obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
|
|
||||||
obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
|
|
||||||
obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
|
|
||||||
obj-$(CONFIG_SHARP_SCOOP) += scoop.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/arch/arm/common/krait-l2-accessors.c
|
|
||||||
@@ -0,0 +1,58 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
+#include <linux/export.h>
|
|
||||||
+
|
|
||||||
+#include <asm/barrier.h>
|
|
||||||
+#include <asm/krait-l2-accessors.h>
|
|
||||||
+
|
|
||||||
+static DEFINE_RAW_SPINLOCK(krait_l2_lock);
|
|
||||||
+
|
|
||||||
+void krait_set_l2_indirect_reg(u32 addr, u32 val)
|
|
||||||
+{
|
|
||||||
+ unsigned long flags;
|
|
||||||
+
|
|
||||||
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
|
|
||||||
+ /*
|
|
||||||
+ * Select the L2 window by poking l2cpselr, then write to the window
|
|
||||||
+ * via l2cpdr.
|
|
||||||
+ */
|
|
||||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
|
|
||||||
+ isb();
|
|
||||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
|
|
||||||
+ isb();
|
|
||||||
+
|
|
||||||
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(krait_set_l2_indirect_reg);
|
|
||||||
+
|
|
||||||
+u32 krait_get_l2_indirect_reg(u32 addr)
|
|
||||||
+{
|
|
||||||
+ u32 val;
|
|
||||||
+ unsigned long flags;
|
|
||||||
+
|
|
||||||
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
|
|
||||||
+ /*
|
|
||||||
+ * Select the L2 window by poking l2cpselr, then read from the window
|
|
||||||
+ * via l2cpdr.
|
|
||||||
+ */
|
|
||||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
|
|
||||||
+ isb();
|
|
||||||
+ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
|
|
||||||
+
|
|
||||||
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
|
|
||||||
+
|
|
||||||
+ return val;
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(krait_get_l2_indirect_reg);
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/arch/arm/include/asm/krait-l2-accessors.h
|
|
||||||
@@ -0,0 +1,20 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
|
|
||||||
+#define __ASMARM_KRAIT_L2_ACCESSORS_H
|
|
||||||
+
|
|
||||||
+extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
|
|
||||||
+extern u32 krait_get_l2_indirect_reg(u32 addr);
|
|
||||||
+
|
|
||||||
+#endif
|
|
|
@ -1,192 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,02/13] clk: mux: Split out register accessors for reuse
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063111
|
|
||||||
Message-Id: <1426920332-9340-3-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:21 -0700
|
|
||||||
|
|
||||||
We want to reuse the logic in clk-mux.c for other clock drivers
|
|
||||||
that don't use readl as register accessors. Fortunately, there
|
|
||||||
really isn't much to the mux code besides the table indirection
|
|
||||||
and quirk flags if you assume any bit shifting and masking has
|
|
||||||
been done already. Pull that logic out into reusable functions
|
|
||||||
that operate on an optional table and some flags so that other
|
|
||||||
drivers can use the same logic.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
drivers/clk/clk-mux.c | 76 +++++++++++++++++++++++++++-----------------
|
|
||||||
include/linux/clk-provider.h | 9 ++++--
|
|
||||||
2 files changed, 54 insertions(+), 31 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/clk/clk-mux.c
|
|
||||||
+++ b/drivers/clk/clk-mux.c
|
|
||||||
@@ -29,35 +29,24 @@
|
|
||||||
|
|
||||||
#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
|
|
||||||
|
|
||||||
-static u8 clk_mux_get_parent(struct clk_hw *hw)
|
|
||||||
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
|
|
||||||
+ unsigned int *table, unsigned long flags)
|
|
||||||
{
|
|
||||||
- struct clk_mux *mux = to_clk_mux(hw);
|
|
||||||
int num_parents = __clk_get_num_parents(hw->clk);
|
|
||||||
- u32 val;
|
|
||||||
|
|
||||||
- /*
|
|
||||||
- * FIXME need a mux-specific flag to determine if val is bitwise or numeric
|
|
||||||
- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
|
|
||||||
- * to 0x7 (index starts at one)
|
|
||||||
- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
|
|
||||||
- * val = 0x4 really means "bit 2, index starts at bit 0"
|
|
||||||
- */
|
|
||||||
- val = clk_readl(mux->reg) >> mux->shift;
|
|
||||||
- val &= mux->mask;
|
|
||||||
-
|
|
||||||
- if (mux->table) {
|
|
||||||
+ if (table) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_parents; i++)
|
|
||||||
- if (mux->table[i] == val)
|
|
||||||
+ if (table[i] == val)
|
|
||||||
return i;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (val && (mux->flags & CLK_MUX_INDEX_BIT))
|
|
||||||
+ if (val && (flags & CLK_MUX_INDEX_BIT))
|
|
||||||
val = ffs(val) - 1;
|
|
||||||
|
|
||||||
- if (val && (mux->flags & CLK_MUX_INDEX_ONE))
|
|
||||||
+ if (val && (flags & CLK_MUX_INDEX_ONE))
|
|
||||||
val--;
|
|
||||||
|
|
||||||
if (val >= num_parents)
|
|
||||||
@@ -65,24 +54,53 @@ static u8 clk_mux_get_parent(struct clk_
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
|
|
||||||
|
|
||||||
-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
|
||||||
+static u8 _clk_mux_get_parent(struct clk_hw *hw)
|
|
||||||
{
|
|
||||||
struct clk_mux *mux = to_clk_mux(hw);
|
|
||||||
u32 val;
|
|
||||||
- unsigned long flags = 0;
|
|
||||||
|
|
||||||
- if (mux->table)
|
|
||||||
- index = mux->table[index];
|
|
||||||
+ /*
|
|
||||||
+ * FIXME need a mux-specific flag to determine if val is bitwise or numeric
|
|
||||||
+ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
|
|
||||||
+ * to 0x7 (index starts at one)
|
|
||||||
+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
|
|
||||||
+ * val = 0x4 really means "bit 2, index starts at bit 0"
|
|
||||||
+ */
|
|
||||||
+ val = clk_readl(mux->reg) >> mux->shift;
|
|
||||||
+ val &= mux->mask;
|
|
||||||
+
|
|
||||||
+ return clk_mux_get_parent(hw, val, mux->table, mux->flags);
|
|
||||||
+}
|
|
||||||
|
|
||||||
- else {
|
|
||||||
- if (mux->flags & CLK_MUX_INDEX_BIT)
|
|
||||||
- index = 1 << index;
|
|
||||||
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
|
|
||||||
+ unsigned long flags)
|
|
||||||
+{
|
|
||||||
+ unsigned int val = index;
|
|
||||||
|
|
||||||
- if (mux->flags & CLK_MUX_INDEX_ONE)
|
|
||||||
- index++;
|
|
||||||
+ if (table) {
|
|
||||||
+ val = table[val];
|
|
||||||
+ } else {
|
|
||||||
+ if (flags & CLK_MUX_INDEX_BIT)
|
|
||||||
+ val = 1 << index;
|
|
||||||
+
|
|
||||||
+ if (flags & CLK_MUX_INDEX_ONE)
|
|
||||||
+ val++;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ return val;
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL_GPL(clk_mux_reindex);
|
|
||||||
+
|
|
||||||
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
|
||||||
+{
|
|
||||||
+ struct clk_mux *mux = to_clk_mux(hw);
|
|
||||||
+ u32 val;
|
|
||||||
+ unsigned long flags = 0;
|
|
||||||
+
|
|
||||||
+ index = clk_mux_reindex(index, mux->table, mux->flags);
|
|
||||||
+
|
|
||||||
if (mux->lock)
|
|
||||||
spin_lock_irqsave(mux->lock, flags);
|
|
||||||
|
|
||||||
@@ -102,21 +120,21 @@ static int clk_mux_set_parent(struct clk
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct clk_ops clk_mux_ops = {
|
|
||||||
- .get_parent = clk_mux_get_parent,
|
|
||||||
+ .get_parent = _clk_mux_get_parent,
|
|
||||||
.set_parent = clk_mux_set_parent,
|
|
||||||
.determine_rate = __clk_mux_determine_rate,
|
|
||||||
};
|
|
||||||
EXPORT_SYMBOL_GPL(clk_mux_ops);
|
|
||||||
|
|
||||||
const struct clk_ops clk_mux_ro_ops = {
|
|
||||||
- .get_parent = clk_mux_get_parent,
|
|
||||||
+ .get_parent = _clk_mux_get_parent,
|
|
||||||
};
|
|
||||||
EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
|
|
||||||
|
|
||||||
struct clk *clk_register_mux_table(struct device *dev, const char *name,
|
|
||||||
const char **parent_names, u8 num_parents, unsigned long flags,
|
|
||||||
void __iomem *reg, u8 shift, u32 mask,
|
|
||||||
- u8 clk_mux_flags, u32 *table, spinlock_t *lock)
|
|
||||||
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
|
|
||||||
{
|
|
||||||
struct clk_mux *mux;
|
|
||||||
struct clk *clk;
|
|
||||||
--- a/include/linux/clk-provider.h
|
|
||||||
+++ b/include/linux/clk-provider.h
|
|
||||||
@@ -390,7 +390,7 @@ void clk_unregister_divider(struct clk *
|
|
||||||
struct clk_mux {
|
|
||||||
struct clk_hw hw;
|
|
||||||
void __iomem *reg;
|
|
||||||
- u32 *table;
|
|
||||||
+ unsigned int *table;
|
|
||||||
u32 mask;
|
|
||||||
u8 shift;
|
|
||||||
u8 flags;
|
|
||||||
@@ -406,6 +406,11 @@ struct clk_mux {
|
|
||||||
extern const struct clk_ops clk_mux_ops;
|
|
||||||
extern const struct clk_ops clk_mux_ro_ops;
|
|
||||||
|
|
||||||
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
|
|
||||||
+ unsigned int *table, unsigned long flags);
|
|
||||||
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
|
|
||||||
+ unsigned long flags);
|
|
||||||
+
|
|
||||||
struct clk *clk_register_mux(struct device *dev, const char *name,
|
|
||||||
const char **parent_names, u8 num_parents, unsigned long flags,
|
|
||||||
void __iomem *reg, u8 shift, u8 width,
|
|
||||||
@@ -414,7 +419,7 @@ struct clk *clk_register_mux(struct devi
|
|
||||||
struct clk *clk_register_mux_table(struct device *dev, const char *name,
|
|
||||||
const char **parent_names, u8 num_parents, unsigned long flags,
|
|
||||||
void __iomem *reg, u8 shift, u32 mask,
|
|
||||||
- u8 clk_mux_flags, u32 *table, spinlock_t *lock);
|
|
||||||
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
|
|
||||||
|
|
||||||
void clk_unregister_mux(struct clk *clk);
|
|
||||||
|
|
|
@ -1,129 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3, 03/13] clk: Avoid sending high rates to downstream clocks during
|
|
||||||
set_rate
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063271
|
|
||||||
Message-Id: <1426920332-9340-4-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:22 -0700
|
|
||||||
|
|
||||||
If a clock is on and we call clk_set_rate() on it we may get into
|
|
||||||
a situation where the clock temporarily increases in rate
|
|
||||||
dramatically while we walk the tree and call .set_rate() ops. For
|
|
||||||
example, consider a case where a PLL feeds into a divider.
|
|
||||||
Initially the divider is set to divide by 1 and the PLL is
|
|
||||||
running fairly slow (100MHz). The downstream consumer of the
|
|
||||||
divider output can only handle rates =< 400 MHz, but the divider
|
|
||||||
can only choose between divisors of 1 and 4.
|
|
||||||
|
|
||||||
+-----+ +----------------+
|
|
||||||
| PLL |-->| div 1 or div 4 |---> consumer device
|
|
||||||
+-----+ +----------------+
|
|
||||||
|
|
||||||
To achieve a rate of 400MHz on the output of the divider, we
|
|
||||||
would have to set the rate of the PLL to 1.6 GHz and then divide
|
|
||||||
it by 4. The current code would set the PLL to 1.6GHz first while
|
|
||||||
the divider is still set to 1, thus causing the downstream
|
|
||||||
consumer of the clock to receive a few clock cycles of 1.6GHz
|
|
||||||
clock (far beyond it's maximum acceptable rate). We should be
|
|
||||||
changing the divider first before increasing the PLL rate to
|
|
||||||
avoid this problem.
|
|
||||||
|
|
||||||
Therefore, set the rate of any child clocks that are increasing
|
|
||||||
in rate from their current rate so that they can increase their
|
|
||||||
dividers if necessary. We assume that there isn't such a thing as
|
|
||||||
minimum rate requirements.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
drivers/clk/clk.c | 34 ++++++++++++++++++++++------------
|
|
||||||
1 file changed, 22 insertions(+), 12 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/clk/clk.c
|
|
||||||
+++ b/drivers/clk/clk.c
|
|
||||||
@@ -1476,21 +1476,23 @@ static struct clk *clk_propagate_rate_ch
|
|
||||||
* walk down a subtree and set the new rates notifying the rate
|
|
||||||
* change on the way
|
|
||||||
*/
|
|
||||||
-static void clk_change_rate(struct clk *clk)
|
|
||||||
+static void clk_change_rate(struct clk *clk, unsigned long best_parent_rate)
|
|
||||||
{
|
|
||||||
struct clk *child;
|
|
||||||
struct hlist_node *tmp;
|
|
||||||
unsigned long old_rate;
|
|
||||||
- unsigned long best_parent_rate = 0;
|
|
||||||
bool skip_set_rate = false;
|
|
||||||
struct clk *old_parent;
|
|
||||||
|
|
||||||
- old_rate = clk->rate;
|
|
||||||
+ hlist_for_each_entry(child, &clk->children, child_node) {
|
|
||||||
+ /* Skip children who will be reparented to another clock */
|
|
||||||
+ if (child->new_parent && child->new_parent != clk)
|
|
||||||
+ continue;
|
|
||||||
+ if (child->new_rate > child->rate)
|
|
||||||
+ clk_change_rate(child, clk->new_rate);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (clk->new_parent)
|
|
||||||
- best_parent_rate = clk->new_parent->rate;
|
|
||||||
- else if (clk->parent)
|
|
||||||
- best_parent_rate = clk->parent->rate;
|
|
||||||
+ old_rate = clk->rate;
|
|
||||||
|
|
||||||
if (clk->new_parent && clk->new_parent != clk->parent) {
|
|
||||||
old_parent = __clk_set_parent_before(clk, clk->new_parent);
|
|
||||||
@@ -1510,7 +1512,7 @@ static void clk_change_rate(struct clk *
|
|
||||||
if (!skip_set_rate && clk->ops->set_rate)
|
|
||||||
clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
|
|
||||||
|
|
||||||
- clk->rate = clk_recalc(clk, best_parent_rate);
|
|
||||||
+ clk->rate = clk->new_rate;
|
|
||||||
|
|
||||||
if (clk->notifier_count && old_rate != clk->rate)
|
|
||||||
__clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
|
|
||||||
@@ -1523,12 +1525,13 @@ static void clk_change_rate(struct clk *
|
|
||||||
/* Skip children who will be reparented to another clock */
|
|
||||||
if (child->new_parent && child->new_parent != clk)
|
|
||||||
continue;
|
|
||||||
- clk_change_rate(child);
|
|
||||||
+ if (child->new_rate != child->rate)
|
|
||||||
+ clk_change_rate(child, clk->new_rate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle the new child who might not be in clk->children yet */
|
|
||||||
- if (clk->new_child)
|
|
||||||
- clk_change_rate(clk->new_child);
|
|
||||||
+ if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate)
|
|
||||||
+ clk_change_rate(clk->new_child, clk->new_rate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@@ -1556,6 +1559,7 @@ int clk_set_rate(struct clk *clk, unsign
|
|
||||||
{
|
|
||||||
struct clk *top, *fail_clk;
|
|
||||||
int ret = 0;
|
|
||||||
+ unsigned long parent_rate;
|
|
||||||
|
|
||||||
if (!clk)
|
|
||||||
return 0;
|
|
||||||
@@ -1589,8 +1593,13 @@ int clk_set_rate(struct clk *clk, unsign
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ if (top->parent)
|
|
||||||
+ parent_rate = top->parent->rate;
|
|
||||||
+ else
|
|
||||||
+ parent_rate = 0;
|
|
||||||
+
|
|
||||||
/* change the rates */
|
|
||||||
- clk_change_rate(top);
|
|
||||||
+ clk_change_rate(top, parent_rate);
|
|
||||||
|
|
||||||
out:
|
|
||||||
clk_prepare_unlock();
|
|
|
@ -1,170 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,04/13] clk: Add safe switch hook
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063211
|
|
||||||
Message-Id: <1426920332-9340-5-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:23 -0700
|
|
||||||
|
|
||||||
Sometimes clocks can't accept their parent source turning off
|
|
||||||
while the source is reprogrammed to a different rate. Most
|
|
||||||
notably CPU clocks require a way to switch away from the current
|
|
||||||
PLL they're running on, reprogram that PLL to a new rate, and
|
|
||||||
then switch back to the PLL with the new rate once they're done.
|
|
||||||
Add a hook that drivers can implement allowing them to return a
|
|
||||||
'safe parent' that they can switch their parent to while the
|
|
||||||
upstream source is reprogrammed to support this.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
This patch is good enough for Krait, but soon I'll need to
|
|
||||||
support a "safe rate" where we ask a clock what rate it needs to be running
|
|
||||||
at to be sure it's within voltage constraints. Right now safe parent
|
|
||||||
handles that problem on Krait, but on other platforms it won't work.
|
|
||||||
|
|
||||||
drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------
|
|
||||||
include/linux/clk-provider.h | 1 +
|
|
||||||
2 files changed, 54 insertions(+), 8 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/clk/clk.c
|
|
||||||
+++ b/drivers/clk/clk.c
|
|
||||||
@@ -1350,7 +1350,8 @@ out:
|
|
||||||
static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
|
|
||||||
struct clk *new_parent, u8 p_index)
|
|
||||||
{
|
|
||||||
- struct clk *child;
|
|
||||||
+ struct clk *child, *parent;
|
|
||||||
+ struct clk_hw *parent_hw;
|
|
||||||
|
|
||||||
clk->new_rate = new_rate;
|
|
||||||
clk->new_parent = new_parent;
|
|
||||||
@@ -1360,6 +1361,18 @@ static void clk_calc_subtree(struct clk
|
|
||||||
if (new_parent && new_parent != clk->parent)
|
|
||||||
new_parent->new_child = clk;
|
|
||||||
|
|
||||||
+ if (clk->ops->get_safe_parent) {
|
|
||||||
+ parent_hw = clk->ops->get_safe_parent(clk->hw);
|
|
||||||
+ if (parent_hw) {
|
|
||||||
+ parent = parent_hw->clk;
|
|
||||||
+ p_index = clk_fetch_parent_index(clk, parent);
|
|
||||||
+ clk->safe_parent_index = p_index;
|
|
||||||
+ clk->safe_parent = parent;
|
|
||||||
+ }
|
|
||||||
+ } else {
|
|
||||||
+ clk->safe_parent = NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
hlist_for_each_entry(child, &clk->children, child_node) {
|
|
||||||
child->new_rate = clk_recalc(child, new_rate);
|
|
||||||
clk_calc_subtree(child, child->new_rate, NULL, 0);
|
|
||||||
@@ -1439,17 +1452,47 @@ out:
|
|
||||||
* so that in case of an error we can walk down the whole tree again and
|
|
||||||
* abort the change.
|
|
||||||
*/
|
|
||||||
-static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
|
|
||||||
+static struct clk *clk_propagate_rate_change(struct clk *clk,
|
|
||||||
+ unsigned long event)
|
|
||||||
{
|
|
||||||
struct clk *child, *tmp_clk, *fail_clk = NULL;
|
|
||||||
+ struct clk *old_parent;
|
|
||||||
int ret = NOTIFY_DONE;
|
|
||||||
|
|
||||||
- if (clk->rate == clk->new_rate)
|
|
||||||
+ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
+ switch (event) {
|
|
||||||
+ case PRE_RATE_CHANGE:
|
|
||||||
+ if (clk->safe_parent)
|
|
||||||
+ clk->ops->set_parent(clk->hw, clk->safe_parent_index);
|
|
||||||
+ clk->old_rate = clk->rate;
|
|
||||||
+ break;
|
|
||||||
+ case POST_RATE_CHANGE:
|
|
||||||
+ if (clk->safe_parent) {
|
|
||||||
+ old_parent = __clk_set_parent_before(clk,
|
|
||||||
+ clk->new_parent);
|
|
||||||
+ if (clk->ops->set_rate_and_parent) {
|
|
||||||
+ clk->ops->set_rate_and_parent(clk->hw,
|
|
||||||
+ clk->new_rate,
|
|
||||||
+ clk->new_parent ?
|
|
||||||
+ clk->new_parent->rate : 0,
|
|
||||||
+ clk->new_parent_index);
|
|
||||||
+ } else if (clk->ops->set_parent) {
|
|
||||||
+ clk->ops->set_parent(clk->hw,
|
|
||||||
+ clk->new_parent_index);
|
|
||||||
+ }
|
|
||||||
+ __clk_set_parent_after(clk, clk->new_parent,
|
|
||||||
+ old_parent);
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (clk->notifier_count) {
|
|
||||||
- ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
|
|
||||||
- if (ret & NOTIFY_STOP_MASK)
|
|
||||||
+ if (event != POST_RATE_CHANGE || clk->old_rate != clk->rate)
|
|
||||||
+ ret = __clk_notify(clk, event, clk->old_rate,
|
|
||||||
+ clk->new_rate);
|
|
||||||
+ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
|
|
||||||
fail_clk = clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1494,7 +1537,8 @@ static void clk_change_rate(struct clk *
|
|
||||||
|
|
||||||
old_rate = clk->rate;
|
|
||||||
|
|
||||||
- if (clk->new_parent && clk->new_parent != clk->parent) {
|
|
||||||
+ if (clk->new_parent && clk->new_parent != clk->parent &&
|
|
||||||
+ !clk->safe_parent) {
|
|
||||||
old_parent = __clk_set_parent_before(clk, clk->new_parent);
|
|
||||||
|
|
||||||
if (clk->ops->set_rate_and_parent) {
|
|
||||||
@@ -1514,9 +1558,6 @@ static void clk_change_rate(struct clk *
|
|
||||||
|
|
||||||
clk->rate = clk->new_rate;
|
|
||||||
|
|
||||||
- if (clk->notifier_count && old_rate != clk->rate)
|
|
||||||
- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
|
|
||||||
-
|
|
||||||
/*
|
|
||||||
* Use safe iteration, as change_rate can actually swap parents
|
|
||||||
* for certain clock types.
|
|
||||||
@@ -1601,6 +1642,8 @@ int clk_set_rate(struct clk *clk, unsign
|
|
||||||
/* change the rates */
|
|
||||||
clk_change_rate(top, parent_rate);
|
|
||||||
|
|
||||||
+ clk_propagate_rate_change(top, POST_RATE_CHANGE);
|
|
||||||
+
|
|
||||||
out:
|
|
||||||
clk_prepare_unlock();
|
|
||||||
|
|
||||||
--- a/include/linux/clk-provider.h
|
|
||||||
+++ b/include/linux/clk-provider.h
|
|
||||||
@@ -179,6 +179,7 @@ struct clk_ops {
|
|
||||||
struct clk **best_parent_clk);
|
|
||||||
int (*set_parent)(struct clk_hw *hw, u8 index);
|
|
||||||
u8 (*get_parent)(struct clk_hw *hw);
|
|
||||||
+ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw);
|
|
||||||
int (*set_rate)(struct clk_hw *hw, unsigned long rate,
|
|
||||||
unsigned long parent_rate);
|
|
||||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
|
||||||
--- a/include/linux/clk-private.h
|
|
||||||
+++ b/include/linux/clk-private.h
|
|
||||||
@@ -38,8 +38,11 @@ struct clk {
|
|
||||||
struct clk **parents;
|
|
||||||
u8 num_parents;
|
|
||||||
u8 new_parent_index;
|
|
||||||
+ u8 safe_parent_index;
|
|
||||||
unsigned long rate;
|
|
||||||
+ unsigned long old_rate;
|
|
||||||
unsigned long new_rate;
|
|
||||||
+ struct clk *safe_parent;
|
|
||||||
struct clk *new_parent;
|
|
||||||
struct clk *new_child;
|
|
||||||
unsigned long flags;
|
|
|
@ -1,351 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063261
|
|
||||||
Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:24 -0700
|
|
||||||
|
|
||||||
HFPLLs are the main frequency source for Krait CPU clocks. Add
|
|
||||||
support for changing the rate of these PLLs.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
I'd really like to get rid of __clk_hfpll_init_once() if possible...
|
|
||||||
|
|
||||||
drivers/clk/qcom/Makefile | 1 +
|
|
||||||
drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
drivers/clk/qcom/clk-hfpll.h | 54 +++++++++
|
|
||||||
3 files changed, 308 insertions(+)
|
|
||||||
create mode 100644 drivers/clk/qcom/clk-hfpll.c
|
|
||||||
create mode 100644 drivers/clk/qcom/clk-hfpll.h
|
|
||||||
|
|
||||||
--- a/drivers/clk/qcom/Makefile
|
|
||||||
+++ b/drivers/clk/qcom/Makefile
|
|
||||||
@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
|
|
||||||
clk-qcom-y += clk-rcg.o
|
|
||||||
clk-qcom-y += clk-rcg2.o
|
|
||||||
clk-qcom-y += clk-branch.o
|
|
||||||
+clk-qcom-y += clk-hfpll.o
|
|
||||||
clk-qcom-y += reset.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/clk/qcom/clk-hfpll.c
|
|
||||||
@@ -0,0 +1,253 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/export.h>
|
|
||||||
+#include <linux/regmap.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+#include <linux/clk-provider.h>
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
+
|
|
||||||
+#include "clk-regmap.h"
|
|
||||||
+#include "clk-hfpll.h"
|
|
||||||
+
|
|
||||||
+#define PLL_OUTCTRL BIT(0)
|
|
||||||
+#define PLL_BYPASSNL BIT(1)
|
|
||||||
+#define PLL_RESET_N BIT(2)
|
|
||||||
+
|
|
||||||
+/* Initialize a HFPLL at a given rate and enable it. */
|
|
||||||
+static void __clk_hfpll_init_once(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+
|
|
||||||
+ if (likely(h->init_done))
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ /* Configure PLL parameters for integer mode. */
|
|
||||||
+ if (hd->config_val)
|
|
||||||
+ regmap_write(regmap, hd->config_reg, hd->config_val);
|
|
||||||
+ regmap_write(regmap, hd->m_reg, 0);
|
|
||||||
+ regmap_write(regmap, hd->n_reg, 1);
|
|
||||||
+
|
|
||||||
+ if (hd->user_reg) {
|
|
||||||
+ u32 regval = hd->user_val;
|
|
||||||
+ unsigned long rate;
|
|
||||||
+
|
|
||||||
+ rate = __clk_get_rate(hw->clk);
|
|
||||||
+
|
|
||||||
+ /* Pick the right VCO. */
|
|
||||||
+ if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
|
|
||||||
+ regval |= hd->user_vco_mask;
|
|
||||||
+ regmap_write(regmap, hd->user_reg, regval);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (hd->droop_reg)
|
|
||||||
+ regmap_write(regmap, hd->droop_reg, hd->droop_val);
|
|
||||||
+
|
|
||||||
+ h->init_done = true;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __clk_hfpll_enable(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+ u32 val;
|
|
||||||
+
|
|
||||||
+ __clk_hfpll_init_once(hw);
|
|
||||||
+
|
|
||||||
+ /* Disable PLL bypass mode. */
|
|
||||||
+ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * H/W requires a 5us delay between disabling the bypass and
|
|
||||||
+ * de-asserting the reset. Delay 10us just to be safe.
|
|
||||||
+ */
|
|
||||||
+ udelay(10);
|
|
||||||
+
|
|
||||||
+ /* De-assert active-low PLL reset. */
|
|
||||||
+ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
|
|
||||||
+
|
|
||||||
+ /* Wait for PLL to lock. */
|
|
||||||
+ if (hd->status_reg) {
|
|
||||||
+ do {
|
|
||||||
+ regmap_read(regmap, hd->status_reg, &val);
|
|
||||||
+ } while (!(val & BIT(hd->lock_bit)));
|
|
||||||
+ } else {
|
|
||||||
+ udelay(60);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Enable PLL output. */
|
|
||||||
+ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* Enable an already-configured HFPLL. */
|
|
||||||
+static int clk_hfpll_enable(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ unsigned long flags;
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+ u32 mode;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&h->lock, flags);
|
|
||||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
|
||||||
+ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
|
|
||||||
+ __clk_hfpll_enable(hw);
|
|
||||||
+ spin_unlock_irqrestore(&h->lock, flags);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __clk_hfpll_disable(struct clk_hfpll *h)
|
|
||||||
+{
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Disable the PLL output, disable test mode, enable the bypass mode,
|
|
||||||
+ * and assert the reset.
|
|
||||||
+ */
|
|
||||||
+ regmap_update_bits(regmap, hd->mode_reg,
|
|
||||||
+ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void clk_hfpll_disable(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ unsigned long flags;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&h->lock, flags);
|
|
||||||
+ __clk_hfpll_disable(h);
|
|
||||||
+ spin_unlock_irqrestore(&h->lock, flags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long *parent_rate)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ unsigned long rrate;
|
|
||||||
+
|
|
||||||
+ rate = clamp(rate, hd->min_rate, hd->max_rate);
|
|
||||||
+
|
|
||||||
+ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
|
|
||||||
+ if (rrate > hd->max_rate)
|
|
||||||
+ rrate -= *parent_rate;
|
|
||||||
+
|
|
||||||
+ return rrate;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * For optimization reasons, assumes no downstream clocks are actively using
|
|
||||||
+ * it.
|
|
||||||
+ */
|
|
||||||
+static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long parent_rate)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+ unsigned long flags;
|
|
||||||
+ u32 l_val, val;
|
|
||||||
+ bool enabled;
|
|
||||||
+
|
|
||||||
+ l_val = rate / parent_rate;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&h->lock, flags);
|
|
||||||
+
|
|
||||||
+ enabled = __clk_is_enabled(hw->clk);
|
|
||||||
+ if (enabled)
|
|
||||||
+ __clk_hfpll_disable(h);
|
|
||||||
+
|
|
||||||
+ /* Pick the right VCO. */
|
|
||||||
+ if (hd->user_reg && hd->user_vco_mask) {
|
|
||||||
+ regmap_read(regmap, hd->user_reg, &val);
|
|
||||||
+ if (rate <= hd->low_vco_max_rate)
|
|
||||||
+ val &= ~hd->user_vco_mask;
|
|
||||||
+ else
|
|
||||||
+ val |= hd->user_vco_mask;
|
|
||||||
+ regmap_write(regmap, hd->user_reg, val);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ regmap_write(regmap, hd->l_reg, l_val);
|
|
||||||
+
|
|
||||||
+ if (enabled)
|
|
||||||
+ __clk_hfpll_enable(hw);
|
|
||||||
+
|
|
||||||
+ spin_unlock_irqrestore(&h->lock, flags);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
|
|
||||||
+ unsigned long parent_rate)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+ u32 l_val;
|
|
||||||
+
|
|
||||||
+ regmap_read(regmap, hd->l_reg, &l_val);
|
|
||||||
+
|
|
||||||
+ return l_val * parent_rate;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void clk_hfpll_init(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+ u32 mode, status;
|
|
||||||
+
|
|
||||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
|
||||||
+ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
|
|
||||||
+ __clk_hfpll_init_once(hw);
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (hd->status_reg) {
|
|
||||||
+ regmap_read(regmap, hd->status_reg, &status);
|
|
||||||
+ if (!(status & BIT(hd->lock_bit))) {
|
|
||||||
+ WARN(1, "HFPLL %s is ON, but not locked!\n",
|
|
||||||
+ __clk_get_name(hw->clk));
|
|
||||||
+ clk_hfpll_disable(hw);
|
|
||||||
+ __clk_hfpll_init_once(hw);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int hfpll_is_enabled(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
|
||||||
+ struct hfpll_data const *hd = h->d;
|
|
||||||
+ struct regmap *regmap = h->clkr.regmap;
|
|
||||||
+ u32 mode;
|
|
||||||
+
|
|
||||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
|
||||||
+ mode &= 0x7;
|
|
||||||
+ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+const struct clk_ops clk_ops_hfpll = {
|
|
||||||
+ .enable = clk_hfpll_enable,
|
|
||||||
+ .disable = clk_hfpll_disable,
|
|
||||||
+ .is_enabled = hfpll_is_enabled,
|
|
||||||
+ .round_rate = clk_hfpll_round_rate,
|
|
||||||
+ .set_rate = clk_hfpll_set_rate,
|
|
||||||
+ .recalc_rate = clk_hfpll_recalc_rate,
|
|
||||||
+ .init = clk_hfpll_init,
|
|
||||||
+};
|
|
||||||
+EXPORT_SYMBOL_GPL(clk_ops_hfpll);
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/clk/qcom/clk-hfpll.h
|
|
||||||
@@ -0,0 +1,54 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+#ifndef __QCOM_CLK_HFPLL_H__
|
|
||||||
+#define __QCOM_CLK_HFPLL_H__
|
|
||||||
+
|
|
||||||
+#include <linux/clk-provider.h>
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
+#include "clk-regmap.h"
|
|
||||||
+
|
|
||||||
+struct hfpll_data {
|
|
||||||
+ u32 mode_reg;
|
|
||||||
+ u32 l_reg;
|
|
||||||
+ u32 m_reg;
|
|
||||||
+ u32 n_reg;
|
|
||||||
+ u32 user_reg;
|
|
||||||
+ u32 droop_reg;
|
|
||||||
+ u32 config_reg;
|
|
||||||
+ u32 status_reg;
|
|
||||||
+ u8 lock_bit;
|
|
||||||
+
|
|
||||||
+ u32 droop_val;
|
|
||||||
+ u32 config_val;
|
|
||||||
+ u32 user_val;
|
|
||||||
+ u32 user_vco_mask;
|
|
||||||
+ unsigned long low_vco_max_rate;
|
|
||||||
+
|
|
||||||
+ unsigned long min_rate;
|
|
||||||
+ unsigned long max_rate;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct clk_hfpll {
|
|
||||||
+ struct hfpll_data const *d;
|
|
||||||
+ int init_done;
|
|
||||||
+
|
|
||||||
+ struct clk_regmap clkr;
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define to_clk_hfpll(_hw) \
|
|
||||||
+ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
|
|
||||||
+
|
|
||||||
+extern const struct clk_ops clk_ops_hfpll;
|
|
||||||
+
|
|
||||||
+#endif
|
|
|
@ -1,206 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,06/13] clk: qcom: Add HFPLL driver
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063231
|
|
||||||
Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:25 -0700
|
|
||||||
|
|
||||||
On some devices (MSM8974 for example), the HFPLLs are
|
|
||||||
instantiated within the Krait processor subsystem as separate
|
|
||||||
register regions. Add a driver for these PLLs so that we can
|
|
||||||
provide HFPLL clocks for use by the system.
|
|
||||||
|
|
||||||
Cc: <devicetree@vger.kernel.org>
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++
|
|
||||||
drivers/clk/qcom/Kconfig | 8 ++
|
|
||||||
drivers/clk/qcom/Makefile | 1 +
|
|
||||||
drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++
|
|
||||||
4 files changed, 158 insertions(+)
|
|
||||||
create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
|
|
||||||
create mode 100644 drivers/clk/qcom/hfpll.c
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
|
|
||||||
@@ -0,0 +1,40 @@
|
|
||||||
+High-Frequency PLL (HFPLL)
|
|
||||||
+
|
|
||||||
+PROPERTIES
|
|
||||||
+
|
|
||||||
+- compatible:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: must be "qcom,hfpll"
|
|
||||||
+
|
|
||||||
+- reg:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: address and size of HPLL registers. An optional second
|
|
||||||
+ element specifies the address and size of the alias
|
|
||||||
+ register region.
|
|
||||||
+
|
|
||||||
+- clock-output-names:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: Name of the PLL. Typically hfpllX where X is a CPU number
|
|
||||||
+ starting at 0. Otherwise hfpll_Y where Y is more specific
|
|
||||||
+ such as "l2".
|
|
||||||
+
|
|
||||||
+Example:
|
|
||||||
+
|
|
||||||
+1) An HFPLL for the L2 cache.
|
|
||||||
+
|
|
||||||
+ clock-controller@f9016000 {
|
|
||||||
+ compatible = "qcom,hfpll";
|
|
||||||
+ reg = <0xf9016000 0x30>;
|
|
||||||
+ clock-output-names = "hfpll_l2";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+2) An HFPLL for CPU0. This HFPLL has the alias register region.
|
|
||||||
+
|
|
||||||
+ clock-controller@f908a000 {
|
|
||||||
+ compatible = "qcom,hfpll";
|
|
||||||
+ reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
|
|
||||||
+ clock-output-names = "hfpll0";
|
|
||||||
+ };
|
|
||||||
--- a/drivers/clk/qcom/Kconfig
|
|
||||||
+++ b/drivers/clk/qcom/Kconfig
|
|
||||||
@@ -70,3 +70,11 @@ config MSM_MMCC_8974
|
|
||||||
Support for the multimedia clock controller on msm8974 devices.
|
|
||||||
Say Y if you want to support multimedia devices such as display,
|
|
||||||
graphics, video encode/decode, camera, etc.
|
|
||||||
+
|
|
||||||
+config QCOM_HFPLL
|
|
||||||
+ tristate "High-Frequency PLL (HFPLL) Clock Controller"
|
|
||||||
+ depends on COMMON_CLK_QCOM
|
|
||||||
+ help
|
|
||||||
+ Support for the high-frequency PLLs present on Qualcomm devices.
|
|
||||||
+ Say Y if you want to support CPU frequency scaling on devices
|
|
||||||
+ such as MSM8974, APQ8084, etc.
|
|
||||||
--- a/drivers/clk/qcom/Makefile
|
|
||||||
+++ b/drivers/clk/qcom/Makefile
|
|
||||||
@@ -17,3 +17,4 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm896
|
|
||||||
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
|
|
||||||
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
|
|
||||||
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
|
|
||||||
+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/clk/qcom/hfpll.c
|
|
||||||
@@ -0,0 +1,109 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/of.h>
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/clk-provider.h>
|
|
||||||
+#include <linux/regmap.h>
|
|
||||||
+
|
|
||||||
+#include "clk-regmap.h"
|
|
||||||
+#include "clk-hfpll.h"
|
|
||||||
+
|
|
||||||
+static const struct hfpll_data hdata = {
|
|
||||||
+ .mode_reg = 0x00,
|
|
||||||
+ .l_reg = 0x04,
|
|
||||||
+ .m_reg = 0x08,
|
|
||||||
+ .n_reg = 0x0c,
|
|
||||||
+ .user_reg = 0x10,
|
|
||||||
+ .config_reg = 0x14,
|
|
||||||
+ .config_val = 0x430405d,
|
|
||||||
+ .status_reg = 0x1c,
|
|
||||||
+ .lock_bit = 16,
|
|
||||||
+
|
|
||||||
+ .user_val = 0x8,
|
|
||||||
+ .user_vco_mask = 0x100000,
|
|
||||||
+ .low_vco_max_rate = 1248000000,
|
|
||||||
+ .min_rate = 537600000UL,
|
|
||||||
+ .max_rate = 2900000000UL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct of_device_id qcom_hfpll_match_table[] = {
|
|
||||||
+ { .compatible = "qcom,hfpll" },
|
|
||||||
+ { }
|
|
||||||
+};
|
|
||||||
+MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
|
|
||||||
+
|
|
||||||
+static const struct regmap_config hfpll_regmap_config = {
|
|
||||||
+ .reg_bits = 32,
|
|
||||||
+ .reg_stride = 4,
|
|
||||||
+ .val_bits = 32,
|
|
||||||
+ .max_register = 0x30,
|
|
||||||
+ .fast_io = true,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int qcom_hfpll_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct clk *clk;
|
|
||||||
+ struct resource *res;
|
|
||||||
+ struct device *dev = &pdev->dev;
|
|
||||||
+ void __iomem *base;
|
|
||||||
+ struct regmap *regmap;
|
|
||||||
+ struct clk_hfpll *h;
|
|
||||||
+ struct clk_init_data init = {
|
|
||||||
+ .parent_names = (const char *[]){ "xo" },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &clk_ops_hfpll,
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
|
|
||||||
+ if (!h)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ base = devm_ioremap_resource(dev, res);
|
|
||||||
+ if (IS_ERR(base))
|
|
||||||
+ return PTR_ERR(base);
|
|
||||||
+
|
|
||||||
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
|
|
||||||
+ if (IS_ERR(regmap))
|
|
||||||
+ return PTR_ERR(regmap);
|
|
||||||
+
|
|
||||||
+ if (of_property_read_string_index(dev->of_node, "clock-output-names",
|
|
||||||
+ 0, &init.name))
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ h->d = &hdata;
|
|
||||||
+ h->clkr.hw.init = &init;
|
|
||||||
+ spin_lock_init(&h->lock);
|
|
||||||
+
|
|
||||||
+ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr);
|
|
||||||
+
|
|
||||||
+ return PTR_ERR_OR_ZERO(clk);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver qcom_hfpll_driver = {
|
|
||||||
+ .probe = qcom_hfpll_probe,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "qcom-hfpll",
|
|
||||||
+ .of_match_table = qcom_hfpll_match_table,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+module_platform_driver(qcom_hfpll_driver);
|
|
||||||
+
|
|
||||||
+MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
+MODULE_ALIAS("platform:qcom-hfpll");
|
|
|
@ -1,127 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063241
|
|
||||||
Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:27 -0700
|
|
||||||
|
|
||||||
Describe the HFPLLs present on IPQ806X devices.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
1 file changed, 83 insertions(+)
|
|
||||||
|
|
||||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
|
||||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
|
||||||
@@ -30,6 +30,7 @@
|
|
||||||
#include "clk-pll.h"
|
|
||||||
#include "clk-rcg.h"
|
|
||||||
#include "clk-branch.h"
|
|
||||||
+#include "clk-hfpll.h"
|
|
||||||
#include "reset.h"
|
|
||||||
|
|
||||||
static struct clk_pll pll0 = {
|
|
||||||
@@ -102,6 +103,85 @@ static struct clk_regmap pll8_vote = {
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
+static struct hfpll_data hfpll0_data = {
|
|
||||||
+ .mode_reg = 0x3200,
|
|
||||||
+ .l_reg = 0x3208,
|
|
||||||
+ .m_reg = 0x320c,
|
|
||||||
+ .n_reg = 0x3210,
|
|
||||||
+ .config_reg = 0x3204,
|
|
||||||
+ .status_reg = 0x321c,
|
|
||||||
+ .config_val = 0x7845c665,
|
|
||||||
+ .droop_reg = 0x3214,
|
|
||||||
+ .droop_val = 0x0108c000,
|
|
||||||
+ .min_rate = 600000000UL,
|
|
||||||
+ .max_rate = 1800000000UL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_hfpll hfpll0 = {
|
|
||||||
+ .d = &hfpll0_data,
|
|
||||||
+ .clkr.hw.init = &(struct clk_init_data){
|
|
||||||
+ .parent_names = (const char *[]){ "pxo" },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .name = "hfpll0",
|
|
||||||
+ .ops = &clk_ops_hfpll,
|
|
||||||
+ .flags = CLK_IGNORE_UNUSED,
|
|
||||||
+ },
|
|
||||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct hfpll_data hfpll1_data = {
|
|
||||||
+ .mode_reg = 0x3240,
|
|
||||||
+ .l_reg = 0x3248,
|
|
||||||
+ .m_reg = 0x324c,
|
|
||||||
+ .n_reg = 0x3250,
|
|
||||||
+ .config_reg = 0x3244,
|
|
||||||
+ .status_reg = 0x325c,
|
|
||||||
+ .config_val = 0x7845c665,
|
|
||||||
+ .droop_reg = 0x3314,
|
|
||||||
+ .droop_val = 0x0108c000,
|
|
||||||
+ .min_rate = 600000000UL,
|
|
||||||
+ .max_rate = 1800000000UL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_hfpll hfpll1 = {
|
|
||||||
+ .d = &hfpll1_data,
|
|
||||||
+ .clkr.hw.init = &(struct clk_init_data){
|
|
||||||
+ .parent_names = (const char *[]){ "pxo" },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .name = "hfpll1",
|
|
||||||
+ .ops = &clk_ops_hfpll,
|
|
||||||
+ .flags = CLK_IGNORE_UNUSED,
|
|
||||||
+ },
|
|
||||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct hfpll_data hfpll_l2_data = {
|
|
||||||
+ .mode_reg = 0x3300,
|
|
||||||
+ .l_reg = 0x3308,
|
|
||||||
+ .m_reg = 0x330c,
|
|
||||||
+ .n_reg = 0x3310,
|
|
||||||
+ .config_reg = 0x3304,
|
|
||||||
+ .status_reg = 0x331c,
|
|
||||||
+ .config_val = 0x7845c665,
|
|
||||||
+ .droop_reg = 0x3314,
|
|
||||||
+ .droop_val = 0x0108c000,
|
|
||||||
+ .min_rate = 600000000UL,
|
|
||||||
+ .max_rate = 1800000000UL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_hfpll hfpll_l2 = {
|
|
||||||
+ .d = &hfpll_l2_data,
|
|
||||||
+ .clkr.hw.init = &(struct clk_init_data){
|
|
||||||
+ .parent_names = (const char *[]){ "pxo" },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .name = "hfpll_l2",
|
|
||||||
+ .ops = &clk_ops_hfpll,
|
|
||||||
+ .flags = CLK_IGNORE_UNUSED,
|
|
||||||
+ },
|
|
||||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
static struct clk_pll pll14 = {
|
|
||||||
.l_reg = 0x31c4,
|
|
||||||
.m_reg = 0x31c8,
|
|
||||||
@@ -2261,6 +2341,9 @@ static struct clk_regmap *gcc_ipq806x_cl
|
|
||||||
[USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr,
|
|
||||||
[USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr,
|
|
||||||
[USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr,
|
|
||||||
+ [PLL9] = &hfpll0.clkr,
|
|
||||||
+ [PLL10] = &hfpll1.clkr,
|
|
||||||
+ [PLL12] = &hfpll_l2.clkr,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct qcom_reset_map gcc_ipq806x_resets[] = {
|
|
|
@ -1,271 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,09/13] clk: qcom: Add support for Krait clocks
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063251
|
|
||||||
Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:28 -0700
|
|
||||||
|
|
||||||
The Krait clocks are made up of a series of muxes and a divider
|
|
||||||
that choose between a fixed rate clock and dedicated HFPLLs for
|
|
||||||
each CPU. Instead of using mmio accesses to remux parents, the
|
|
||||||
Krait implementation exposes the remux control via cp15
|
|
||||||
registers. Support these clocks.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
drivers/clk/qcom/Kconfig | 4 ++
|
|
||||||
drivers/clk/qcom/Makefile | 1 +
|
|
||||||
drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
drivers/clk/qcom/clk-krait.h | 49 +++++++++++++
|
|
||||||
4 files changed, 220 insertions(+)
|
|
||||||
create mode 100644 drivers/clk/qcom/clk-krait.c
|
|
||||||
create mode 100644 drivers/clk/qcom/clk-krait.h
|
|
||||||
|
|
||||||
--- a/drivers/clk/qcom/Kconfig
|
|
||||||
+++ b/drivers/clk/qcom/Kconfig
|
|
||||||
@@ -78,3 +78,7 @@ config QCOM_HFPLL
|
|
||||||
Support for the high-frequency PLLs present on Qualcomm devices.
|
|
||||||
Say Y if you want to support CPU frequency scaling on devices
|
|
||||||
such as MSM8974, APQ8084, etc.
|
|
||||||
+
|
|
||||||
+config KRAIT_CLOCKS
|
|
||||||
+ bool
|
|
||||||
+ select KRAIT_L2_ACCESSORS
|
|
||||||
--- a/drivers/clk/qcom/Makefile
|
|
||||||
+++ b/drivers/clk/qcom/Makefile
|
|
||||||
@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
|
|
||||||
clk-qcom-y += clk-rcg.o
|
|
||||||
clk-qcom-y += clk-rcg2.o
|
|
||||||
clk-qcom-y += clk-branch.o
|
|
||||||
+clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
|
|
||||||
clk-qcom-y += clk-hfpll.o
|
|
||||||
clk-qcom-y += reset.o
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/clk/qcom/clk-krait.c
|
|
||||||
@@ -0,0 +1,166 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+#include <linux/clk-provider.h>
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
+
|
|
||||||
+#include <asm/krait-l2-accessors.h>
|
|
||||||
+
|
|
||||||
+#include "clk-krait.h"
|
|
||||||
+
|
|
||||||
+/* Secondary and primary muxes share the same cp15 register */
|
|
||||||
+static DEFINE_SPINLOCK(krait_clock_reg_lock);
|
|
||||||
+
|
|
||||||
+#define LPL_SHIFT 8
|
|
||||||
+static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
|
|
||||||
+{
|
|
||||||
+ unsigned long flags;
|
|
||||||
+ u32 regval;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
|
|
||||||
+ regval = krait_get_l2_indirect_reg(mux->offset);
|
|
||||||
+ regval &= ~(mux->mask << mux->shift);
|
|
||||||
+ regval |= (sel & mux->mask) << mux->shift;
|
|
||||||
+ if (mux->lpl) {
|
|
||||||
+ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
|
|
||||||
+ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
|
|
||||||
+ }
|
|
||||||
+ krait_set_l2_indirect_reg(mux->offset, regval);
|
|
||||||
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
|
|
||||||
+
|
|
||||||
+ /* Wait for switch to complete. */
|
|
||||||
+ mb();
|
|
||||||
+ udelay(1);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
|
|
||||||
+{
|
|
||||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
|
||||||
+ u32 sel;
|
|
||||||
+
|
|
||||||
+ sel = clk_mux_reindex(index, mux->parent_map, 0);
|
|
||||||
+ mux->en_mask = sel;
|
|
||||||
+ /* Don't touch mux if CPU is off as it won't work */
|
|
||||||
+ if (__clk_is_enabled(hw->clk))
|
|
||||||
+ __krait_mux_set_sel(mux, sel);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static u8 krait_mux_get_parent(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
|
||||||
+ u32 sel;
|
|
||||||
+
|
|
||||||
+ sel = krait_get_l2_indirect_reg(mux->offset);
|
|
||||||
+ sel >>= mux->shift;
|
|
||||||
+ sel &= mux->mask;
|
|
||||||
+ mux->en_mask = sel;
|
|
||||||
+
|
|
||||||
+ return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ int i;
|
|
||||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
|
||||||
+ int num_parents = __clk_get_num_parents(hw->clk);
|
|
||||||
+
|
|
||||||
+ i = mux->safe_sel;
|
|
||||||
+ for (i = 0; i < num_parents; i++)
|
|
||||||
+ if (mux->safe_sel == mux->parent_map[i])
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ return __clk_get_hw(clk_get_parent_by_index(hw->clk, i));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int krait_mux_enable(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
|
||||||
+
|
|
||||||
+ __krait_mux_set_sel(mux, mux->en_mask);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void krait_mux_disable(struct clk_hw *hw)
|
|
||||||
+{
|
|
||||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
|
||||||
+
|
|
||||||
+ __krait_mux_set_sel(mux, mux->safe_sel);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+const struct clk_ops krait_mux_clk_ops = {
|
|
||||||
+ .enable = krait_mux_enable,
|
|
||||||
+ .disable = krait_mux_disable,
|
|
||||||
+ .set_parent = krait_mux_set_parent,
|
|
||||||
+ .get_parent = krait_mux_get_parent,
|
|
||||||
+ .determine_rate = __clk_mux_determine_rate_closest,
|
|
||||||
+ .get_safe_parent = krait_mux_get_safe_parent,
|
|
||||||
+};
|
|
||||||
+EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
|
|
||||||
+
|
|
||||||
+/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
|
|
||||||
+static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long *parent_rate)
|
|
||||||
+{
|
|
||||||
+ *parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), rate * 2);
|
|
||||||
+ return DIV_ROUND_UP(*parent_rate, 2);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
||||||
+ unsigned long parent_rate)
|
|
||||||
+{
|
|
||||||
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
|
|
||||||
+ unsigned long flags;
|
|
||||||
+ u32 val;
|
|
||||||
+ u32 mask = BIT(d->width) - 1;
|
|
||||||
+
|
|
||||||
+ if (d->lpl)
|
|
||||||
+ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
|
|
||||||
+ val = krait_get_l2_indirect_reg(d->offset);
|
|
||||||
+ val &= ~mask;
|
|
||||||
+ krait_set_l2_indirect_reg(d->offset, val);
|
|
||||||
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static unsigned long
|
|
||||||
+krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
||||||
+{
|
|
||||||
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
|
|
||||||
+ u32 mask = BIT(d->width) - 1;
|
|
||||||
+ u32 div;
|
|
||||||
+
|
|
||||||
+ div = krait_get_l2_indirect_reg(d->offset);
|
|
||||||
+ div >>= d->shift;
|
|
||||||
+ div &= mask;
|
|
||||||
+ div = (div + 1) * 2;
|
|
||||||
+
|
|
||||||
+ return DIV_ROUND_UP(parent_rate, div);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+const struct clk_ops krait_div2_clk_ops = {
|
|
||||||
+ .round_rate = krait_div2_round_rate,
|
|
||||||
+ .set_rate = krait_div2_set_rate,
|
|
||||||
+ .recalc_rate = krait_div2_recalc_rate,
|
|
||||||
+};
|
|
||||||
+EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/clk/qcom/clk-krait.h
|
|
||||||
@@ -0,0 +1,49 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef __QCOM_CLK_KRAIT_H
|
|
||||||
+#define __QCOM_CLK_KRAIT_H
|
|
||||||
+
|
|
||||||
+#include <linux/clk-provider.h>
|
|
||||||
+
|
|
||||||
+struct krait_mux_clk {
|
|
||||||
+ unsigned int *parent_map;
|
|
||||||
+ bool has_safe_parent;
|
|
||||||
+ u8 safe_sel;
|
|
||||||
+ u32 offset;
|
|
||||||
+ u32 mask;
|
|
||||||
+ u32 shift;
|
|
||||||
+ u32 en_mask;
|
|
||||||
+ bool lpl;
|
|
||||||
+
|
|
||||||
+ struct clk_hw hw;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
|
|
||||||
+
|
|
||||||
+extern const struct clk_ops krait_mux_clk_ops;
|
|
||||||
+
|
|
||||||
+struct krait_div2_clk {
|
|
||||||
+ u32 offset;
|
|
||||||
+ u8 width;
|
|
||||||
+ u32 shift;
|
|
||||||
+ bool lpl;
|
|
||||||
+
|
|
||||||
+ struct clk_hw hw;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
|
|
||||||
+
|
|
||||||
+extern const struct clk_ops krait_div2_clk_ops;
|
|
||||||
+
|
|
||||||
+#endif
|
|
|
@ -1,205 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063201
|
|
||||||
Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:29 -0700
|
|
||||||
|
|
||||||
The ACC and GCC regions present in KPSSv1 contain registers to
|
|
||||||
control clocks and power to each Krait CPU and L2. For CPUfreq
|
|
||||||
purposes probe these devices and expose a mux clock that chooses
|
|
||||||
between PXO and PLL8.
|
|
||||||
|
|
||||||
Cc: <devicetree@vger.kernel.org>
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++
|
|
||||||
.../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++
|
|
||||||
drivers/clk/qcom/Kconfig | 8 ++
|
|
||||||
drivers/clk/qcom/Makefile | 1 +
|
|
||||||
drivers/clk/qcom/kpss-xcc.c | 95 ++++++++++++++++++++++
|
|
||||||
5 files changed, 139 insertions(+)
|
|
||||||
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
|
|
||||||
create mode 100644 drivers/clk/qcom/kpss-xcc.c
|
|
||||||
|
|
||||||
--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
|
|
||||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
|
|
||||||
@@ -21,10 +21,17 @@ PROPERTIES
|
|
||||||
the register region. An optional second element specifies
|
|
||||||
the base address and size of the alias register region.
|
|
||||||
|
|
||||||
+- clock-output-names:
|
|
||||||
+ Usage: optional
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: Name of the output clock. Typically acpuX_aux where X is a
|
|
||||||
+ CPU number starting at 0.
|
|
||||||
+
|
|
||||||
Example:
|
|
||||||
|
|
||||||
clock-controller@2088000 {
|
|
||||||
compatible = "qcom,kpss-acc-v2";
|
|
||||||
reg = <0x02088000 0x1000>,
|
|
||||||
<0x02008000 0x1000>;
|
|
||||||
+ clock-output-names = "acpu0_aux";
|
|
||||||
};
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
|
|
||||||
@@ -0,0 +1,28 @@
|
|
||||||
+Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
|
|
||||||
+
|
|
||||||
+PROPERTIES
|
|
||||||
+
|
|
||||||
+- compatible:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: should be one of:
|
|
||||||
+ "qcom,kpss-gcc"
|
|
||||||
+
|
|
||||||
+- reg:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: base address and size of the register region
|
|
||||||
+
|
|
||||||
+- clock-output-names:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: Name of the output clock. Typically acpu_l2_aux indicating
|
|
||||||
+ an L2 cache auxiliary clock.
|
|
||||||
+
|
|
||||||
+Example:
|
|
||||||
+
|
|
||||||
+ l2cc: clock-controller@2011000 {
|
|
||||||
+ compatible = "qcom,kpss-gcc";
|
|
||||||
+ reg = <0x2011000 0x1000>;
|
|
||||||
+ clock-output-names = "acpu_l2_aux";
|
|
||||||
+ };
|
|
||||||
--- a/drivers/clk/qcom/Kconfig
|
|
||||||
+++ b/drivers/clk/qcom/Kconfig
|
|
||||||
@@ -79,6 +79,14 @@ config QCOM_HFPLL
|
|
||||||
Say Y if you want to support CPU frequency scaling on devices
|
|
||||||
such as MSM8974, APQ8084, etc.
|
|
||||||
|
|
||||||
+config KPSS_XCC
|
|
||||||
+ tristate "KPSS Clock Controller"
|
|
||||||
+ depends on COMMON_CLK_QCOM
|
|
||||||
+ help
|
|
||||||
+ Support for the Krait ACC and GCC clock controllers. Say Y
|
|
||||||
+ if you want to support CPU frequency scaling on devices such
|
|
||||||
+ as MSM8960, APQ8064, etc.
|
|
||||||
+
|
|
||||||
config KRAIT_CLOCKS
|
|
||||||
bool
|
|
||||||
select KRAIT_L2_ACCESSORS
|
|
||||||
--- a/drivers/clk/qcom/Makefile
|
|
||||||
+++ b/drivers/clk/qcom/Makefile
|
|
||||||
@@ -18,4 +18,5 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm896
|
|
||||||
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
|
|
||||||
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
|
|
||||||
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
|
|
||||||
+obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
|
|
||||||
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/clk/qcom/kpss-xcc.c
|
|
||||||
@@ -0,0 +1,95 @@
|
|
||||||
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/of.h>
|
|
||||||
+#include <linux/of_device.h>
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/clk-provider.h>
|
|
||||||
+
|
|
||||||
+static const char *aux_parents[] = {
|
|
||||||
+ "pll8_vote",
|
|
||||||
+ "pxo",
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static unsigned int aux_parent_map[] = {
|
|
||||||
+ 3,
|
|
||||||
+ 0,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct of_device_id kpss_xcc_match_table[] = {
|
|
||||||
+ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
|
|
||||||
+ { .compatible = "qcom,kpss-gcc" },
|
|
||||||
+ {}
|
|
||||||
+};
|
|
||||||
+MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
|
|
||||||
+
|
|
||||||
+static int kpss_xcc_driver_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ const struct of_device_id *id;
|
|
||||||
+ struct clk *clk;
|
|
||||||
+ struct resource *res;
|
|
||||||
+ void __iomem *base;
|
|
||||||
+ const char *name;
|
|
||||||
+
|
|
||||||
+ id = of_match_device(kpss_xcc_match_table, &pdev->dev);
|
|
||||||
+ if (!id)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ base = devm_ioremap_resource(&pdev->dev, res);
|
|
||||||
+ if (IS_ERR(base))
|
|
||||||
+ return PTR_ERR(base);
|
|
||||||
+
|
|
||||||
+ if (id->data) {
|
|
||||||
+ if (of_property_read_string_index(pdev->dev.of_node,
|
|
||||||
+ "clock-output-names", 0, &name))
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ base += 0x14;
|
|
||||||
+ } else {
|
|
||||||
+ name = "acpu_l2_aux";
|
|
||||||
+ base += 0x28;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
|
|
||||||
+ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
|
|
||||||
+ 0, aux_parent_map, NULL);
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, clk);
|
|
||||||
+
|
|
||||||
+ return PTR_ERR_OR_ZERO(clk);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int kpss_xcc_driver_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ clk_unregister_mux(platform_get_drvdata(pdev));
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver kpss_xcc_driver = {
|
|
||||||
+ .probe = kpss_xcc_driver_probe,
|
|
||||||
+ .remove = kpss_xcc_driver_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "kpss-xcc",
|
|
||||||
+ .of_match_table = kpss_xcc_match_table,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+module_platform_driver(kpss_xcc_driver);
|
|
||||||
+
|
|
||||||
+MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
+MODULE_ALIAS("platform:kpss-xcc");
|
|
|
@ -1,435 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063121
|
|
||||||
Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:30 -0700
|
|
||||||
|
|
||||||
The Krait CPU clocks are made up of a primary mux and secondary
|
|
||||||
mux for each CPU and the L2, controlled via cp15 accessors. For
|
|
||||||
Kraits within KPSSv1 each secondary mux accepts a different aux
|
|
||||||
source, but on KPSSv2 each secondary mux accepts the same aux
|
|
||||||
source.
|
|
||||||
|
|
||||||
Cc: <devicetree@vger.kernel.org>
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++
|
|
||||||
drivers/clk/qcom/Kconfig | 8 +
|
|
||||||
drivers/clk/qcom/Makefile | 1 +
|
|
||||||
drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++
|
|
||||||
4 files changed, 383 insertions(+)
|
|
||||||
create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
|
|
||||||
create mode 100644 drivers/clk/qcom/krait-cc.c
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
|
|
||||||
@@ -0,0 +1,22 @@
|
|
||||||
+Krait Clock Controller
|
|
||||||
+
|
|
||||||
+PROPERTIES
|
|
||||||
+
|
|
||||||
+- compatible:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <string>
|
|
||||||
+ Definition: must be one of:
|
|
||||||
+ "qcom,krait-cc-v1"
|
|
||||||
+ "qcom,krait-cc-v2"
|
|
||||||
+
|
|
||||||
+- #clock-cells:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <u32>
|
|
||||||
+ Definition: must be 1
|
|
||||||
+
|
|
||||||
+Example:
|
|
||||||
+
|
|
||||||
+ kraitcc: clock-controller {
|
|
||||||
+ compatible = "qcom,krait-cc-v1";
|
|
||||||
+ #clock-cells = <1>;
|
|
||||||
+ };
|
|
||||||
--- a/drivers/clk/qcom/Kconfig
|
|
||||||
+++ b/drivers/clk/qcom/Kconfig
|
|
||||||
@@ -87,6 +87,14 @@ config KPSS_XCC
|
|
||||||
if you want to support CPU frequency scaling on devices such
|
|
||||||
as MSM8960, APQ8064, etc.
|
|
||||||
|
|
||||||
+config KRAITCC
|
|
||||||
+ tristate "Krait Clock Controller"
|
|
||||||
+ depends on COMMON_CLK_QCOM && ARM
|
|
||||||
+ select KRAIT_CLOCKS
|
|
||||||
+ help
|
|
||||||
+ Support for the Krait CPU clocks on Qualcomm devices.
|
|
||||||
+ Say Y if you want to support CPU frequency scaling.
|
|
||||||
+
|
|
||||||
config KRAIT_CLOCKS
|
|
||||||
bool
|
|
||||||
select KRAIT_L2_ACCESSORS
|
|
||||||
--- a/drivers/clk/qcom/Makefile
|
|
||||||
+++ b/drivers/clk/qcom/Makefile
|
|
||||||
@@ -20,3 +20,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8
|
|
||||||
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
|
|
||||||
obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
|
|
||||||
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
|
||||||
+obj-$(CONFIG_KRAITCC) += krait-cc.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/clk/qcom/krait-cc.c
|
|
||||||
@@ -0,0 +1,352 @@
|
|
||||||
+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/of.h>
|
|
||||||
+#include <linux/of_device.h>
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/clk-provider.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+
|
|
||||||
+#include "clk-krait.h"
|
|
||||||
+
|
|
||||||
+static unsigned int sec_mux_map[] = {
|
|
||||||
+ 2,
|
|
||||||
+ 0,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static unsigned int pri_mux_map[] = {
|
|
||||||
+ 1,
|
|
||||||
+ 2,
|
|
||||||
+ 0,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int
|
|
||||||
+krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
|
|
||||||
+{
|
|
||||||
+ struct krait_div2_clk *div;
|
|
||||||
+ struct clk_init_data init = {
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &krait_div2_clk_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ };
|
|
||||||
+ const char *p_names[1];
|
|
||||||
+ struct clk *clk;
|
|
||||||
+
|
|
||||||
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
|
|
||||||
+ if (!div)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ div->width = 2;
|
|
||||||
+ div->shift = 6;
|
|
||||||
+ div->lpl = id >= 0;
|
|
||||||
+ div->offset = offset;
|
|
||||||
+ div->hw.init = &init;
|
|
||||||
+
|
|
||||||
+ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
|
|
||||||
+ if (!init.name)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ init.parent_names = p_names;
|
|
||||||
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
|
|
||||||
+ if (!p_names[0]) {
|
|
||||||
+ kfree(init.name);
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ clk = devm_clk_register(dev, &div->hw);
|
|
||||||
+ kfree(p_names[0]);
|
|
||||||
+ kfree(init.name);
|
|
||||||
+
|
|
||||||
+ return PTR_ERR_OR_ZERO(clk);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int
|
|
||||||
+krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
|
|
||||||
+ bool unique_aux)
|
|
||||||
+{
|
|
||||||
+ struct krait_mux_clk *mux;
|
|
||||||
+ static const char *sec_mux_list[] = {
|
|
||||||
+ "acpu_aux",
|
|
||||||
+ "qsb",
|
|
||||||
+ };
|
|
||||||
+ struct clk_init_data init = {
|
|
||||||
+ .parent_names = sec_mux_list,
|
|
||||||
+ .num_parents = ARRAY_SIZE(sec_mux_list),
|
|
||||||
+ .ops = &krait_mux_clk_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ };
|
|
||||||
+ struct clk *clk;
|
|
||||||
+
|
|
||||||
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
|
||||||
+ if (!mux)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ mux->offset = offset;
|
|
||||||
+ mux->lpl = id >= 0;
|
|
||||||
+ mux->has_safe_parent = true;
|
|
||||||
+ mux->safe_sel = 2;
|
|
||||||
+ mux->mask = 0x3;
|
|
||||||
+ mux->shift = 2;
|
|
||||||
+ mux->parent_map = sec_mux_map;
|
|
||||||
+ mux->hw.init = &init;
|
|
||||||
+
|
|
||||||
+ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
|
||||||
+ if (!init.name)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ if (unique_aux) {
|
|
||||||
+ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
|
|
||||||
+ if (!sec_mux_list[0]) {
|
|
||||||
+ clk = ERR_PTR(-ENOMEM);
|
|
||||||
+ goto err_aux;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ clk = devm_clk_register(dev, &mux->hw);
|
|
||||||
+
|
|
||||||
+ if (unique_aux)
|
|
||||||
+ kfree(sec_mux_list[0]);
|
|
||||||
+err_aux:
|
|
||||||
+ kfree(init.name);
|
|
||||||
+ return PTR_ERR_OR_ZERO(clk);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct clk *
|
|
||||||
+krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset)
|
|
||||||
+{
|
|
||||||
+ struct krait_mux_clk *mux;
|
|
||||||
+ const char *p_names[3];
|
|
||||||
+ struct clk_init_data init = {
|
|
||||||
+ .parent_names = p_names,
|
|
||||||
+ .num_parents = ARRAY_SIZE(p_names),
|
|
||||||
+ .ops = &krait_mux_clk_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ };
|
|
||||||
+ struct clk *clk;
|
|
||||||
+
|
|
||||||
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
|
||||||
+ if (!mux)
|
|
||||||
+ return ERR_PTR(-ENOMEM);
|
|
||||||
+
|
|
||||||
+ mux->has_safe_parent = true;
|
|
||||||
+ mux->safe_sel = 0;
|
|
||||||
+ mux->mask = 0x3;
|
|
||||||
+ mux->shift = 0;
|
|
||||||
+ mux->offset = offset;
|
|
||||||
+ mux->lpl = id >= 0;
|
|
||||||
+ mux->parent_map = pri_mux_map;
|
|
||||||
+ mux->hw.init = &init;
|
|
||||||
+
|
|
||||||
+ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
|
|
||||||
+ if (!init.name)
|
|
||||||
+ return ERR_PTR(-ENOMEM);
|
|
||||||
+
|
|
||||||
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
|
|
||||||
+ if (!p_names[0]) {
|
|
||||||
+ clk = ERR_PTR(-ENOMEM);
|
|
||||||
+ goto err_p0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
|
|
||||||
+ if (!p_names[1]) {
|
|
||||||
+ clk = ERR_PTR(-ENOMEM);
|
|
||||||
+ goto err_p1;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
|
||||||
+ if (!p_names[2]) {
|
|
||||||
+ clk = ERR_PTR(-ENOMEM);
|
|
||||||
+ goto err_p2;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ clk = devm_clk_register(dev, &mux->hw);
|
|
||||||
+
|
|
||||||
+ kfree(p_names[2]);
|
|
||||||
+err_p2:
|
|
||||||
+ kfree(p_names[1]);
|
|
||||||
+err_p1:
|
|
||||||
+ kfree(p_names[0]);
|
|
||||||
+err_p0:
|
|
||||||
+ kfree(init.name);
|
|
||||||
+ return clk;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* id < 0 for L2, otherwise id == physical CPU number */
|
|
||||||
+static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ unsigned offset;
|
|
||||||
+ void *p = NULL;
|
|
||||||
+ const char *s;
|
|
||||||
+ struct clk *clk;
|
|
||||||
+
|
|
||||||
+ if (id >= 0) {
|
|
||||||
+ offset = 0x4501 + (0x1000 * id);
|
|
||||||
+ s = p = kasprintf(GFP_KERNEL, "%d", id);
|
|
||||||
+ if (!s)
|
|
||||||
+ return ERR_PTR(-ENOMEM);
|
|
||||||
+ } else {
|
|
||||||
+ offset = 0x500;
|
|
||||||
+ s = "_l2";
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = krait_add_div(dev, id, s, offset);
|
|
||||||
+ if (ret) {
|
|
||||||
+ clk = ERR_PTR(ret);
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
|
|
||||||
+ if (ret) {
|
|
||||||
+ clk = ERR_PTR(ret);
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ clk = krait_add_pri_mux(dev, id, s, offset);
|
|
||||||
+err:
|
|
||||||
+ kfree(p);
|
|
||||||
+ return clk;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
|
|
||||||
+{
|
|
||||||
+ unsigned int idx = clkspec->args[0];
|
|
||||||
+ struct clk **clks = data;
|
|
||||||
+
|
|
||||||
+ if (idx >= 5) {
|
|
||||||
+ pr_err("%s: invalid clock index %d\n", __func__, idx);
|
|
||||||
+ return ERR_PTR(-EINVAL);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return clks[idx] ? : ERR_PTR(-ENODEV);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct of_device_id krait_cc_match_table[] = {
|
|
||||||
+ { .compatible = "qcom,krait-cc-v1", (void *)1UL },
|
|
||||||
+ { .compatible = "qcom,krait-cc-v2" },
|
|
||||||
+ {}
|
|
||||||
+};
|
|
||||||
+MODULE_DEVICE_TABLE(of, krait_cc_match_table);
|
|
||||||
+
|
|
||||||
+static int krait_cc_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &pdev->dev;
|
|
||||||
+ const struct of_device_id *id;
|
|
||||||
+ unsigned long cur_rate, aux_rate;
|
|
||||||
+ int cpu;
|
|
||||||
+ struct clk *clk;
|
|
||||||
+ struct clk **clks;
|
|
||||||
+ struct clk *l2_pri_mux_clk;
|
|
||||||
+
|
|
||||||
+ id = of_match_device(krait_cc_match_table, dev);
|
|
||||||
+ if (!id)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
|
|
||||||
+ clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
|
|
||||||
+ if (IS_ERR(clk))
|
|
||||||
+ return PTR_ERR(clk);
|
|
||||||
+
|
|
||||||
+ if (!id->data) {
|
|
||||||
+ clk = clk_register_fixed_factor(dev, "acpu_aux",
|
|
||||||
+ "gpll0_vote", 0, 1, 2);
|
|
||||||
+ if (IS_ERR(clk))
|
|
||||||
+ return PTR_ERR(clk);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Krait configurations have at most 4 CPUs and one L2 */
|
|
||||||
+ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
|
|
||||||
+ if (!clks)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ for_each_possible_cpu(cpu) {
|
|
||||||
+ clk = krait_add_clks(dev, cpu, id->data);
|
|
||||||
+ if (IS_ERR(clk))
|
|
||||||
+ return PTR_ERR(clk);
|
|
||||||
+ clks[cpu] = clk;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
|
|
||||||
+ if (IS_ERR(l2_pri_mux_clk))
|
|
||||||
+ return PTR_ERR(l2_pri_mux_clk);
|
|
||||||
+ clks[4] = l2_pri_mux_clk;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * We don't want the CPU or L2 clocks to be turned off at late init
|
|
||||||
+ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
|
|
||||||
+ * refcount of these clocks. Any cpufreq/hotplug manager can assume
|
|
||||||
+ * that the clocks have already been prepared and enabled by the time
|
|
||||||
+ * they take over.
|
|
||||||
+ */
|
|
||||||
+ for_each_online_cpu(cpu) {
|
|
||||||
+ clk_prepare_enable(l2_pri_mux_clk);
|
|
||||||
+ WARN(clk_prepare_enable(clks[cpu]),
|
|
||||||
+ "Unable to turn on CPU%d clock", cpu);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Force reinit of HFPLLs and muxes to overwrite any potential
|
|
||||||
+ * incorrect configuration of HFPLLs and muxes by the bootloader.
|
|
||||||
+ * While at it, also make sure the cores are running at known rates
|
|
||||||
+ * and print the current rate.
|
|
||||||
+ *
|
|
||||||
+ * The clocks are set to aux clock rate first to make sure the
|
|
||||||
+ * secondary mux is not sourcing off of QSB. The rate is then set to
|
|
||||||
+ * two different rates to force a HFPLL reinit under all
|
|
||||||
+ * circumstances.
|
|
||||||
+ */
|
|
||||||
+ cur_rate = clk_get_rate(l2_pri_mux_clk);
|
|
||||||
+ aux_rate = 384000000;
|
|
||||||
+ if (cur_rate == 1) {
|
|
||||||
+ pr_info("L2 @ QSB rate. Forcing new rate.\n");
|
|
||||||
+ cur_rate = aux_rate;
|
|
||||||
+ }
|
|
||||||
+ clk_set_rate(l2_pri_mux_clk, aux_rate);
|
|
||||||
+ clk_set_rate(l2_pri_mux_clk, 2);
|
|
||||||
+ clk_set_rate(l2_pri_mux_clk, cur_rate);
|
|
||||||
+ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
|
|
||||||
+ for_each_possible_cpu(cpu) {
|
|
||||||
+ clk = clks[cpu];
|
|
||||||
+ cur_rate = clk_get_rate(clk);
|
|
||||||
+ if (cur_rate == 1) {
|
|
||||||
+ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
|
|
||||||
+ cur_rate = aux_rate;
|
|
||||||
+ }
|
|
||||||
+ clk_set_rate(clk, aux_rate);
|
|
||||||
+ clk_set_rate(clk, 2);
|
|
||||||
+ clk_set_rate(clk, cur_rate);
|
|
||||||
+ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ of_clk_add_provider(dev->of_node, krait_of_get, clks);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver krait_cc_driver = {
|
|
||||||
+ .probe = krait_cc_probe,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "krait-cc",
|
|
||||||
+ .of_match_table = krait_cc_match_table,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+module_platform_driver(krait_cc_driver);
|
|
||||||
+
|
|
||||||
+MODULE_DESCRIPTION("Krait CPU Clock Driver");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
+MODULE_ALIAS("platform:krait-cc");
|
|
|
@ -1,304 +0,0 @@
|
||||||
Content-Type: text/plain; charset="utf-8"
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
X-Patchwork-Id: 6063191
|
|
||||||
Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org>
|
|
||||||
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
|
||||||
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
|
|
||||||
Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
|
|
||||||
Date: Fri, 20 Mar 2015 23:45:31 -0700
|
|
||||||
|
|
||||||
Register a cpufreq-generic device whenever we detect that a
|
|
||||||
"qcom,krait" compatible CPU is present in DT.
|
|
||||||
|
|
||||||
Cc: <devicetree@vger.kernel.org>
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++
|
|
||||||
drivers/cpufreq/Kconfig.arm | 9 +
|
|
||||||
drivers/cpufreq/Makefile | 1 +
|
|
||||||
drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++
|
|
||||||
4 files changed, 252 insertions(+)
|
|
||||||
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
|
|
||||||
create mode 100644 drivers/cpufreq/qcom-cpufreq.c
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
|
|
||||||
@@ -0,0 +1,38 @@
|
|
||||||
+Qualcomm Process Voltage Scaling Tables
|
|
||||||
+
|
|
||||||
+The node name is required to be "qcom,pvs". There shall only be one
|
|
||||||
+such node present in the root of the tree.
|
|
||||||
+
|
|
||||||
+PROPERTIES
|
|
||||||
+
|
|
||||||
+- qcom,pvs-format-a or qcom,pvs-format-b:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <empty>
|
|
||||||
+ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
|
|
||||||
+ If qcom,pvs-format-a is used the table is two columns
|
|
||||||
+ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage,
|
|
||||||
+ and current in that order).
|
|
||||||
+
|
|
||||||
+- qcom,speedX-pvsY-bin-vZ:
|
|
||||||
+ Usage: required
|
|
||||||
+ Value type: <prop-encoded-array>
|
|
||||||
+ Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
|
|
||||||
+ and version Z.
|
|
||||||
+Example:
|
|
||||||
+
|
|
||||||
+ qcom,pvs {
|
|
||||||
+ qcom,pvs-format-a;
|
|
||||||
+ qcom,speed0-pvs0-bin-v0 =
|
|
||||||
+ < 384000000 950000 >,
|
|
||||||
+ < 486000000 975000 >,
|
|
||||||
+ < 594000000 1000000 >,
|
|
||||||
+ < 702000000 1025000 >,
|
|
||||||
+ < 810000000 1075000 >,
|
|
||||||
+ < 918000000 1100000 >,
|
|
||||||
+ < 1026000000 1125000 >,
|
|
||||||
+ < 1134000000 1175000 >,
|
|
||||||
+ < 1242000000 1200000 >,
|
|
||||||
+ < 1350000000 1225000 >,
|
|
||||||
+ < 1458000000 1237500 >,
|
|
||||||
+ < 1512000000 1250000 >;
|
|
||||||
+ };
|
|
||||||
--- a/drivers/cpufreq/Kconfig.arm
|
|
||||||
+++ b/drivers/cpufreq/Kconfig.arm
|
|
||||||
@@ -129,6 +129,15 @@ config ARM_OMAP2PLUS_CPUFREQ
|
|
||||||
depends on ARCH_OMAP2PLUS
|
|
||||||
default ARCH_OMAP2PLUS
|
|
||||||
|
|
||||||
+config ARM_QCOM_CPUFREQ
|
|
||||||
+ tristate "Qualcomm based"
|
|
||||||
+ depends on ARCH_QCOM
|
|
||||||
+ select PM_OPP
|
|
||||||
+ help
|
|
||||||
+ This adds the CPUFreq driver for Qualcomm SoC based boards.
|
|
||||||
+
|
|
||||||
+ If in doubt, say N.
|
|
||||||
+
|
|
||||||
config ARM_S3C_CPUFREQ
|
|
||||||
bool
|
|
||||||
help
|
|
||||||
--- a/drivers/cpufreq/Makefile
|
|
||||||
+++ b/drivers/cpufreq/Makefile
|
|
||||||
@@ -64,6 +64,7 @@ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += o
|
|
||||||
obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o
|
|
||||||
obj-$(CONFIG_PXA27x) += pxa2xx-cpufreq.o
|
|
||||||
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
|
|
||||||
+obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o
|
|
||||||
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
|
|
||||||
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
|
|
||||||
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/cpufreq/qcom-cpufreq.c
|
|
||||||
@@ -0,0 +1,204 @@
|
|
||||||
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 and
|
|
||||||
+ * only version 2 as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/cpu.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/of.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/pm_opp.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+#include <linux/cpufreq-dt.h>
|
|
||||||
+
|
|
||||||
+static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
|
|
||||||
+{
|
|
||||||
+ void __iomem *base;
|
|
||||||
+ u32 pte_efuse;
|
|
||||||
+
|
|
||||||
+ *speed = *pvs = *pvs_ver = 0;
|
|
||||||
+
|
|
||||||
+ base = ioremap(0x007000c0, 4);
|
|
||||||
+ if (!base) {
|
|
||||||
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pte_efuse = readl_relaxed(base);
|
|
||||||
+ iounmap(base);
|
|
||||||
+
|
|
||||||
+ *speed = pte_efuse & 0xf;
|
|
||||||
+ if (*speed == 0xf)
|
|
||||||
+ *speed = (pte_efuse >> 4) & 0xf;
|
|
||||||
+
|
|
||||||
+ if (*speed == 0xf) {
|
|
||||||
+ *speed = 0;
|
|
||||||
+ pr_warn("Speed bin: Defaulting to %d\n", *speed);
|
|
||||||
+ } else {
|
|
||||||
+ pr_info("Speed bin: %d\n", *speed);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ *pvs = (pte_efuse >> 10) & 0x7;
|
|
||||||
+ if (*pvs == 0x7)
|
|
||||||
+ *pvs = (pte_efuse >> 13) & 0x7;
|
|
||||||
+
|
|
||||||
+ if (*pvs == 0x7) {
|
|
||||||
+ *pvs = 0;
|
|
||||||
+ pr_warn("PVS bin: Defaulting to %d\n", *pvs);
|
|
||||||
+ } else {
|
|
||||||
+ pr_info("PVS bin: %d\n", *pvs);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
|
|
||||||
+{
|
|
||||||
+ u32 pte_efuse, redundant_sel;
|
|
||||||
+ void __iomem *base;
|
|
||||||
+
|
|
||||||
+ *speed = 0;
|
|
||||||
+ *pvs = 0;
|
|
||||||
+ *pvs_ver = 0;
|
|
||||||
+
|
|
||||||
+ base = ioremap(0xfc4b80b0, 8);
|
|
||||||
+ if (!base) {
|
|
||||||
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pte_efuse = readl_relaxed(base);
|
|
||||||
+ redundant_sel = (pte_efuse >> 24) & 0x7;
|
|
||||||
+ *speed = pte_efuse & 0x7;
|
|
||||||
+ /* 4 bits of PVS are in efuse register bits 31, 8-6. */
|
|
||||||
+ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
|
|
||||||
+ *pvs_ver = (pte_efuse >> 4) & 0x3;
|
|
||||||
+
|
|
||||||
+ switch (redundant_sel) {
|
|
||||||
+ case 1:
|
|
||||||
+ *speed = (pte_efuse >> 27) & 0xf;
|
|
||||||
+ break;
|
|
||||||
+ case 2:
|
|
||||||
+ *pvs = (pte_efuse >> 27) & 0xf;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check SPEED_BIN_BLOW_STATUS */
|
|
||||||
+ if (pte_efuse & BIT(3)) {
|
|
||||||
+ pr_info("Speed bin: %d\n", *speed);
|
|
||||||
+ } else {
|
|
||||||
+ pr_warn("Speed bin not set. Defaulting to 0!\n");
|
|
||||||
+ *speed = 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check PVS_BLOW_STATUS */
|
|
||||||
+ pte_efuse = readl_relaxed(base + 0x4) & BIT(21);
|
|
||||||
+ if (pte_efuse) {
|
|
||||||
+ pr_info("PVS bin: %d\n", *pvs);
|
|
||||||
+ } else {
|
|
||||||
+ pr_warn("PVS bin not set. Defaulting to 0!\n");
|
|
||||||
+ *pvs = 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pr_info("PVS version: %d\n", *pvs_ver);
|
|
||||||
+ iounmap(base);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int __init qcom_cpufreq_populate_opps(void)
|
|
||||||
+{
|
|
||||||
+ int len, rows, cols, i, k, speed, pvs, pvs_ver;
|
|
||||||
+ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
|
|
||||||
+ struct device_node *np;
|
|
||||||
+ struct device *dev;
|
|
||||||
+ int cpu = 0;
|
|
||||||
+
|
|
||||||
+ np = of_find_node_by_name(NULL, "qcom,pvs");
|
|
||||||
+ if (!np)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ if (of_property_read_bool(np, "qcom,pvs-format-a")) {
|
|
||||||
+ get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
|
|
||||||
+ cols = 2;
|
|
||||||
+ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) {
|
|
||||||
+ get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
|
|
||||||
+ cols = 3;
|
|
||||||
+ } else {
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ snprintf(table_name, sizeof(table_name),
|
|
||||||
+ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
|
|
||||||
+
|
|
||||||
+ if (!of_find_property(np, table_name, &len))
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ len /= sizeof(u32);
|
|
||||||
+ if (len % cols || len == 0)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ rows = len / cols;
|
|
||||||
+
|
|
||||||
+ for (i = 0, k = 0; i < rows; i++) {
|
|
||||||
+ u32 freq, volt;
|
|
||||||
+
|
|
||||||
+ of_property_read_u32_index(np, table_name, k++, &freq);
|
|
||||||
+ of_property_read_u32_index(np, table_name, k++, &volt);
|
|
||||||
+ while (k % cols)
|
|
||||||
+ k++; /* Skip uA entries if present */
|
|
||||||
+ for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
|
|
||||||
+ dev = get_cpu_device(cpu);
|
|
||||||
+ if (!dev)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ if (dev_pm_opp_add(dev, freq, volt))
|
|
||||||
+ pr_warn("failed to add OPP %u\n", freq);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int __init qcom_cpufreq_driver_init(void)
|
|
||||||
+{
|
|
||||||
+ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
|
|
||||||
+ struct platform_device_info devinfo = {
|
|
||||||
+ .name = "cpufreq-dt",
|
|
||||||
+ .data = &pdata,
|
|
||||||
+ .size_data = sizeof(pdata),
|
|
||||||
+ };
|
|
||||||
+ struct device *cpu_dev;
|
|
||||||
+ struct device_node *np;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ cpu_dev = get_cpu_device(0);
|
|
||||||
+ if (!cpu_dev)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ np = of_node_get(cpu_dev->of_node);
|
|
||||||
+ if (!np)
|
|
||||||
+ return -ENOENT;
|
|
||||||
+
|
|
||||||
+ if (!of_device_is_compatible(np, "qcom,krait")) {
|
|
||||||
+ of_node_put(np);
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+ of_node_put(np);
|
|
||||||
+
|
|
||||||
+ ret = qcom_cpufreq_populate_opps();
|
|
||||||
+ if (ret)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
|
|
||||||
+}
|
|
||||||
+module_init(qcom_cpufreq_driver_init);
|
|
||||||
+
|
|
||||||
+MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
|
@ -1,100 +0,0 @@
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -24,6 +24,11 @@
|
|
||||||
next-level-cache = <&L2>;
|
|
||||||
qcom,acc = <&acc0>;
|
|
||||||
qcom,saw = <&saw0>;
|
|
||||||
+ clocks = <&kraitcc 0>;
|
|
||||||
+ clock-names = "cpu";
|
|
||||||
+ clock-latency = <100000>;
|
|
||||||
+ core-supply = <&smb208_s2a>;
|
|
||||||
+ voltage-tolerance = <5>;
|
|
||||||
};
|
|
||||||
|
|
||||||
cpu@1 {
|
|
||||||
@@ -34,11 +39,24 @@
|
|
||||||
next-level-cache = <&L2>;
|
|
||||||
qcom,acc = <&acc1>;
|
|
||||||
qcom,saw = <&saw1>;
|
|
||||||
+ clocks = <&kraitcc 1>;
|
|
||||||
+ clock-names = "cpu";
|
|
||||||
+ clock-latency = <100000>;
|
|
||||||
+ core-supply = <&smb208_s2b>;
|
|
||||||
};
|
|
||||||
|
|
||||||
L2: l2-cache {
|
|
||||||
compatible = "cache";
|
|
||||||
cache-level = <2>;
|
|
||||||
+ clocks = <&kraitcc 4>;
|
|
||||||
+ clock-names = "cache";
|
|
||||||
+ cache-points-kHz = <
|
|
||||||
+ /* kHz uV CPU kHz */
|
|
||||||
+ 1200000 1150000 1200000
|
|
||||||
+ 1000000 1100000 600000
|
|
||||||
+ 384000 1100000 384000
|
|
||||||
+ >;
|
|
||||||
+ vdd_dig-supply = <&smb208_s1a>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -71,6 +89,46 @@
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
+ kraitcc: clock-controller {
|
|
||||||
+ compatible = "qcom,krait-cc-v1";
|
|
||||||
+ #clock-cells = <1>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ qcom,pvs {
|
|
||||||
+ qcom,pvs-format-a;
|
|
||||||
+ qcom,speed0-pvs0-bin-v0 =
|
|
||||||
+ < 1400000000 1250000 >,
|
|
||||||
+ < 1200000000 1200000 >,
|
|
||||||
+ < 1000000000 1150000 >,
|
|
||||||
+ < 800000000 1100000 >,
|
|
||||||
+ < 600000000 1050000 >,
|
|
||||||
+ < 384000000 1000000 >;
|
|
||||||
+
|
|
||||||
+ qcom,speed0-pvs1-bin-v0 =
|
|
||||||
+ < 1400000000 1175000 >,
|
|
||||||
+ < 1200000000 1125000 >,
|
|
||||||
+ < 1000000000 1075000 >,
|
|
||||||
+ < 800000000 1025000 >,
|
|
||||||
+ < 600000000 975000 >,
|
|
||||||
+ < 384000000 925000 >;
|
|
||||||
+
|
|
||||||
+ qcom,speed0-pvs2-bin-v0 =
|
|
||||||
+ < 1400000000 1125000 >,
|
|
||||||
+ < 1200000000 1075000 >,
|
|
||||||
+ < 1000000000 1025000 >,
|
|
||||||
+ < 800000000 995000 >,
|
|
||||||
+ < 600000000 925000 >,
|
|
||||||
+ < 384000000 875000 >;
|
|
||||||
+
|
|
||||||
+ qcom,speed0-pvs3-bin-v0 =
|
|
||||||
+ < 1400000000 1050000 >,
|
|
||||||
+ < 1200000000 1000000 >,
|
|
||||||
+ < 1000000000 950000 >,
|
|
||||||
+ < 800000000 900000 >,
|
|
||||||
+ < 600000000 850000 >,
|
|
||||||
+ < 384000000 800000 >;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
soc: soc {
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <1>;
|
|
||||||
@@ -171,11 +229,13 @@
|
|
||||||
acc0: clock-controller@2088000 {
|
|
||||||
compatible = "qcom,kpss-acc-v1";
|
|
||||||
reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
|
|
||||||
+ clock-output-names = "acpu0_aux";
|
|
||||||
};
|
|
||||||
|
|
||||||
acc1: clock-controller@2098000 {
|
|
||||||
compatible = "qcom,kpss-acc-v1";
|
|
||||||
reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
|
|
||||||
+ clock-output-names = "acpu1_aux";
|
|
||||||
};
|
|
||||||
|
|
||||||
l2cc: clock-controller@2011000 {
|
|
|
@ -1,461 +0,0 @@
|
||||||
From dd77db4143290689d3a5e1ec61627233d0711b66 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Date: Fri, 30 May 2014 16:36:11 -0700
|
|
||||||
Subject: [PATCH] FROMLIST: cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
|
|
||||||
|
|
||||||
Krait processors have individual clocks for each CPU that can
|
|
||||||
scale independently from one another. cpufreq-cpu0 is fairly
|
|
||||||
close to this, but assumes that there is only one clock for all
|
|
||||||
CPUs. Add a driver to support the Krait configuration.
|
|
||||||
|
|
||||||
TODO: Merge into cpufreq-cpu0? Or make generic?
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
|
|
||||||
---
|
|
||||||
drivers/cpufreq/Kconfig | 13 +++
|
|
||||||
drivers/cpufreq/Makefile | 1 +
|
|
||||||
drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 204 insertions(+)
|
|
||||||
create mode 100644 drivers/cpufreq/cpufreq-krait.c
|
|
||||||
|
|
||||||
--- a/drivers/cpufreq/Kconfig
|
|
||||||
+++ b/drivers/cpufreq/Kconfig
|
|
||||||
@@ -196,6 +196,19 @@ config CPUFREQ_DT
|
|
||||||
|
|
||||||
If in doubt, say N.
|
|
||||||
|
|
||||||
+config GENERIC_CPUFREQ_KRAIT
|
|
||||||
+ tristate "Krait cpufreq driver"
|
|
||||||
+ depends on HAVE_CLK && OF
|
|
||||||
+ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y:
|
|
||||||
+ depends on !CPU_THERMAL || THERMAL
|
|
||||||
+ select PM_OPP
|
|
||||||
+ help
|
|
||||||
+ This adds a generic cpufreq driver for CPU0 frequency management.
|
|
||||||
+ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
|
|
||||||
+ systems which share clock and voltage across all CPUs.
|
|
||||||
+
|
|
||||||
+ If in doubt, say N.
|
|
||||||
+
|
|
||||||
menu "x86 CPU frequency scaling drivers"
|
|
||||||
depends on X86
|
|
||||||
source "drivers/cpufreq/Kconfig.x86"
|
|
||||||
--- a/drivers/cpufreq/Makefile
|
|
||||||
+++ b/drivers/cpufreq/Makefile
|
|
||||||
@@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE)
|
|
||||||
obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
|
|
||||||
+obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o
|
|
||||||
|
|
||||||
##################################################################################
|
|
||||||
# x86 drivers.
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/cpufreq/cpufreq-krait.c
|
|
||||||
@@ -0,0 +1,390 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
|
|
||||||
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * The OPP code in function krait_set_target() is reused from
|
|
||||||
+ * drivers/cpufreq/omap-cpufreq.c
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 as
|
|
||||||
+ * published by the Free Software Foundation.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/cpu.h>
|
|
||||||
+#include <linux/cpu_cooling.h>
|
|
||||||
+#include <linux/cpufreq.h>
|
|
||||||
+#include <linux/cpumask.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/of.h>
|
|
||||||
+#include <linux/pm_opp.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/regulator/consumer.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+#include <linux/thermal.h>
|
|
||||||
+
|
|
||||||
+static unsigned int transition_latency;
|
|
||||||
+static unsigned int voltage_tolerance; /* in percentage */
|
|
||||||
+
|
|
||||||
+static struct device *cpu_dev;
|
|
||||||
+static DEFINE_PER_CPU(struct clk *, krait_cpu_clks);
|
|
||||||
+static DEFINE_PER_CPU(struct regulator *, krait_supply_core);
|
|
||||||
+static struct cpufreq_frequency_table *freq_table;
|
|
||||||
+static struct thermal_cooling_device *cdev;
|
|
||||||
+
|
|
||||||
+struct cache_points {
|
|
||||||
+ unsigned long cache_freq;
|
|
||||||
+ unsigned int cache_volt;
|
|
||||||
+ unsigned long cpu_freq;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct regulator *krait_l2_reg;
|
|
||||||
+static struct clk *krait_l2_clk;
|
|
||||||
+static struct cache_points *krait_l2_points;
|
|
||||||
+static int nr_krait_l2_points;
|
|
||||||
+
|
|
||||||
+static int krait_parse_cache_points(struct device *dev,
|
|
||||||
+ struct device_node *of_node)
|
|
||||||
+{
|
|
||||||
+ const struct property *prop;
|
|
||||||
+ const __be32 *val;
|
|
||||||
+ int nr, i;
|
|
||||||
+
|
|
||||||
+ prop = of_find_property(of_node, "cache-points-kHz", NULL);
|
|
||||||
+ if (!prop)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ if (!prop->value)
|
|
||||||
+ return -ENODATA;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Each OPP is a set of tuples consisting of frequency and
|
|
||||||
+ * cpu-frequency like <freq-kHz volt-uV freq-kHz>.
|
|
||||||
+ */
|
|
||||||
+ nr = prop->length / sizeof(u32);
|
|
||||||
+ if (nr % 3) {
|
|
||||||
+ dev_err(dev, "%s: Invalid cache points\n", __func__);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+ nr /= 3;
|
|
||||||
+
|
|
||||||
+ krait_l2_points = devm_kcalloc(dev, nr, sizeof(*krait_l2_points),
|
|
||||||
+ GFP_KERNEL);
|
|
||||||
+ if (!krait_l2_points)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ nr_krait_l2_points = nr;
|
|
||||||
+
|
|
||||||
+ for (i = 0, val = prop->value; i < nr; i++) {
|
|
||||||
+ unsigned long cache_freq = be32_to_cpup(val++) * 1000;
|
|
||||||
+ unsigned int cache_volt = be32_to_cpup(val++);
|
|
||||||
+ unsigned long cpu_freq = be32_to_cpup(val++) * 1000;
|
|
||||||
+
|
|
||||||
+ krait_l2_points[i].cache_freq = cache_freq;
|
|
||||||
+ krait_l2_points[i].cache_volt = cache_volt;
|
|
||||||
+ krait_l2_points[i].cpu_freq = cpu_freq;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int krait_set_target(struct cpufreq_policy *policy, unsigned int index)
|
|
||||||
+{
|
|
||||||
+ struct dev_pm_opp *opp;
|
|
||||||
+ unsigned long volt = 0, volt_old = 0, tol = 0;
|
|
||||||
+ unsigned long freq, max_cpu_freq = 0;
|
|
||||||
+ unsigned int old_freq, new_freq;
|
|
||||||
+ long freq_Hz, freq_exact;
|
|
||||||
+ int ret, i;
|
|
||||||
+ struct clk *cpu_clk;
|
|
||||||
+ struct regulator *core;
|
|
||||||
+ unsigned int cpu;
|
|
||||||
+
|
|
||||||
+ cpu_clk = per_cpu(krait_cpu_clks, policy->cpu);
|
|
||||||
+
|
|
||||||
+ freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
|
|
||||||
+ if (freq_Hz <= 0)
|
|
||||||
+ freq_Hz = freq_table[index].frequency * 1000;
|
|
||||||
+
|
|
||||||
+ freq_exact = freq_Hz;
|
|
||||||
+ new_freq = freq_Hz / 1000;
|
|
||||||
+ old_freq = clk_get_rate(cpu_clk) / 1000;
|
|
||||||
+
|
|
||||||
+ core = per_cpu(krait_supply_core, policy->cpu);
|
|
||||||
+
|
|
||||||
+ rcu_read_lock();
|
|
||||||
+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
|
|
||||||
+ if (IS_ERR(opp)) {
|
|
||||||
+ rcu_read_unlock();
|
|
||||||
+ pr_err("failed to find OPP for %ld\n", freq_Hz);
|
|
||||||
+ return PTR_ERR(opp);
|
|
||||||
+ }
|
|
||||||
+ volt = dev_pm_opp_get_voltage(opp);
|
|
||||||
+ rcu_read_unlock();
|
|
||||||
+ tol = volt * voltage_tolerance / 100;
|
|
||||||
+ volt_old = regulator_get_voltage(core);
|
|
||||||
+
|
|
||||||
+ pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
|
|
||||||
+ old_freq / 1000, volt_old ? volt_old / 1000 : -1,
|
|
||||||
+ new_freq / 1000, volt ? volt / 1000 : -1);
|
|
||||||
+
|
|
||||||
+ /* scaling up? scale voltage before frequency */
|
|
||||||
+ if (new_freq > old_freq) {
|
|
||||||
+ ret = regulator_set_voltage_tol(core, volt, tol);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed to scale voltage up: %d\n", ret);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_set_rate(cpu_clk, freq_exact);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed to set clock rate: %d\n", ret);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* scaling down? scale voltage after frequency */
|
|
||||||
+ if (new_freq < old_freq) {
|
|
||||||
+ ret = regulator_set_voltage_tol(core, volt, tol);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed to scale voltage down: %d\n", ret);
|
|
||||||
+ clk_set_rate(cpu_clk, old_freq * 1000);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for_each_possible_cpu(cpu) {
|
|
||||||
+ freq = clk_get_rate(per_cpu(krait_cpu_clks, cpu));
|
|
||||||
+ max_cpu_freq = max(max_cpu_freq, freq);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < nr_krait_l2_points; i++) {
|
|
||||||
+ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) {
|
|
||||||
+ if (krait_l2_reg) {
|
|
||||||
+ ret = regulator_set_voltage_tol(krait_l2_reg,
|
|
||||||
+ krait_l2_points[i].cache_volt,
|
|
||||||
+ tol);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed to scale l2 voltage: %d\n",
|
|
||||||
+ ret);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ ret = clk_set_rate(krait_l2_clk,
|
|
||||||
+ krait_l2_points[i].cache_freq);
|
|
||||||
+ if (ret)
|
|
||||||
+ pr_err("failed to scale l2 clk: %d\n", ret);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int krait_cpufreq_init(struct cpufreq_policy *policy)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ policy->clk = per_cpu(krait_cpu_clks, policy->cpu);
|
|
||||||
+
|
|
||||||
+ ret = cpufreq_table_validate_and_show(policy, freq_table);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("%s: invalid frequency table: %d\n", __func__, ret);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ policy->cpuinfo.transition_latency = transition_latency;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct cpufreq_driver krait_cpufreq_driver = {
|
|
||||||
+ .flags = CPUFREQ_STICKY,
|
|
||||||
+ .verify = cpufreq_generic_frequency_table_verify,
|
|
||||||
+ .target_index = krait_set_target,
|
|
||||||
+ .get = cpufreq_generic_get,
|
|
||||||
+ .init = krait_cpufreq_init,
|
|
||||||
+ .name = "generic_krait",
|
|
||||||
+ .attr = cpufreq_generic_attr,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int krait_cpufreq_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct device_node *np, *cache;
|
|
||||||
+ int ret, i;
|
|
||||||
+ unsigned int cpu;
|
|
||||||
+ struct device *dev;
|
|
||||||
+ struct clk *clk;
|
|
||||||
+ struct regulator *core;
|
|
||||||
+ unsigned long freq_Hz, freq, max_cpu_freq;
|
|
||||||
+ struct dev_pm_opp *opp;
|
|
||||||
+ unsigned long volt, tol;
|
|
||||||
+
|
|
||||||
+ cpu_dev = get_cpu_device(0);
|
|
||||||
+ if (!cpu_dev) {
|
|
||||||
+ pr_err("failed to get krait device\n");
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ np = of_node_get(cpu_dev->of_node);
|
|
||||||
+ if (!np) {
|
|
||||||
+ pr_err("failed to find krait node\n");
|
|
||||||
+ return -ENOENT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed to init cpufreq table: %d\n", ret);
|
|
||||||
+ goto out_put_node;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance);
|
|
||||||
+
|
|
||||||
+ if (of_property_read_u32(np, "clock-latency", &transition_latency))
|
|
||||||
+ transition_latency = CPUFREQ_ETERNAL;
|
|
||||||
+
|
|
||||||
+ cache = of_find_next_cache_node(np);
|
|
||||||
+ if (cache) {
|
|
||||||
+ struct device_node *vdd;
|
|
||||||
+
|
|
||||||
+ vdd = of_parse_phandle(cache, "vdd_dig-supply", 0);
|
|
||||||
+ if (vdd) {
|
|
||||||
+ krait_l2_reg = regulator_get(NULL, vdd->name);
|
|
||||||
+ if (IS_ERR(krait_l2_reg)) {
|
|
||||||
+ pr_warn("failed to get l2 vdd_dig supply\n");
|
|
||||||
+ krait_l2_reg = NULL;
|
|
||||||
+ }
|
|
||||||
+ of_node_put(vdd);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ krait_l2_clk = of_clk_get(cache, 0);
|
|
||||||
+ if (!IS_ERR(krait_l2_clk)) {
|
|
||||||
+ ret = krait_parse_cache_points(&pdev->dev, cache);
|
|
||||||
+ if (ret)
|
|
||||||
+ clk_put(krait_l2_clk);
|
|
||||||
+ }
|
|
||||||
+ if (IS_ERR(krait_l2_clk) || ret)
|
|
||||||
+ krait_l2_clk = NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for_each_possible_cpu(cpu) {
|
|
||||||
+ dev = get_cpu_device(cpu);
|
|
||||||
+ if (!dev) {
|
|
||||||
+ pr_err("failed to get krait device\n");
|
|
||||||
+ ret = -ENOENT;
|
|
||||||
+ goto out_free_table;
|
|
||||||
+ }
|
|
||||||
+ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL);
|
|
||||||
+ if (IS_ERR(clk)) {
|
|
||||||
+ ret = PTR_ERR(clk);
|
|
||||||
+ goto out_free_table;
|
|
||||||
+ }
|
|
||||||
+ core = devm_regulator_get(dev, "core");
|
|
||||||
+ if (IS_ERR(core)) {
|
|
||||||
+ pr_debug("failed to get core regulator\n");
|
|
||||||
+ ret = PTR_ERR(core);
|
|
||||||
+ goto out_free_table;
|
|
||||||
+ }
|
|
||||||
+ per_cpu(krait_supply_core, cpu) = core;
|
|
||||||
+
|
|
||||||
+ freq_Hz = clk_get_rate(clk);
|
|
||||||
+
|
|
||||||
+ rcu_read_lock();
|
|
||||||
+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
|
|
||||||
+ if (IS_ERR(opp)) {
|
|
||||||
+ rcu_read_unlock();
|
|
||||||
+ pr_err("failed to find OPP for %ld\n", freq_Hz);
|
|
||||||
+ ret = PTR_ERR(opp);
|
|
||||||
+ goto out_free_table;
|
|
||||||
+ }
|
|
||||||
+ volt = dev_pm_opp_get_voltage(opp);
|
|
||||||
+ rcu_read_unlock();
|
|
||||||
+
|
|
||||||
+ tol = volt * voltage_tolerance / 100;
|
|
||||||
+ ret = regulator_set_voltage_tol(core, volt, tol);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed to scale voltage up: %d\n", ret);
|
|
||||||
+ goto out_free_table;
|
|
||||||
+ }
|
|
||||||
+ ret = regulator_enable(core);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed to enable regulator: %d\n", ret);
|
|
||||||
+ goto out_free_table;
|
|
||||||
+ }
|
|
||||||
+ max_cpu_freq = max(max_cpu_freq, freq);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < nr_krait_l2_points; i++) {
|
|
||||||
+ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) {
|
|
||||||
+ if (krait_l2_reg) {
|
|
||||||
+ ret = regulator_set_voltage_tol(krait_l2_reg,
|
|
||||||
+ krait_l2_points[i].cache_volt,
|
|
||||||
+ tol);
|
|
||||||
+ if (ret)
|
|
||||||
+ pr_err("failed to scale l2 voltage: %d\n",
|
|
||||||
+ ret);
|
|
||||||
+ ret = regulator_enable(krait_l2_reg);
|
|
||||||
+ if (ret)
|
|
||||||
+ pr_err("failed to enable l2 voltage: %d\n",
|
|
||||||
+ ret);
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = cpufreq_register_driver(&krait_cpufreq_driver);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("failed register driver: %d\n", ret);
|
|
||||||
+ goto out_free_table;
|
|
||||||
+ }
|
|
||||||
+ of_node_put(np);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * For now, just loading the cooling device;
|
|
||||||
+ * thermal DT code takes care of matching them.
|
|
||||||
+ */
|
|
||||||
+ for_each_possible_cpu(cpu) {
|
|
||||||
+ dev = get_cpu_device(cpu);
|
|
||||||
+ np = of_node_get(dev->of_node);
|
|
||||||
+ if (of_find_property(np, "#cooling-cells", NULL)) {
|
|
||||||
+ cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu));
|
|
||||||
+ if (IS_ERR(cdev))
|
|
||||||
+ pr_err("running cpufreq without cooling device: %ld\n",
|
|
||||||
+ PTR_ERR(cdev));
|
|
||||||
+ }
|
|
||||||
+ of_node_put(np);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+out_free_table:
|
|
||||||
+ regulator_put(krait_l2_reg);
|
|
||||||
+ clk_put(krait_l2_clk);
|
|
||||||
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
|
||||||
+out_put_node:
|
|
||||||
+ of_node_put(np);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int krait_cpufreq_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ cpufreq_cooling_unregister(cdev);
|
|
||||||
+ cpufreq_unregister_driver(&krait_cpufreq_driver);
|
|
||||||
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
|
||||||
+ clk_put(krait_l2_clk);
|
|
||||||
+ regulator_put(krait_l2_reg);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver krait_cpufreq_platdrv = {
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "cpufreq-krait",
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ },
|
|
||||||
+ .probe = krait_cpufreq_probe,
|
|
||||||
+ .remove = krait_cpufreq_remove,
|
|
||||||
+};
|
|
||||||
+module_platform_driver(krait_cpufreq_platdrv);
|
|
||||||
+
|
|
||||||
+MODULE_DESCRIPTION("Krait CPUfreq driver");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
--- a/drivers/cpufreq/qcom-cpufreq.c
|
|
||||||
+++ b/drivers/cpufreq/qcom-cpufreq.c
|
|
||||||
@@ -168,11 +168,8 @@ static int __init qcom_cpufreq_populate_
|
|
||||||
|
|
||||||
static int __init qcom_cpufreq_driver_init(void)
|
|
||||||
{
|
|
||||||
- struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
|
|
||||||
struct platform_device_info devinfo = {
|
|
||||||
- .name = "cpufreq-dt",
|
|
||||||
- .data = &pdata,
|
|
||||||
- .size_data = sizeof(pdata),
|
|
||||||
+ .name = "cpufreq-krait",
|
|
||||||
};
|
|
||||||
struct device *cpu_dev;
|
|
||||||
struct device_node *np;
|
|
|
@ -1,62 +0,0 @@
|
||||||
From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Wed, 29 Apr 2015 15:21:46 -0700
|
|
||||||
Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom
|
|
||||||
|
|
||||||
ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended
|
|
||||||
on most ARM architectures. This automatically calculate ZRELADDR by
|
|
||||||
masking PHYS_OFFSET with 0xf8000000.
|
|
||||||
|
|
||||||
However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware
|
|
||||||
network accelerators, and the bootloader removes this section from the
|
|
||||||
layout passed from the ATAGS (when used).
|
|
||||||
|
|
||||||
For newer bootloader, when DT is used, this is not a problem, we just
|
|
||||||
reserve this memory in the device tree. But if the bootloader doesn't
|
|
||||||
have DT support, then ATAGS have to be used. In this case, the ARM
|
|
||||||
decompressor will position the kernel in this low mem, which will not be
|
|
||||||
in the RAM section mapped by the bootloader, which means the kernel will
|
|
||||||
freeze in the middle of the boot process trying to map the memory.
|
|
||||||
|
|
||||||
As a work around, this patch allows disabling AUTO_ZRELADDR when
|
|
||||||
ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders
|
|
||||||
which don't support device-tree, which is the case on certain early
|
|
||||||
IPQ806x based designs.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/Kconfig | 2 +-
|
|
||||||
arch/arm/Makefile | 2 ++
|
|
||||||
arch/arm/mach-qcom/Makefile.boot | 1 +
|
|
||||||
3 files changed, 4 insertions(+), 1 deletion(-)
|
|
||||||
create mode 100644 arch/arm/mach-qcom/Makefile.boot
|
|
||||||
|
|
||||||
--- a/arch/arm/Kconfig
|
|
||||||
+++ b/arch/arm/Kconfig
|
|
||||||
@@ -311,7 +311,7 @@ config ARCH_MULTIPLATFORM
|
|
||||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
|
||||||
select ARM_HAS_SG_CHAIN
|
|
||||||
select ARM_PATCH_PHYS_VIRT
|
|
||||||
- select AUTO_ZRELADDR
|
|
||||||
+ select AUTO_ZRELADDR if !ARCH_QCOM
|
|
||||||
select CLKSRC_OF
|
|
||||||
select COMMON_CLK
|
|
||||||
select GENERIC_CLOCKEVENTS
|
|
||||||
--- a/arch/arm/Makefile
|
|
||||||
+++ b/arch/arm/Makefile
|
|
||||||
@@ -240,9 +240,11 @@ MACHINE := arch/arm/mach-$(word 1,$(mac
|
|
||||||
else
|
|
||||||
MACHINE :=
|
|
||||||
endif
|
|
||||||
+ifeq ($(CONFIG_ARCH_QCOM),)
|
|
||||||
ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y)
|
|
||||||
MACHINE :=
|
|
||||||
endif
|
|
||||||
+endif
|
|
||||||
|
|
||||||
machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y))
|
|
||||||
platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y)))
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/arch/arm/mach-qcom/Makefile.boot
|
|
||||||
@@ -0,0 +1 @@
|
|
||||||
+zreladdr-y+= 0x42208000
|
|
|
@ -1,733 +0,0 @@
|
||||||
From 2fbb18f85826a9ba308fedb2cf90d3a661a39fd7 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
Date: Fri, 27 Mar 2015 00:16:14 -0700
|
|
||||||
Subject: [PATCH] clk: qcom: Add support for NSS/GMAC clocks and resets
|
|
||||||
|
|
||||||
Add the NSS/GMAC clocks and the TCM clock and NSS resets.
|
|
||||||
|
|
||||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/clk/qcom/gcc-ipq806x.c | 594 ++++++++++++++++++++++++++-
|
|
||||||
drivers/clk/qcom/gcc-ipq806x.c.rej | 50 +++
|
|
||||||
include/dt-bindings/clock/qcom,gcc-ipq806x.h | 2 +
|
|
||||||
include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++
|
|
||||||
4 files changed, 688 insertions(+), 1 deletion(-)
|
|
||||||
create mode 100644 drivers/clk/qcom/gcc-ipq806x.c.rej
|
|
||||||
|
|
||||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
|
||||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
|
||||||
@@ -209,11 +209,46 @@ static struct clk_regmap pll14_vote = {
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
+#define NSS_PLL_RATE(f, _l, _m, _n, i) \
|
|
||||||
+ { \
|
|
||||||
+ .freq = f, \
|
|
||||||
+ .l = _l, \
|
|
||||||
+ .m = _m, \
|
|
||||||
+ .n = _n, \
|
|
||||||
+ .ibits = i, \
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+static struct pll_freq_tbl pll18_freq_tbl[] = {
|
|
||||||
+ NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625),
|
|
||||||
+ NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625),
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_pll pll18 = {
|
|
||||||
+ .l_reg = 0x31a4,
|
|
||||||
+ .m_reg = 0x31a8,
|
|
||||||
+ .n_reg = 0x31ac,
|
|
||||||
+ .config_reg = 0x31b4,
|
|
||||||
+ .mode_reg = 0x31a0,
|
|
||||||
+ .status_reg = 0x31b8,
|
|
||||||
+ .status_bit = 16,
|
|
||||||
+ .post_div_shift = 16,
|
|
||||||
+ .post_div_width = 1,
|
|
||||||
+ .freq_tbl = pll18_freq_tbl,
|
|
||||||
+ .clkr.hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "pll18",
|
|
||||||
+ .parent_names = (const char *[]){ "pxo" },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &clk_pll_ops,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
#define P_PXO 0
|
|
||||||
#define P_PLL8 1
|
|
||||||
#define P_PLL3 1
|
|
||||||
#define P_PLL0 2
|
|
||||||
#define P_CXO 2
|
|
||||||
+#define P_PLL14 3
|
|
||||||
+#define P_PLL18 4
|
|
||||||
|
|
||||||
static const u8 gcc_pxo_pll8_map[] = {
|
|
||||||
[P_PXO] = 0,
|
|
||||||
@@ -264,6 +299,22 @@ static const char *gcc_pxo_pll8_pll0_map
|
|
||||||
"pll0_vote",
|
|
||||||
};
|
|
||||||
|
|
||||||
+static const u8 gcc_pxo_pll8_pll14_pll18_pll0_map[] = {
|
|
||||||
+ [P_PXO] = 0 ,
|
|
||||||
+ [P_PLL8] = 4,
|
|
||||||
+ [P_PLL0] = 2,
|
|
||||||
+ [P_PLL14] = 5,
|
|
||||||
+ [P_PLL18] = 1,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const char *gcc_pxo_pll8_pll14_pll18_pll0[] = {
|
|
||||||
+ "pxo",
|
|
||||||
+ "pll8_vote",
|
|
||||||
+ "pll0_vote",
|
|
||||||
+ "pll14",
|
|
||||||
+ "pll18",
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static struct freq_tbl clk_tbl_gsbi_uart[] = {
|
|
||||||
{ 1843200, P_PLL8, 2, 6, 625 },
|
|
||||||
{ 3686400, P_PLL8, 2, 12, 625 },
|
|
||||||
@@ -2239,6 +2290,472 @@ static struct clk_branch usb_fs1_h_clk =
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
+static const struct freq_tbl clk_tbl_gmac[] = {
|
|
||||||
+ { 133000000, P_PLL0, 1, 50, 301 },
|
|
||||||
+ { 266000000, P_PLL0, 1, 127, 382 },
|
|
||||||
+ { }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_dyn_rcg gmac_core1_src = {
|
|
||||||
+ .ns_reg[0] = 0x3cac,
|
|
||||||
+ .ns_reg[1] = 0x3cb0,
|
|
||||||
+ .md_reg[0] = 0x3ca4,
|
|
||||||
+ .md_reg[1] = 0x3ca8,
|
|
||||||
+ .bank_reg = 0x3ca0,
|
|
||||||
+ .mn[0] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .mn[1] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .s[0] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .s[1] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .p[0] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .p[1] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .mux_sel_bit = 0,
|
|
||||||
+ .freq_tbl = clk_tbl_gmac,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3ca0,
|
|
||||||
+ .enable_mask = BIT(1),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core1_src",
|
|
||||||
+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
|
|
||||||
+ .num_parents = 5,
|
|
||||||
+ .ops = &clk_dyn_rcg_ops,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_branch gmac_core1_clk = {
|
|
||||||
+ .halt_reg = 0x3c20,
|
|
||||||
+ .halt_bit = 4,
|
|
||||||
+ .hwcg_reg = 0x3cb4,
|
|
||||||
+ .hwcg_bit = 6,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3cb4,
|
|
||||||
+ .enable_mask = BIT(4),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core1_clk",
|
|
||||||
+ .parent_names = (const char *[]){
|
|
||||||
+ "gmac_core1_src",
|
|
||||||
+ },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &clk_branch_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_dyn_rcg gmac_core2_src = {
|
|
||||||
+ .ns_reg[0] = 0x3ccc,
|
|
||||||
+ .ns_reg[1] = 0x3cd0,
|
|
||||||
+ .md_reg[0] = 0x3cc4,
|
|
||||||
+ .md_reg[1] = 0x3cc8,
|
|
||||||
+ .bank_reg = 0x3ca0,
|
|
||||||
+ .mn[0] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .mn[1] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .s[0] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .s[1] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .p[0] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .p[1] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .mux_sel_bit = 0,
|
|
||||||
+ .freq_tbl = clk_tbl_gmac,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3cc0,
|
|
||||||
+ .enable_mask = BIT(1),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core2_src",
|
|
||||||
+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
|
|
||||||
+ .num_parents = 5,
|
|
||||||
+ .ops = &clk_dyn_rcg_ops,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_branch gmac_core2_clk = {
|
|
||||||
+ .halt_reg = 0x3c20,
|
|
||||||
+ .halt_bit = 5,
|
|
||||||
+ .hwcg_reg = 0x3cd4,
|
|
||||||
+ .hwcg_bit = 6,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3cd4,
|
|
||||||
+ .enable_mask = BIT(4),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core2_clk",
|
|
||||||
+ .parent_names = (const char *[]){
|
|
||||||
+ "gmac_core2_src",
|
|
||||||
+ },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &clk_branch_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_dyn_rcg gmac_core3_src = {
|
|
||||||
+ .ns_reg[0] = 0x3cec,
|
|
||||||
+ .ns_reg[1] = 0x3cf0,
|
|
||||||
+ .md_reg[0] = 0x3ce4,
|
|
||||||
+ .md_reg[1] = 0x3ce8,
|
|
||||||
+ .bank_reg = 0x3ce0,
|
|
||||||
+ .mn[0] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .mn[1] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .s[0] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .s[1] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .p[0] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .p[1] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .mux_sel_bit = 0,
|
|
||||||
+ .freq_tbl = clk_tbl_gmac,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3ce0,
|
|
||||||
+ .enable_mask = BIT(1),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core3_src",
|
|
||||||
+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
|
|
||||||
+ .num_parents = 5,
|
|
||||||
+ .ops = &clk_dyn_rcg_ops,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_branch gmac_core3_clk = {
|
|
||||||
+ .halt_reg = 0x3c20,
|
|
||||||
+ .halt_bit = 6,
|
|
||||||
+ .hwcg_reg = 0x3cf4,
|
|
||||||
+ .hwcg_bit = 6,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3cf4,
|
|
||||||
+ .enable_mask = BIT(4),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core3_clk",
|
|
||||||
+ .parent_names = (const char *[]){
|
|
||||||
+ "gmac_core3_src",
|
|
||||||
+ },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &clk_branch_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_dyn_rcg gmac_core4_src = {
|
|
||||||
+ .ns_reg[0] = 0x3d0c,
|
|
||||||
+ .ns_reg[1] = 0x3d10,
|
|
||||||
+ .md_reg[0] = 0x3d04,
|
|
||||||
+ .md_reg[1] = 0x3d08,
|
|
||||||
+ .bank_reg = 0x3d00,
|
|
||||||
+ .mn[0] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .mn[1] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .s[0] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .s[1] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .p[0] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .p[1] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .mux_sel_bit = 0,
|
|
||||||
+ .freq_tbl = clk_tbl_gmac,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3d00,
|
|
||||||
+ .enable_mask = BIT(1),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core4_src",
|
|
||||||
+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
|
|
||||||
+ .num_parents = 5,
|
|
||||||
+ .ops = &clk_dyn_rcg_ops,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_branch gmac_core4_clk = {
|
|
||||||
+ .halt_reg = 0x3c20,
|
|
||||||
+ .halt_bit = 7,
|
|
||||||
+ .hwcg_reg = 0x3d14,
|
|
||||||
+ .hwcg_bit = 6,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3d14,
|
|
||||||
+ .enable_mask = BIT(4),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "gmac_core4_clk",
|
|
||||||
+ .parent_names = (const char *[]){
|
|
||||||
+ "gmac_core4_src",
|
|
||||||
+ },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &clk_branch_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct freq_tbl clk_tbl_nss_tcm[] = {
|
|
||||||
+ { 266000000, P_PLL0, 3, 0, 0 },
|
|
||||||
+ { 400000000, P_PLL0, 2, 0, 0 },
|
|
||||||
+ { }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_dyn_rcg nss_tcm_src = {
|
|
||||||
+ .ns_reg[0] = 0x3dc4,
|
|
||||||
+ .ns_reg[1] = 0x3dc8,
|
|
||||||
+ .bank_reg = 0x3dc0,
|
|
||||||
+ .s[0] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .s[1] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .p[0] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 4,
|
|
||||||
+ },
|
|
||||||
+ .p[1] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 4,
|
|
||||||
+ },
|
|
||||||
+ .mux_sel_bit = 0,
|
|
||||||
+ .freq_tbl = clk_tbl_nss_tcm,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3dc0,
|
|
||||||
+ .enable_mask = BIT(1),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "nss_tcm_src",
|
|
||||||
+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
|
|
||||||
+ .num_parents = 5,
|
|
||||||
+ .ops = &clk_dyn_rcg_ops,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_branch nss_tcm_clk = {
|
|
||||||
+ .halt_reg = 0x3c20,
|
|
||||||
+ .halt_bit = 14,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3dd0,
|
|
||||||
+ .enable_mask = BIT(6) | BIT(4),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "nss_tcm_clk",
|
|
||||||
+ .parent_names = (const char *[]){
|
|
||||||
+ "nss_tcm_src",
|
|
||||||
+ },
|
|
||||||
+ .num_parents = 1,
|
|
||||||
+ .ops = &clk_branch_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct freq_tbl clk_tbl_nss[] = {
|
|
||||||
+ { 110000000, P_PLL18, 1, 1, 5 },
|
|
||||||
+ { 275000000, P_PLL18, 2, 0, 0 },
|
|
||||||
+ { 550000000, P_PLL18, 1, 0, 0 },
|
|
||||||
+ { 733000000, P_PLL18, 1, 0, 0 },
|
|
||||||
+ { }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_dyn_rcg ubi32_core1_src_clk = {
|
|
||||||
+ .ns_reg[0] = 0x3d2c,
|
|
||||||
+ .ns_reg[1] = 0x3d30,
|
|
||||||
+ .md_reg[0] = 0x3d24,
|
|
||||||
+ .md_reg[1] = 0x3d28,
|
|
||||||
+ .bank_reg = 0x3d20,
|
|
||||||
+ .mn[0] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .mn[1] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .s[0] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .s[1] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .p[0] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .p[1] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .mux_sel_bit = 0,
|
|
||||||
+ .freq_tbl = clk_tbl_nss,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3d20,
|
|
||||||
+ .enable_mask = BIT(1),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "ubi32_core1_src_clk",
|
|
||||||
+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
|
|
||||||
+ .num_parents = 5,
|
|
||||||
+ .ops = &clk_dyn_rcg_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct clk_dyn_rcg ubi32_core2_src_clk = {
|
|
||||||
+ .ns_reg[0] = 0x3d4c,
|
|
||||||
+ .ns_reg[1] = 0x3d50,
|
|
||||||
+ .md_reg[0] = 0x3d44,
|
|
||||||
+ .md_reg[1] = 0x3d48,
|
|
||||||
+ .bank_reg = 0x3d40,
|
|
||||||
+ .mn[0] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .mn[1] = {
|
|
||||||
+ .mnctr_en_bit = 8,
|
|
||||||
+ .mnctr_reset_bit = 7,
|
|
||||||
+ .mnctr_mode_shift = 5,
|
|
||||||
+ .n_val_shift = 16,
|
|
||||||
+ .m_val_shift = 16,
|
|
||||||
+ .width = 8,
|
|
||||||
+ },
|
|
||||||
+ .s[0] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .s[1] = {
|
|
||||||
+ .src_sel_shift = 0,
|
|
||||||
+ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map,
|
|
||||||
+ },
|
|
||||||
+ .p[0] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .p[1] = {
|
|
||||||
+ .pre_div_shift = 3,
|
|
||||||
+ .pre_div_width = 2,
|
|
||||||
+ },
|
|
||||||
+ .mux_sel_bit = 0,
|
|
||||||
+ .freq_tbl = clk_tbl_nss,
|
|
||||||
+ .clkr = {
|
|
||||||
+ .enable_reg = 0x3d40,
|
|
||||||
+ .enable_mask = BIT(1),
|
|
||||||
+ .hw.init = &(struct clk_init_data){
|
|
||||||
+ .name = "ubi32_core2_src_clk",
|
|
||||||
+ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0,
|
|
||||||
+ .num_parents = 5,
|
|
||||||
+ .ops = &clk_dyn_rcg_ops,
|
|
||||||
+ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static struct clk_regmap *gcc_ipq806x_clks[] = {
|
|
||||||
[PLL0] = &pll0.clkr,
|
|
||||||
[PLL0_VOTE] = &pll0_vote,
|
|
||||||
@@ -2247,6 +2764,7 @@ static struct clk_regmap *gcc_ipq806x_cl
|
|
||||||
[PLL8_VOTE] = &pll8_vote,
|
|
||||||
[PLL14] = &pll14.clkr,
|
|
||||||
[PLL14_VOTE] = &pll14_vote,
|
|
||||||
+ [PLL18] = &pll18.clkr,
|
|
||||||
[GSBI1_UART_SRC] = &gsbi1_uart_src.clkr,
|
|
||||||
[GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr,
|
|
||||||
[GSBI2_UART_SRC] = &gsbi2_uart_src.clkr,
|
|
||||||
@@ -2344,6 +2862,18 @@ static struct clk_regmap *gcc_ipq806x_cl
|
|
||||||
[PLL9] = &hfpll0.clkr,
|
|
||||||
[PLL10] = &hfpll1.clkr,
|
|
||||||
[PLL12] = &hfpll_l2.clkr,
|
|
||||||
+ [GMAC_CORE1_CLK_SRC] = &gmac_core1_src.clkr,
|
|
||||||
+ [GMAC_CORE1_CLK] = &gmac_core1_clk.clkr,
|
|
||||||
+ [GMAC_CORE2_CLK_SRC] = &gmac_core2_src.clkr,
|
|
||||||
+ [GMAC_CORE2_CLK] = &gmac_core2_clk.clkr,
|
|
||||||
+ [GMAC_CORE3_CLK_SRC] = &gmac_core3_src.clkr,
|
|
||||||
+ [GMAC_CORE3_CLK] = &gmac_core3_clk.clkr,
|
|
||||||
+ [GMAC_CORE4_CLK_SRC] = &gmac_core4_src.clkr,
|
|
||||||
+ [GMAC_CORE4_CLK] = &gmac_core4_clk.clkr,
|
|
||||||
+ [UBI32_CORE1_CLK_SRC] = &ubi32_core1_src_clk.clkr,
|
|
||||||
+ [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
|
|
||||||
+ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
|
|
||||||
+ [NSSTCM_CLK] = &nss_tcm_clk.clkr,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct qcom_reset_map gcc_ipq806x_resets[] = {
|
|
||||||
@@ -2462,6 +2992,48 @@ static const struct qcom_reset_map gcc_i
|
|
||||||
[USB30_1_PHY_RESET] = { 0x3b58, 0 },
|
|
||||||
[NSSFB0_RESET] = { 0x3b60, 6 },
|
|
||||||
[NSSFB1_RESET] = { 0x3b60, 7 },
|
|
||||||
+ [UBI32_CORE1_CLKRST_CLAMP_RESET] = { 0x3d3c, 3},
|
|
||||||
+ [UBI32_CORE1_CLAMP_RESET] = { 0x3d3c, 2 },
|
|
||||||
+ [UBI32_CORE1_AHB_RESET] = { 0x3d3c, 1 },
|
|
||||||
+ [UBI32_CORE1_AXI_RESET] = { 0x3d3c, 0 },
|
|
||||||
+ [UBI32_CORE2_CLKRST_CLAMP_RESET] = { 0x3d5c, 3 },
|
|
||||||
+ [UBI32_CORE2_CLAMP_RESET] = { 0x3d5c, 2 },
|
|
||||||
+ [UBI32_CORE2_AHB_RESET] = { 0x3d5c, 1 },
|
|
||||||
+ [UBI32_CORE2_AXI_RESET] = { 0x3d5c, 0 },
|
|
||||||
+ [GMAC_CORE1_RESET] = { 0x3cbc, 0 },
|
|
||||||
+ [GMAC_CORE2_RESET] = { 0x3cdc, 0 },
|
|
||||||
+ [GMAC_CORE3_RESET] = { 0x3cfc, 0 },
|
|
||||||
+ [GMAC_CORE4_RESET] = { 0x3d1c, 0 },
|
|
||||||
+ [GMAC_AHB_RESET] = { 0x3e24, 0 },
|
|
||||||
+ [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 },
|
|
||||||
+ [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 },
|
|
||||||
+ [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 },
|
|
||||||
+ [NSS_CH0_HW_RST_RX_125M_N_RESET] = { 0x3b60, 3 },
|
|
||||||
+ [NSS_CH0_RST_TX_125M_N_RESET] = { 0x3b60, 4 },
|
|
||||||
+ [NSS_CH1_RST_RX_CLK_N_RESET] = { 0x3b60, 5 },
|
|
||||||
+ [NSS_CH1_RST_TX_CLK_N_RESET] = { 0x3b60, 6 },
|
|
||||||
+ [NSS_CH1_RST_RX_125M_N_RESET] = { 0x3b60, 7 },
|
|
||||||
+ [NSS_CH1_HW_RST_RX_125M_N_RESET] = { 0x3b60, 8 },
|
|
||||||
+ [NSS_CH1_RST_TX_125M_N_RESET] = { 0x3b60, 9 },
|
|
||||||
+ [NSS_CH2_RST_RX_CLK_N_RESET] = { 0x3b60, 10 },
|
|
||||||
+ [NSS_CH2_RST_TX_CLK_N_RESET] = { 0x3b60, 11 },
|
|
||||||
+ [NSS_CH2_RST_RX_125M_N_RESET] = { 0x3b60, 12 },
|
|
||||||
+ [NSS_CH2_HW_RST_RX_125M_N_RESET] = { 0x3b60, 13 },
|
|
||||||
+ [NSS_CH2_RST_TX_125M_N_RESET] = { 0x3b60, 14 },
|
|
||||||
+ [NSS_CH3_RST_RX_CLK_N_RESET] = { 0x3b60, 15 },
|
|
||||||
+ [NSS_CH3_RST_TX_CLK_N_RESET] = { 0x3b60, 16 },
|
|
||||||
+ [NSS_CH3_RST_RX_125M_N_RESET] = { 0x3b60, 17 },
|
|
||||||
+ [NSS_CH3_HW_RST_RX_125M_N_RESET] = { 0x3b60, 18 },
|
|
||||||
+ [NSS_CH3_RST_TX_125M_N_RESET] = { 0x3b60, 19 },
|
|
||||||
+ [NSS_RST_RX_250M_125M_N_RESET] = { 0x3b60, 20 },
|
|
||||||
+ [NSS_RST_TX_250M_125M_N_RESET] = { 0x3b60, 21 },
|
|
||||||
+ [NSS_QSGMII_TXPI_RST_N_RESET] = { 0x3b60, 22 },
|
|
||||||
+ [NSS_QSGMII_CDR_RST_N_RESET] = { 0x3b60, 23 },
|
|
||||||
+ [NSS_SGMII2_CDR_RST_N_RESET] = { 0x3b60, 24 },
|
|
||||||
+ [NSS_SGMII3_CDR_RST_N_RESET] = { 0x3b60, 25 },
|
|
||||||
+ [NSS_CAL_PRBS_RST_N_RESET] = { 0x3b60, 26 },
|
|
||||||
+ [NSS_LCKDT_RST_N_RESET] = { 0x3b60, 27 },
|
|
||||||
+ [NSS_SRDS_N_RESET] = { 0x3b60, 28 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct regmap_config gcc_ipq806x_regmap_config = {
|
|
||||||
@@ -2490,6 +3062,8 @@ static int gcc_ipq806x_probe(struct plat
|
|
||||||
{
|
|
||||||
struct clk *clk;
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
+ struct regmap *regmap;
|
|
||||||
+ int ret;
|
|
||||||
|
|
||||||
/* Temporary until RPM clocks supported */
|
|
||||||
clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000);
|
|
||||||
@@ -2500,7 +3074,25 @@ static int gcc_ipq806x_probe(struct plat
|
|
||||||
if (IS_ERR(clk))
|
|
||||||
return PTR_ERR(clk);
|
|
||||||
|
|
||||||
- return qcom_cc_probe(pdev, &gcc_ipq806x_desc);
|
|
||||||
+ ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc);
|
|
||||||
+ if (ret)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ regmap = dev_get_regmap(dev, NULL);
|
|
||||||
+ if (!regmap)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ /* Setup PLL18 static bits */
|
|
||||||
+ regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400);
|
|
||||||
+ regmap_write(regmap, 0x31b0, 0x3080);
|
|
||||||
+
|
|
||||||
+ /* Set GMAC footswitch sleep/wakeup values */
|
|
||||||
+ regmap_write(regmap, 0x3cb8, 8);
|
|
||||||
+ regmap_write(regmap, 0x3cd8, 8);
|
|
||||||
+ regmap_write(regmap, 0x3cf8, 8);
|
|
||||||
+ regmap_write(regmap, 0x3d18, 8);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gcc_ipq806x_remove(struct platform_device *pdev)
|
|
||||||
--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h
|
|
||||||
+++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
|
|
||||||
@@ -289,5 +289,7 @@
|
|
||||||
#define UBI32_CORE2_CLK_SRC 278
|
|
||||||
#define UBI32_CORE1_CLK 279
|
|
||||||
#define UBI32_CORE2_CLK 280
|
|
||||||
+#define NSSTCM_CLK_SRC 281
|
|
||||||
+#define NSSTCM_CLK 282
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h
|
|
||||||
+++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h
|
|
||||||
@@ -129,4 +129,47 @@
|
|
||||||
#define USB30_1_PHY_RESET 112
|
|
||||||
#define NSSFB0_RESET 113
|
|
||||||
#define NSSFB1_RESET 114
|
|
||||||
+#define UBI32_CORE1_CLKRST_CLAMP_RESET 115
|
|
||||||
+#define UBI32_CORE1_CLAMP_RESET 116
|
|
||||||
+#define UBI32_CORE1_AHB_RESET 117
|
|
||||||
+#define UBI32_CORE1_AXI_RESET 118
|
|
||||||
+#define UBI32_CORE2_CLKRST_CLAMP_RESET 119
|
|
||||||
+#define UBI32_CORE2_CLAMP_RESET 120
|
|
||||||
+#define UBI32_CORE2_AHB_RESET 121
|
|
||||||
+#define UBI32_CORE2_AXI_RESET 122
|
|
||||||
+#define GMAC_CORE1_RESET 123
|
|
||||||
+#define GMAC_CORE2_RESET 124
|
|
||||||
+#define GMAC_CORE3_RESET 125
|
|
||||||
+#define GMAC_CORE4_RESET 126
|
|
||||||
+#define GMAC_AHB_RESET 127
|
|
||||||
+#define NSS_CH0_RST_RX_CLK_N_RESET 128
|
|
||||||
+#define NSS_CH0_RST_TX_CLK_N_RESET 129
|
|
||||||
+#define NSS_CH0_RST_RX_125M_N_RESET 130
|
|
||||||
+#define NSS_CH0_HW_RST_RX_125M_N_RESET 131
|
|
||||||
+#define NSS_CH0_RST_TX_125M_N_RESET 132
|
|
||||||
+#define NSS_CH1_RST_RX_CLK_N_RESET 133
|
|
||||||
+#define NSS_CH1_RST_TX_CLK_N_RESET 134
|
|
||||||
+#define NSS_CH1_RST_RX_125M_N_RESET 135
|
|
||||||
+#define NSS_CH1_HW_RST_RX_125M_N_RESET 136
|
|
||||||
+#define NSS_CH1_RST_TX_125M_N_RESET 137
|
|
||||||
+#define NSS_CH2_RST_RX_CLK_N_RESET 138
|
|
||||||
+#define NSS_CH2_RST_TX_CLK_N_RESET 139
|
|
||||||
+#define NSS_CH2_RST_RX_125M_N_RESET 140
|
|
||||||
+#define NSS_CH2_HW_RST_RX_125M_N_RESET 141
|
|
||||||
+#define NSS_CH2_RST_TX_125M_N_RESET 142
|
|
||||||
+#define NSS_CH3_RST_RX_CLK_N_RESET 143
|
|
||||||
+#define NSS_CH3_RST_TX_CLK_N_RESET 144
|
|
||||||
+#define NSS_CH3_RST_RX_125M_N_RESET 145
|
|
||||||
+#define NSS_CH3_HW_RST_RX_125M_N_RESET 146
|
|
||||||
+#define NSS_CH3_RST_TX_125M_N_RESET 147
|
|
||||||
+#define NSS_RST_RX_250M_125M_N_RESET 148
|
|
||||||
+#define NSS_RST_TX_250M_125M_N_RESET 149
|
|
||||||
+#define NSS_QSGMII_TXPI_RST_N_RESET 150
|
|
||||||
+#define NSS_QSGMII_CDR_RST_N_RESET 151
|
|
||||||
+#define NSS_SGMII2_CDR_RST_N_RESET 152
|
|
||||||
+#define NSS_SGMII3_CDR_RST_N_RESET 153
|
|
||||||
+#define NSS_CAL_PRBS_RST_N_RESET 154
|
|
||||||
+#define NSS_LCKDT_RST_N_RESET 155
|
|
||||||
+#define NSS_SRDS_N_RESET 156
|
|
||||||
+
|
|
||||||
#endif
|
|
|
@ -1,105 +0,0 @@
|
||||||
From 4f09499bc1d9bb095caccbcd73ff951ee631e521 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Fri, 8 May 2015 15:42:40 -0700
|
|
||||||
Subject: [PATCH 1/8] stmmac: add phy-handle support to the platform layer
|
|
||||||
|
|
||||||
On stmmac driver, PHY specification in device-tree was done using the
|
|
||||||
non-standard property "snps,phy-addr". Specifying a PHY on a different
|
|
||||||
MDIO bus that the one within the stmmac controller doesn't seem to be
|
|
||||||
possible when device-tree is used.
|
|
||||||
|
|
||||||
This change adds support for the phy-handle property, as specified in
|
|
||||||
Documentation/devicetree/bindings/net/ethernet.txt.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 28 ++++++++++++++--------
|
|
||||||
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 ++++-
|
|
||||||
include/linux/stmmac.h | 1 +
|
|
||||||
3 files changed, 24 insertions(+), 11 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
@@ -52,6 +52,7 @@
|
|
||||||
#include "stmmac_ptp.h"
|
|
||||||
#include "stmmac.h"
|
|
||||||
#include <linux/reset.h>
|
|
||||||
+#include <linux/of_mdio.h>
|
|
||||||
|
|
||||||
#define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x)
|
|
||||||
|
|
||||||
@@ -818,18 +819,25 @@ static int stmmac_init_phy(struct net_de
|
|
||||||
priv->speed = 0;
|
|
||||||
priv->oldduplex = -1;
|
|
||||||
|
|
||||||
- if (priv->plat->phy_bus_name)
|
|
||||||
- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
|
|
||||||
- priv->plat->phy_bus_name, priv->plat->bus_id);
|
|
||||||
- else
|
|
||||||
- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
|
|
||||||
- priv->plat->bus_id);
|
|
||||||
-
|
|
||||||
- snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
|
|
||||||
- priv->plat->phy_addr);
|
|
||||||
- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt);
|
|
||||||
+ if (priv->plat->phy_node) {
|
|
||||||
+ phydev = of_phy_connect(dev, priv->plat->phy_node,
|
|
||||||
+ &stmmac_adjust_link, 0, interface);
|
|
||||||
+ } else {
|
|
||||||
+ if (priv->plat->phy_bus_name)
|
|
||||||
+ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
|
|
||||||
+ priv->plat->phy_bus_name, priv->plat->bus_id);
|
|
||||||
+ else
|
|
||||||
+ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
|
|
||||||
+ priv->plat->bus_id);
|
|
||||||
+
|
|
||||||
+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
|
|
||||||
+ priv->plat->phy_addr);
|
|
||||||
+ pr_debug("stmmac_init_phy: trying to attach to %s\n",
|
|
||||||
+ phy_id_fmt);
|
|
||||||
|
|
||||||
- phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface);
|
|
||||||
+ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link,
|
|
||||||
+ interface);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
if (IS_ERR(phydev)) {
|
|
||||||
pr_err("%s: Could not attach to PHY\n", dev->name);
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
@@ -27,6 +27,7 @@
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/of_net.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
+#include <linux/of_mdio.h>
|
|
||||||
#include "stmmac.h"
|
|
||||||
|
|
||||||
static const struct of_device_id stmmac_dt_ids[] = {
|
|
||||||
@@ -155,13 +156,16 @@ static int stmmac_probe_config_dt(struct
|
|
||||||
/* Default to phy auto-detection */
|
|
||||||
plat->phy_addr = -1;
|
|
||||||
|
|
||||||
+ /* If we find a phy-handle property, use it as the PHY */
|
|
||||||
+ plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
|
|
||||||
+
|
|
||||||
/* "snps,phy-addr" is not a standard property. Mark it as deprecated
|
|
||||||
* and warn of its use. Remove this when phy node support is added.
|
|
||||||
*/
|
|
||||||
if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
|
|
||||||
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
|
|
||||||
|
|
||||||
- if (plat->phy_bus_name)
|
|
||||||
+ if (plat->phy_node || plat->phy_bus_name)
|
|
||||||
plat->mdio_bus_data = NULL;
|
|
||||||
else
|
|
||||||
plat->mdio_bus_data =
|
|
||||||
--- a/include/linux/stmmac.h
|
|
||||||
+++ b/include/linux/stmmac.h
|
|
||||||
@@ -99,6 +99,7 @@ struct plat_stmmacenet_data {
|
|
||||||
int phy_addr;
|
|
||||||
int interface;
|
|
||||||
struct stmmac_mdio_bus_data *mdio_bus_data;
|
|
||||||
+ struct device_node *phy_node;
|
|
||||||
struct stmmac_dma_cfg *dma_cfg;
|
|
||||||
int clk_csr;
|
|
||||||
int has_gmac;
|
|
|
@ -1,65 +0,0 @@
|
||||||
From 0149d275415cd1b2382ce94e5eb32641590097d0 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Fri, 8 May 2015 15:57:12 -0700
|
|
||||||
Subject: [PATCH 2/8] stmmac: move error path at the end of
|
|
||||||
stmmac_probe_config_dt()
|
|
||||||
|
|
||||||
We will want to do additional clean-up on certain errors. Therefore,
|
|
||||||
this change moves the error path at the end of the function for better
|
|
||||||
code readability.
|
|
||||||
|
|
||||||
This patch doesn't change anything functionally.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 22 ++++++++++++++++------
|
|
||||||
1 file changed, 16 insertions(+), 6 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
@@ -117,13 +117,18 @@ static int stmmac_probe_config_dt(struct
|
|
||||||
struct device_node *np = pdev->dev.of_node;
|
|
||||||
struct stmmac_dma_cfg *dma_cfg;
|
|
||||||
const struct of_device_id *device;
|
|
||||||
+ int ret;
|
|
||||||
|
|
||||||
- if (!np)
|
|
||||||
- return -ENODEV;
|
|
||||||
+ if (!np) {
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
device = of_match_device(stmmac_dt_ids, &pdev->dev);
|
|
||||||
- if (!device)
|
|
||||||
- return -ENODEV;
|
|
||||||
+ if (!device) {
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
if (device->data) {
|
|
||||||
const struct stmmac_of_data *data = device->data;
|
|
||||||
@@ -219,8 +224,10 @@ static int stmmac_probe_config_dt(struct
|
|
||||||
if (of_find_property(np, "snps,pbl", NULL)) {
|
|
||||||
dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
|
|
||||||
GFP_KERNEL);
|
|
||||||
- if (!dma_cfg)
|
|
||||||
- return -ENOMEM;
|
|
||||||
+ if (!dma_cfg) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
plat->dma_cfg = dma_cfg;
|
|
||||||
of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
|
|
||||||
dma_cfg->fixed_burst =
|
|
||||||
@@ -235,6 +242,9 @@ static int stmmac_probe_config_dt(struct
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
+
|
|
||||||
+err:
|
|
||||||
+ return ret;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static int stmmac_probe_config_dt(struct platform_device *pdev,
|
|
|
@ -1,64 +0,0 @@
|
||||||
From 3a95f75867be562cb919ff23a738f70357188fbd Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Fri, 8 May 2015 16:02:03 -0700
|
|
||||||
Subject: [PATCH 3/8] stmmac: add fixed-link device-tree support
|
|
||||||
|
|
||||||
In case DT is used, this change adds the ability to the stmmac driver to
|
|
||||||
detect a fixed-link PHY, instanciate it, and use it during
|
|
||||||
phy_connect().
|
|
||||||
|
|
||||||
Fixed link PHYs DT usage is described in:
|
|
||||||
Documentation/devicetree/bindings/net/fixed-link.txt
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +-
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 12 +++++++++++-
|
|
||||||
2 files changed, 12 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
@@ -858,7 +858,7 @@ static int stmmac_init_phy(struct net_de
|
|
||||||
* device as well.
|
|
||||||
* Note: phydev->phy_id is the result of reading the UID PHY registers.
|
|
||||||
*/
|
|
||||||
- if (phydev->phy_id == 0) {
|
|
||||||
+ if (!priv->plat->phy_node && phydev->phy_id == 0) {
|
|
||||||
phy_disconnect(phydev);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
@@ -164,6 +164,14 @@ static int stmmac_probe_config_dt(struct
|
|
||||||
/* If we find a phy-handle property, use it as the PHY */
|
|
||||||
plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
|
|
||||||
|
|
||||||
+ /* If phy-handle is not specified, check if we have a fixed-phy */
|
|
||||||
+ if (!plat->phy_node && of_phy_is_fixed_link(np)) {
|
|
||||||
+ if ((of_phy_register_fixed_link(np) < 0))
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ plat->phy_node = of_node_get(np);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
/* "snps,phy-addr" is not a standard property. Mark it as deprecated
|
|
||||||
* and warn of its use. Remove this when phy node support is added.
|
|
||||||
*/
|
|
||||||
@@ -226,7 +234,7 @@ static int stmmac_probe_config_dt(struct
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!dma_cfg) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
- goto err;
|
|
||||||
+ goto err2;
|
|
||||||
}
|
|
||||||
plat->dma_cfg = dma_cfg;
|
|
||||||
of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
|
|
||||||
@@ -243,6 +251,8 @@ static int stmmac_probe_config_dt(struct
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
+err2:
|
|
||||||
+ of_node_put(np);
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
|
@ -1,427 +0,0 @@
|
||||||
From 69fb970ad3fe05af7cb99ea78230c69c7ca0d03b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Fri, 8 May 2015 16:10:22 -0700
|
|
||||||
Subject: [PATCH 4/8] stmmac: add ipq806x glue layer
|
|
||||||
|
|
||||||
The ethernet controller available in IPQ806x is a Synopsys DesignWare
|
|
||||||
Gigabit MAC IP core, already supported by the stmmac driver.
|
|
||||||
|
|
||||||
This glue layer implements some platform specific settings required to
|
|
||||||
get the controller working on an IPQ806x based platform.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/Kconfig | 1 +
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +-
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/dwmac-ipq.c | 324 +++++++++++++++++++++
|
|
||||||
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 1 +
|
|
||||||
.../net/ethernet/stmicro/stmmac/stmmac_platform.h | 1 +
|
|
||||||
5 files changed, 328 insertions(+), 1 deletion(-)
|
|
||||||
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-ipq.c
|
|
||||||
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
|
||||||
@@ -16,6 +16,7 @@ if STMMAC_ETH
|
|
||||||
config STMMAC_PLATFORM
|
|
||||||
bool "STMMAC Platform bus support"
|
|
||||||
depends on STMMAC_ETH
|
|
||||||
+ select MFD_SYSCON
|
|
||||||
default y
|
|
||||||
---help---
|
|
||||||
This selects the platform specific bus support for
|
|
||||||
@@ -26,6 +27,15 @@ config STMMAC_PLATFORM
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
+config DWMAC_IPQ806X
|
|
||||||
+ bool "QCA IPQ806x dwmac support"
|
|
||||||
+ depends on STMMAC_PLATFORM && ARCH_QCOM
|
|
||||||
+ help
|
|
||||||
+ Support for Ethernet controller on QCA IPQ806x SoC.
|
|
||||||
+
|
|
||||||
+ This selects the QCA IPQ806x SoC glue layer support for
|
|
||||||
+ the stmmac device driver.
|
|
||||||
+
|
|
||||||
config DWMAC_MESON
|
|
||||||
bool "Amlogic Meson dwmac support"
|
|
||||||
depends on STMMAC_PLATFORM && ARCH_MESON
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
|
|
||||||
@@ -1,6 +1,7 @@
|
|
||||||
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
|
|
||||||
stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
|
|
||||||
stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
|
|
||||||
+stmmac-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
|
|
||||||
stmmac-$(CONFIG_DWMAC_MESON) += dwmac-meson.o
|
|
||||||
stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
|
|
||||||
stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
||||||
@@ -46,6 +46,9 @@ static const struct of_device_id stmmac_
|
|
||||||
#ifdef CONFIG_DWMAC_SOCFPGA
|
|
||||||
{ .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data },
|
|
||||||
#endif
|
|
||||||
+#ifdef CONFIG_DWMAC_IPQ806X
|
|
||||||
+ { .compatible = "qcom,ipq806x-gmac", .data = &ipq806x_gmac_data },
|
|
||||||
+#endif
|
|
||||||
/* SoC specific glue layers should come before generic bindings */
|
|
||||||
{ .compatible = "st,spear600-gmac"},
|
|
||||||
{ .compatible = "snps,dwmac-3.610"},
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
|
|
||||||
@@ -0,0 +1,343 @@
|
|
||||||
+/*
|
|
||||||
+ * Qualcomm Atheros IPQ806x GMAC glue layer
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2015 The Linux Foundation
|
|
||||||
+ *
|
|
||||||
+ * Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
+ * purpose with or without fee is hereby granted, provided that the above
|
|
||||||
+ * copyright notice and this permission notice appear in all copies.
|
|
||||||
+ *
|
|
||||||
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/device.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/phy.h>
|
|
||||||
+#include <linux/regmap.h>
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/reset.h>
|
|
||||||
+#include <linux/of_net.h>
|
|
||||||
+#include <linux/mfd/syscon.h>
|
|
||||||
+#include <linux/stmmac.h>
|
|
||||||
+#include <linux/of_mdio.h>
|
|
||||||
+
|
|
||||||
+#include "stmmac.h"
|
|
||||||
+
|
|
||||||
+#define NSS_COMMON_CLK_GATE 0x8
|
|
||||||
+#define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x)
|
|
||||||
+#define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2))
|
|
||||||
+#define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2))
|
|
||||||
+#define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x)
|
|
||||||
+#define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x)
|
|
||||||
+
|
|
||||||
+#define NSS_COMMON_CLK_DIV0 0xC
|
|
||||||
+#define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8)
|
|
||||||
+#define NSS_COMMON_CLK_DIV_MASK 0x7f
|
|
||||||
+
|
|
||||||
+#define NSS_COMMON_CLK_SRC_CTRL 0x14
|
|
||||||
+#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (1 << x)
|
|
||||||
+/* Mode is coded on 1 bit but is different depending on the MAC ID:
|
|
||||||
+ * MAC0: QSGMII=0 RGMII=1
|
|
||||||
+ * MAC1: QSGMII=0 SGMII=0 RGMII=1
|
|
||||||
+ * MAC2 & MAC3: QSGMII=0 SGMII=1
|
|
||||||
+ */
|
|
||||||
+#define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1
|
|
||||||
+#define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0)
|
|
||||||
+
|
|
||||||
+#define NSS_COMMON_MACSEC_CTL 0x28
|
|
||||||
+#define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x)
|
|
||||||
+
|
|
||||||
+#define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4))
|
|
||||||
+#define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19)
|
|
||||||
+#define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16)
|
|
||||||
+#define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8
|
|
||||||
+#define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0
|
|
||||||
+#define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f
|
|
||||||
+
|
|
||||||
+#define NSS_COMMON_CLK_DIV_RGMII_1000 1
|
|
||||||
+#define NSS_COMMON_CLK_DIV_RGMII_100 9
|
|
||||||
+#define NSS_COMMON_CLK_DIV_RGMII_10 99
|
|
||||||
+#define NSS_COMMON_CLK_DIV_SGMII_1000 0
|
|
||||||
+#define NSS_COMMON_CLK_DIV_SGMII_100 4
|
|
||||||
+#define NSS_COMMON_CLK_DIV_SGMII_10 49
|
|
||||||
+
|
|
||||||
+#define QSGMII_PCS_MODE_CTL 0x68
|
|
||||||
+#define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7)
|
|
||||||
+
|
|
||||||
+#define QSGMII_PCS_CAL_LCKDT_CTL 0x120
|
|
||||||
+#define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19)
|
|
||||||
+
|
|
||||||
+/* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */
|
|
||||||
+#define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \
|
|
||||||
+ (0x13c + (4 * (x - 2))))
|
|
||||||
+#define QSGMII_PHY_CDR_EN BIT(0)
|
|
||||||
+#define QSGMII_PHY_RX_FRONT_EN BIT(1)
|
|
||||||
+#define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2)
|
|
||||||
+#define QSGMII_PHY_TX_DRIVER_EN BIT(3)
|
|
||||||
+#define QSGMII_PHY_QSGMII_EN BIT(7)
|
|
||||||
+#define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12
|
|
||||||
+#define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7
|
|
||||||
+#define QSGMII_PHY_RX_DC_BIAS_OFFSET 18
|
|
||||||
+#define QSGMII_PHY_RX_DC_BIAS_MASK 0x3
|
|
||||||
+#define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20
|
|
||||||
+#define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3
|
|
||||||
+#define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22
|
|
||||||
+#define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3
|
|
||||||
+#define QSGMII_PHY_TX_DRV_AMP_OFFSET 28
|
|
||||||
+#define QSGMII_PHY_TX_DRV_AMP_MASK 0xf
|
|
||||||
+
|
|
||||||
+struct ipq806x_gmac {
|
|
||||||
+ struct platform_device *pdev;
|
|
||||||
+ struct regmap *nss_common;
|
|
||||||
+ struct regmap *qsgmii_csr;
|
|
||||||
+ uint32_t id;
|
|
||||||
+ struct clk *core_clk;
|
|
||||||
+ phy_interface_t phy_mode;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &gmac->pdev->dev;
|
|
||||||
+ int div;
|
|
||||||
+
|
|
||||||
+ switch (speed) {
|
|
||||||
+ case SPEED_1000:
|
|
||||||
+ div = NSS_COMMON_CLK_DIV_SGMII_1000;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case SPEED_100:
|
|
||||||
+ div = NSS_COMMON_CLK_DIV_SGMII_100;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case SPEED_10:
|
|
||||||
+ div = NSS_COMMON_CLK_DIV_SGMII_10;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ default:
|
|
||||||
+ dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return div;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &gmac->pdev->dev;
|
|
||||||
+ int div;
|
|
||||||
+
|
|
||||||
+ switch (speed) {
|
|
||||||
+ case SPEED_1000:
|
|
||||||
+ div = NSS_COMMON_CLK_DIV_RGMII_1000;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case SPEED_100:
|
|
||||||
+ div = NSS_COMMON_CLK_DIV_RGMII_100;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case SPEED_10:
|
|
||||||
+ div = NSS_COMMON_CLK_DIV_RGMII_10;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ default:
|
|
||||||
+ dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return div;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed)
|
|
||||||
+{
|
|
||||||
+ uint32_t clk_bits, val;
|
|
||||||
+ int div;
|
|
||||||
+
|
|
||||||
+ switch (gmac->phy_mode) {
|
|
||||||
+ case PHY_INTERFACE_MODE_RGMII:
|
|
||||||
+ div = get_clk_div_rgmii(gmac, speed);
|
|
||||||
+ clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
|
|
||||||
+ NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id);
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case PHY_INTERFACE_MODE_SGMII:
|
|
||||||
+ div = get_clk_div_sgmii(gmac, speed);
|
|
||||||
+ clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) |
|
|
||||||
+ NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id);
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ default:
|
|
||||||
+ dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n",
|
|
||||||
+ phy_modes(gmac->phy_mode));
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Disable the clocks */
|
|
||||||
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
|
|
||||||
+ val &= ~clk_bits;
|
|
||||||
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
|
|
||||||
+
|
|
||||||
+ /* Set the divider */
|
|
||||||
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val);
|
|
||||||
+ val &= ~(NSS_COMMON_CLK_DIV_MASK
|
|
||||||
+ << NSS_COMMON_CLK_DIV_OFFSET(gmac->id));
|
|
||||||
+ val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id);
|
|
||||||
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val);
|
|
||||||
+
|
|
||||||
+ /* Enable the clock back */
|
|
||||||
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
|
|
||||||
+ val |= clk_bits;
|
|
||||||
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &gmac->pdev->dev;
|
|
||||||
+
|
|
||||||
+ gmac->phy_mode = of_get_phy_mode(dev->of_node);
|
|
||||||
+ if (gmac->phy_mode < 0) {
|
|
||||||
+ dev_err(dev, "missing phy mode property\n");
|
|
||||||
+ return ERR_PTR(-EINVAL);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) {
|
|
||||||
+ dev_err(dev, "missing qcom id property\n");
|
|
||||||
+ return ERR_PTR(-EINVAL);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* The GMACs are called 1 to 4 in the documentation, but to simplify the
|
|
||||||
+ * code and keep it consistent with the Linux convention, we'll number
|
|
||||||
+ * them from 0 to 3 here.
|
|
||||||
+ */
|
|
||||||
+ if (gmac->id < 0 || gmac->id > 3) {
|
|
||||||
+ dev_err(dev, "invalid gmac id\n");
|
|
||||||
+ return ERR_PTR(-EINVAL);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ gmac->core_clk = devm_clk_get(dev, "stmmaceth");
|
|
||||||
+ if (IS_ERR(gmac->core_clk)) {
|
|
||||||
+ dev_err(dev, "missing stmmaceth clk property\n");
|
|
||||||
+ return gmac->core_clk;
|
|
||||||
+ }
|
|
||||||
+ clk_set_rate(gmac->core_clk, 266000000);
|
|
||||||
+
|
|
||||||
+ /* Setup the register map for the nss common registers */
|
|
||||||
+ gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node,
|
|
||||||
+ "qcom,nss-common");
|
|
||||||
+ if (IS_ERR(gmac->nss_common)) {
|
|
||||||
+ dev_err(dev, "missing nss-common node\n");
|
|
||||||
+ return gmac->nss_common;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Setup the register map for the qsgmii csr registers */
|
|
||||||
+ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node,
|
|
||||||
+ "qcom,qsgmii-csr");
|
|
||||||
+ if (IS_ERR(gmac->qsgmii_csr)) {
|
|
||||||
+ dev_err(dev, "missing qsgmii-csr node\n");
|
|
||||||
+ return gmac->qsgmii_csr;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return NULL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void *ipq806x_gmac_setup(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &pdev->dev;
|
|
||||||
+ struct ipq806x_gmac *gmac;
|
|
||||||
+ int val;
|
|
||||||
+ void *err;
|
|
||||||
+
|
|
||||||
+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
|
|
||||||
+ if (!gmac)
|
|
||||||
+ return ERR_PTR(-ENOMEM);
|
|
||||||
+
|
|
||||||
+ gmac->pdev = pdev;
|
|
||||||
+
|
|
||||||
+ err = ipq806x_gmac_of_parse(gmac);
|
|
||||||
+ if (err) {
|
|
||||||
+ dev_err(dev, "device tree parsing error\n");
|
|
||||||
+ return err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL,
|
|
||||||
+ QSGMII_PCS_CAL_LCKDT_CTL_RST);
|
|
||||||
+
|
|
||||||
+ /* Inter frame gap is set to 12 */
|
|
||||||
+ val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET |
|
|
||||||
+ 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET;
|
|
||||||
+ /* We also initiate an AXI low power exit request */
|
|
||||||
+ val |= NSS_COMMON_GMAC_CTL_CSYS_REQ;
|
|
||||||
+ switch (gmac->phy_mode) {
|
|
||||||
+ case PHY_INTERFACE_MODE_RGMII:
|
|
||||||
+ val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
|
|
||||||
+ break;
|
|
||||||
+ case PHY_INTERFACE_MODE_SGMII:
|
|
||||||
+ val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
|
|
||||||
+ phy_modes(gmac->phy_mode));
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+ regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val);
|
|
||||||
+
|
|
||||||
+ /* Configure the clock src according to the mode */
|
|
||||||
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val);
|
|
||||||
+ val &= ~NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
|
|
||||||
+ switch (gmac->phy_mode) {
|
|
||||||
+ case PHY_INTERFACE_MODE_RGMII:
|
|
||||||
+ val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) <<
|
|
||||||
+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
|
|
||||||
+ break;
|
|
||||||
+ case PHY_INTERFACE_MODE_SGMII:
|
|
||||||
+ val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) <<
|
|
||||||
+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
|
|
||||||
+ phy_modes(gmac->phy_mode));
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val);
|
|
||||||
+
|
|
||||||
+ /* Enable PTP clock */
|
|
||||||
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
|
|
||||||
+ val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id);
|
|
||||||
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
|
|
||||||
+
|
|
||||||
+ if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) {
|
|
||||||
+ regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id),
|
|
||||||
+ QSGMII_PHY_CDR_EN |
|
|
||||||
+ QSGMII_PHY_RX_FRONT_EN |
|
|
||||||
+ QSGMII_PHY_RX_SIGNAL_DETECT_EN |
|
|
||||||
+ QSGMII_PHY_TX_DRIVER_EN |
|
|
||||||
+ QSGMII_PHY_QSGMII_EN |
|
|
||||||
+ 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET |
|
|
||||||
+ 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET |
|
|
||||||
+ 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET |
|
|
||||||
+ 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET |
|
|
||||||
+ 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return gmac;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed)
|
|
||||||
+{
|
|
||||||
+ struct ipq806x_gmac *gmac = priv;
|
|
||||||
+
|
|
||||||
+ ipq806x_gmac_set_speed(gmac, speed);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+const struct stmmac_of_data ipq806x_gmac_data = {
|
|
||||||
+ .has_gmac = 1,
|
|
||||||
+ .setup = ipq806x_gmac_setup,
|
|
||||||
+ .fix_mac_speed = ipq806x_gmac_fix_mac_speed,
|
|
||||||
+};
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
||||||
@@ -137,6 +137,9 @@ void stmmac_disable_eee_mode(struct stmm
|
|
||||||
bool stmmac_eee_init(struct stmmac_priv *priv);
|
|
||||||
|
|
||||||
#ifdef CONFIG_STMMAC_PLATFORM
|
|
||||||
+#ifdef CONFIG_DWMAC_IPQ806X
|
|
||||||
+extern const struct stmmac_of_data ipq806x_gmac_data;
|
|
||||||
+#endif
|
|
||||||
#ifdef CONFIG_DWMAC_MESON
|
|
||||||
extern const struct stmmac_of_data meson6_dwmac_data;
|
|
||||||
#endif
|
|
|
@ -1,52 +0,0 @@
|
||||||
From 0f9605d9409b77a89daef91cc68239fc2ff50457 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Fri, 8 May 2015 16:51:25 -0700
|
|
||||||
Subject: [PATCH 5/8] net: stmmac: ipq806x: document device tree bindings
|
|
||||||
|
|
||||||
Add the device tree bindings documentation for the QCA IPQ806x
|
|
||||||
variant of the Synopsys DesignWare MAC.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
.../devicetree/bindings/net/ipq806x-dwmac.txt | 35 ++++++++++++++++++++++
|
|
||||||
1 file changed, 35 insertions(+)
|
|
||||||
create mode 100644 Documentation/devicetree/bindings/net/ipq806x-dwmac.txt
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/devicetree/bindings/net/ipq806x-dwmac.txt
|
|
||||||
@@ -0,0 +1,35 @@
|
|
||||||
+* IPQ806x DWMAC Ethernet controller
|
|
||||||
+
|
|
||||||
+The device inherits all the properties of the dwmac/stmmac devices
|
|
||||||
+described in the file net/stmmac.txt with the following changes.
|
|
||||||
+
|
|
||||||
+Required properties:
|
|
||||||
+
|
|
||||||
+- compatible: should be "qcom,ipq806x-gmac" along with "snps,dwmac"
|
|
||||||
+ and any applicable more detailed version number
|
|
||||||
+ described in net/stmmac.txt
|
|
||||||
+
|
|
||||||
+- qcom,nss-common: should contain a phandle to a syscon device mapping the
|
|
||||||
+ nss-common registers.
|
|
||||||
+
|
|
||||||
+- qcom,qsgmii-csr: should contain a phandle to a syscon device mapping the
|
|
||||||
+ qsgmii-csr registers.
|
|
||||||
+
|
|
||||||
+Example:
|
|
||||||
+
|
|
||||||
+ gmac: ethernet@37000000 {
|
|
||||||
+ device_type = "network";
|
|
||||||
+ compatible = "qcom,ipq806x-gmac";
|
|
||||||
+ reg = <0x37000000 0x200000>;
|
|
||||||
+ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
+ interrupt-names = "macirq";
|
|
||||||
+
|
|
||||||
+ qcom,nss-common = <&nss_common>;
|
|
||||||
+ qcom,qsgmii-csr = <&qsgmii_csr>;
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc GMAC_CORE1_CLK>;
|
|
||||||
+ clock-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc GMAC_CORE1_RESET>;
|
|
||||||
+ reset-names = "stmmaceth";
|
|
||||||
+ };
|
|
|
@ -1,171 +0,0 @@
|
||||||
From df944689d491e6af533173bf2ef448c3dd334f15 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Mon, 11 May 2015 15:15:25 -0700
|
|
||||||
Subject: [PATCH 6/8] net: stmmac: create one debugfs dir per net-device
|
|
||||||
|
|
||||||
stmmac DebugFS entries are currently global to the driver. As a result,
|
|
||||||
having more than one stmmac device in the system creates the following
|
|
||||||
error:
|
|
||||||
* ERROR stmmaceth, debugfs create directory failed
|
|
||||||
* stmmac_hw_setup: failed debugFS registration
|
|
||||||
|
|
||||||
This also results in being able to access the debugfs information for
|
|
||||||
the first registered device only.
|
|
||||||
|
|
||||||
This patch changes the debugfs structure to have one sub-directory per
|
|
||||||
net-device. Files under "/sys/kernel/debug/stmmaceth" will now show-up
|
|
||||||
under /sys/kernel/debug/stmmaceth/ethN/.
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 6 ++
|
|
||||||
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 76 ++++++++++++++++-------
|
|
||||||
2 files changed, 59 insertions(+), 23 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
||||||
@@ -116,6 +116,12 @@ struct stmmac_priv {
|
|
||||||
int use_riwt;
|
|
||||||
int irq_wake;
|
|
||||||
spinlock_t ptp_lock;
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_DEBUG_FS
|
|
||||||
+ struct dentry *dbgfs_dir;
|
|
||||||
+ struct dentry *dbgfs_rings_status;
|
|
||||||
+ struct dentry *dbgfs_dma_cap;
|
|
||||||
+#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
int stmmac_mdio_unregister(struct net_device *ndev);
|
|
||||||
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
||||||
@@ -119,7 +119,7 @@ static irqreturn_t stmmac_interrupt(int
|
|
||||||
|
|
||||||
#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
||||||
static int stmmac_init_fs(struct net_device *dev);
|
|
||||||
-static void stmmac_exit_fs(void);
|
|
||||||
+static void stmmac_exit_fs(struct net_device *dev);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
|
|
||||||
@@ -1879,7 +1879,7 @@ static int stmmac_release(struct net_dev
|
|
||||||
netif_carrier_off(dev);
|
|
||||||
|
|
||||||
#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
||||||
- stmmac_exit_fs();
|
|
||||||
+ stmmac_exit_fs(dev);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
stmmac_release_ptp(priv);
|
|
||||||
@@ -2467,8 +2467,6 @@ static int stmmac_ioctl(struct net_devic
|
|
||||||
|
|
||||||
#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
||||||
static struct dentry *stmmac_fs_dir;
|
|
||||||
-static struct dentry *stmmac_rings_status;
|
|
||||||
-static struct dentry *stmmac_dma_cap;
|
|
||||||
|
|
||||||
static void sysfs_display_ring(void *head, int size, int extend_desc,
|
|
||||||
struct seq_file *seq)
|
|
||||||
@@ -2607,36 +2605,39 @@ static const struct file_operations stmm
|
|
||||||
|
|
||||||
static int stmmac_init_fs(struct net_device *dev)
|
|
||||||
{
|
|
||||||
- /* Create debugfs entries */
|
|
||||||
- stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
|
|
||||||
+ struct stmmac_priv *priv = netdev_priv(dev);
|
|
||||||
+
|
|
||||||
+ /* Create per netdev entries */
|
|
||||||
+ priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir);
|
|
||||||
|
|
||||||
- if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
|
|
||||||
- pr_err("ERROR %s, debugfs create directory failed\n",
|
|
||||||
- STMMAC_RESOURCE_NAME);
|
|
||||||
+ if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) {
|
|
||||||
+ pr_err("ERROR %s/%s, debugfs create directory failed\n",
|
|
||||||
+ STMMAC_RESOURCE_NAME, dev->name);
|
|
||||||
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Entry to report DMA RX/TX rings */
|
|
||||||
- stmmac_rings_status = debugfs_create_file("descriptors_status",
|
|
||||||
- S_IRUGO, stmmac_fs_dir, dev,
|
|
||||||
- &stmmac_rings_status_fops);
|
|
||||||
+ priv->dbgfs_rings_status =
|
|
||||||
+ debugfs_create_file("descriptors_status", S_IRUGO,
|
|
||||||
+ priv->dbgfs_dir, dev,
|
|
||||||
+ &stmmac_rings_status_fops);
|
|
||||||
|
|
||||||
- if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) {
|
|
||||||
+ if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) {
|
|
||||||
pr_info("ERROR creating stmmac ring debugfs file\n");
|
|
||||||
- debugfs_remove(stmmac_fs_dir);
|
|
||||||
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
||||||
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Entry to report the DMA HW features */
|
|
||||||
- stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir,
|
|
||||||
- dev, &stmmac_dma_cap_fops);
|
|
||||||
+ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
|
|
||||||
+ priv->dbgfs_dir,
|
|
||||||
+ dev, &stmmac_dma_cap_fops);
|
|
||||||
|
|
||||||
- if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) {
|
|
||||||
+ if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
|
|
||||||
pr_info("ERROR creating stmmac MMC debugfs file\n");
|
|
||||||
- debugfs_remove(stmmac_rings_status);
|
|
||||||
- debugfs_remove(stmmac_fs_dir);
|
|
||||||
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
||||||
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
@@ -2644,11 +2645,11 @@ static int stmmac_init_fs(struct net_dev
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static void stmmac_exit_fs(void)
|
|
||||||
+static void stmmac_exit_fs(struct net_device *dev)
|
|
||||||
{
|
|
||||||
- debugfs_remove(stmmac_rings_status);
|
|
||||||
- debugfs_remove(stmmac_dma_cap);
|
|
||||||
- debugfs_remove(stmmac_fs_dir);
|
|
||||||
+ struct stmmac_priv *priv = netdev_priv(dev);
|
|
||||||
+
|
|
||||||
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_STMMAC_DEBUG_FS */
|
|
||||||
|
|
||||||
@@ -3032,6 +3033,21 @@ static int __init stmmac_init(void)
|
|
||||||
ret = stmmac_register_pci();
|
|
||||||
if (ret)
|
|
||||||
goto err_pci;
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
||||||
+ /* Create debugfs main directory if it doesn't exist yet */
|
|
||||||
+ if (stmmac_fs_dir == NULL) {
|
|
||||||
+ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
|
|
||||||
+
|
|
||||||
+ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
|
|
||||||
+ pr_err("ERROR %s, debugfs create directory failed\n",
|
|
||||||
+ STMMAC_RESOURCE_NAME);
|
|
||||||
+
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
return 0;
|
|
||||||
err_pci:
|
|
||||||
stmmac_unregister_platform();
|
|
||||||
@@ -3042,6 +3058,9 @@ err:
|
|
||||||
|
|
||||||
static void __exit stmmac_exit(void)
|
|
||||||
{
|
|
||||||
+#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
||||||
+ debugfs_remove_recursive(stmmac_fs_dir);
|
|
||||||
+#endif
|
|
||||||
stmmac_unregister_platform();
|
|
||||||
stmmac_unregister_pci();
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Mon, 11 May 2015 16:32:09 -0700
|
|
||||||
Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++-
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 85 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
@@ -14,8 +14,9 @@
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
- alias {
|
|
||||||
+ aliases {
|
|
||||||
serial0 = &uart4;
|
|
||||||
+ mdio-gpio0 = &mdio0;
|
|
||||||
};
|
|
||||||
|
|
||||||
chosen {
|
|
||||||
@@ -54,6 +55,15 @@
|
|
||||||
bias-none;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ mdio0_pins: mdio0_pins {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio0", "gpio1";
|
|
||||||
+ function = "gpio";
|
|
||||||
+ drive-strength = <8>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
|
|
||||||
gsbi@16300000 {
|
|
||||||
@@ -163,5 +173,33 @@
|
|
||||||
pinctrl-0 = <&pcie2_pins>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ mdio0: mdio {
|
|
||||||
+ compatible = "virtual,mdio-gpio";
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <0>;
|
|
||||||
+ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>;
|
|
||||||
+ pinctrl-0 = <&mdio0_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+
|
|
||||||
+ phy0: ethernet-phy@0 {
|
|
||||||
+ device_type = "ethernet-phy";
|
|
||||||
+ reg = <0>;
|
|
||||||
+ qca,ar8327-initvals = <
|
|
||||||
+ 0x00004 0x7600000 /* PAD0_MODE */
|
|
||||||
+ 0x00008 0x1000000 /* PAD5_MODE */
|
|
||||||
+ 0x0000c 0x80 /* PAD6_MODE */
|
|
||||||
+ 0x000e4 0xaa545 /* MAC_POWER_SEL */
|
|
||||||
+ 0x000e0 0xc74164de /* SGMII_CTRL */
|
|
||||||
+ 0x0007c 0x4e /* PORT0_STATUS */
|
|
||||||
+ 0x00094 0x4e /* PORT6_STATUS */
|
|
||||||
+ >;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy4: ethernet-phy@4 {
|
|
||||||
+ device_type = "ethernet-phy";
|
|
||||||
+ reg = <4>;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
@@ -16,6 +16,7 @@
|
|
||||||
|
|
||||||
alias {
|
|
||||||
serial0 = &uart2;
|
|
||||||
+ mdio-gpio0 = &mdio0;
|
|
||||||
};
|
|
||||||
|
|
||||||
chosen {
|
|
||||||
@@ -62,6 +63,15 @@
|
|
||||||
bias-none;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ mdio0_pins: mdio0_pins {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio0", "gpio1";
|
|
||||||
+ function = "gpio";
|
|
||||||
+ drive-strength = <8>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
|
|
||||||
gsbi2: gsbi@12480000 {
|
|
||||||
@@ -173,5 +183,44 @@
|
|
||||||
pinctrl-0 = <&pcie3_pins>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ mdio0: mdio {
|
|
||||||
+ compatible = "virtual,mdio-gpio";
|
|
||||||
+ #address-cells = <1>;
|
|
||||||
+ #size-cells = <0>;
|
|
||||||
+ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>;
|
|
||||||
+
|
|
||||||
+ pinctrl-0 = <&mdio0_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+
|
|
||||||
+ phy0: ethernet-phy@0 {
|
|
||||||
+ device_type = "ethernet-phy";
|
|
||||||
+ reg = <0>;
|
|
||||||
+ qca,ar8327-initvals = <
|
|
||||||
+ 0x00004 0x7600000 /* PAD0_MODE */
|
|
||||||
+ 0x00008 0x1000000 /* PAD5_MODE */
|
|
||||||
+ 0x0000c 0x80 /* PAD6_MODE */
|
|
||||||
+ 0x000e4 0xaa545 /* MAC_POWER_SEL */
|
|
||||||
+ 0x000e0 0xc74164de /* SGMII_CTRL */
|
|
||||||
+ 0x0007c 0x4e /* PORT0_STATUS */
|
|
||||||
+ 0x00094 0x4e /* PORT6_STATUS */
|
|
||||||
+ >;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy4: ethernet-phy@4 {
|
|
||||||
+ device_type = "ethernet-phy";
|
|
||||||
+ reg = <4>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy6: ethernet-phy@6 {
|
|
||||||
+ device_type = "ethernet-phy";
|
|
||||||
+ reg = <6>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ phy7: ethernet-phy@7 {
|
|
||||||
+ device_type = "ethernet-phy";
|
|
||||||
+ reg = <7>;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,210 +0,0 @@
|
||||||
From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
Date: Mon, 11 May 2015 12:29:18 -0700
|
|
||||||
Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms
|
|
||||||
|
|
||||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
|
||||||
---
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++
|
|
||||||
arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 160 insertions(+)
|
|
||||||
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
|
|
||||||
@@ -64,6 +64,16 @@
|
|
||||||
bias-disable;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ rgmii2_pins: rgmii2_pins {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
|
|
||||||
+ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
|
|
||||||
+ function = "rgmii2";
|
|
||||||
+ drive-strength = <8>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
|
|
||||||
gsbi@16300000 {
|
|
||||||
@@ -201,5 +211,26 @@
|
|
||||||
reg = <4>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ gmac1: ethernet@37200000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ phy-mode = "rgmii";
|
|
||||||
+ phy-handle = <&phy4>;
|
|
||||||
+ qcom,id = <1>;
|
|
||||||
+
|
|
||||||
+ pinctrl-0 = <&rgmii2_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac2: ethernet@37400000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ phy-mode = "sgmii";
|
|
||||||
+ qcom,id = <2>;
|
|
||||||
+
|
|
||||||
+ fixed-link {
|
|
||||||
+ speed = <1000>;
|
|
||||||
+ full-duplex;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
|
|
||||||
@@ -72,6 +72,14 @@
|
|
||||||
bias-disable;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ rgmii0_pins: rgmii0_pins {
|
|
||||||
+ mux {
|
|
||||||
+ pins = "gpio2", "gpio66";
|
|
||||||
+ drive-strength = <8>;
|
|
||||||
+ bias-disable;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
|
|
||||||
gsbi2: gsbi@12480000 {
|
|
||||||
@@ -222,5 +230,40 @@
|
|
||||||
reg = <7>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ gmac0: ethernet@37000000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ phy-mode = "rgmii";
|
|
||||||
+ qcom,id = <0>;
|
|
||||||
+ phy-handle = <&phy4>;
|
|
||||||
+
|
|
||||||
+ pinctrl-0 = <&rgmii0_pins>;
|
|
||||||
+ pinctrl-names = "default";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac1: ethernet@37200000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ phy-mode = "sgmii";
|
|
||||||
+ qcom,id = <1>;
|
|
||||||
+
|
|
||||||
+ fixed-link {
|
|
||||||
+ speed = <1000>;
|
|
||||||
+ full-duplex;
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac2: ethernet@37400000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ phy-mode = "sgmii";
|
|
||||||
+ qcom,id = <2>;
|
|
||||||
+ phy-handle = <&phy6>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac3: ethernet@37600000 {
|
|
||||||
+ status = "ok";
|
|
||||||
+ phy-mode = "sgmii";
|
|
||||||
+ qcom,id = <3>;
|
|
||||||
+ phy-handle = <&phy7>;
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
|
|
||||||
@@ -638,5 +638,91 @@
|
|
||||||
dr_mode = "host";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+
|
|
||||||
+ nss_common: syscon@03000000 {
|
|
||||||
+ compatible = "syscon";
|
|
||||||
+ reg = <0x03000000 0x0000FFFF>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ qsgmii_csr: syscon@1bb00000 {
|
|
||||||
+ compatible = "syscon";
|
|
||||||
+ reg = <0x1bb00000 0x000001FF>;
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac0: ethernet@37000000 {
|
|
||||||
+ device_type = "network";
|
|
||||||
+ compatible = "qcom,ipq806x-gmac";
|
|
||||||
+ reg = <0x37000000 0x200000>;
|
|
||||||
+ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
+ interrupt-names = "macirq";
|
|
||||||
+
|
|
||||||
+ qcom,nss-common = <&nss_common>;
|
|
||||||
+ qcom,qsgmii-csr = <&qsgmii_csr>;
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc GMAC_CORE1_CLK>;
|
|
||||||
+ clock-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc GMAC_CORE1_RESET>;
|
|
||||||
+ reset-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac1: ethernet@37200000 {
|
|
||||||
+ device_type = "network";
|
|
||||||
+ compatible = "qcom,ipq806x-gmac";
|
|
||||||
+ reg = <0x37200000 0x200000>;
|
|
||||||
+ interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
+ interrupt-names = "macirq";
|
|
||||||
+
|
|
||||||
+ qcom,nss-common = <&nss_common>;
|
|
||||||
+ qcom,qsgmii-csr = <&qsgmii_csr>;
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc GMAC_CORE2_CLK>;
|
|
||||||
+ clock-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc GMAC_CORE2_RESET>;
|
|
||||||
+ reset-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac2: ethernet@37400000 {
|
|
||||||
+ device_type = "network";
|
|
||||||
+ compatible = "qcom,ipq806x-gmac";
|
|
||||||
+ reg = <0x37400000 0x200000>;
|
|
||||||
+ interrupts = <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
+ interrupt-names = "macirq";
|
|
||||||
+
|
|
||||||
+ qcom,nss-common = <&nss_common>;
|
|
||||||
+ qcom,qsgmii-csr = <&qsgmii_csr>;
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc GMAC_CORE3_CLK>;
|
|
||||||
+ clock-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc GMAC_CORE3_RESET>;
|
|
||||||
+ reset-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ gmac3: ethernet@37600000 {
|
|
||||||
+ device_type = "network";
|
|
||||||
+ compatible = "qcom,ipq806x-gmac";
|
|
||||||
+ reg = <0x37600000 0x200000>;
|
|
||||||
+ interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
+ interrupt-names = "macirq";
|
|
||||||
+
|
|
||||||
+ qcom,nss-common = <&nss_common>;
|
|
||||||
+ qcom,qsgmii-csr = <&qsgmii_csr>;
|
|
||||||
+
|
|
||||||
+ clocks = <&gcc GMAC_CORE4_CLK>;
|
|
||||||
+ clock-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ resets = <&gcc GMAC_CORE4_RESET>;
|
|
||||||
+ reset-names = "stmmaceth";
|
|
||||||
+
|
|
||||||
+ status = "disabled";
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,19 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
|
||||||
# Copyright (C) 2009 OpenWrt.org
|
|
||||||
#
|
|
||||||
# This is free software, licensed under the GNU General Public License v2.
|
|
||||||
# See /LICENSE for more information.
|
|
||||||
#
|
|
||||||
|
|
||||||
define Profile/Default
|
|
||||||
NAME:=Default Profile (minimum package set)
|
|
||||||
PACKAGES:= \
|
|
||||||
kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-ledtrig-usbdev \
|
|
||||||
kmod-usb3 kmod-usb-dwc3-qcom kmod-usb-phy-qcom-dwc3
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Profile/Default/Description
|
|
||||||
Default package set compatible with most boards.
|
|
||||||
endef
|
|
||||||
$(eval $(call Profile,Default))
|
|
Loading…
Reference in New Issue